1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * e-debug-log.c: Ring buffer for logging debug messages
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Author: Federico Mena-Quintero <federico@novell.com>
30 #include "e-debug-log.h"
32 #define DEFAULT_RING_BUFFER_NUM_LINES 1000
34 #define KEY_FILE_GROUP "debug log"
35 #define KEY_FILE_DOMAINS_KEY "enable domains"
36 #define KEY_FILE_MAX_LINES_KEY "max lines"
38 static GMutex log_mutex;
40 static GHashTable *domains_hash;
41 static gchar **ring_buffer;
42 static gint ring_buffer_next_index;
43 static gint ring_buffer_num_lines;
44 static gint ring_buffer_max_lines = DEFAULT_RING_BUFFER_NUM_LINES;
46 static GSList *milestones_head;
47 static GSList *milestones_tail;
52 g_mutex_lock (&log_mutex);
58 g_mutex_unlock (&log_mutex);
67 e_debug_log (gboolean is_milestone,
74 va_start (args, format);
75 e_debug_logv (is_milestone, domain, format, args);
80 is_domain_enabled (const gchar *domain)
82 /* User actions are always logged */
83 if (strcmp (domain, E_DEBUG_LOG_DOMAIN_USER) == 0)
89 return (g_hash_table_lookup (domains_hash, domain) != NULL);
98 ring_buffer = g_new0 (gchar *, ring_buffer_max_lines);
99 ring_buffer_next_index = 0;
100 ring_buffer_num_lines = 0;
104 add_to_ring (gchar *str)
108 g_assert (str != NULL);
110 if (ring_buffer_num_lines == ring_buffer_max_lines) {
111 /* We have an overlap, and the ring_buffer_next_index pogints to
112 * the "first" item. Free it to make room for the new item.
115 g_assert (ring_buffer[ring_buffer_next_index] != NULL);
116 g_free (ring_buffer[ring_buffer_next_index]);
118 ring_buffer_num_lines++;
120 g_assert (ring_buffer_num_lines <= ring_buffer_max_lines);
122 ring_buffer[ring_buffer_next_index] = str;
124 ring_buffer_next_index++;
125 if (ring_buffer_next_index == ring_buffer_max_lines) {
126 ring_buffer_next_index = 0;
127 g_assert (ring_buffer_num_lines == ring_buffer_max_lines);
132 add_to_milestones (const gchar *str)
136 str_copy = g_strdup (str);
138 if (milestones_tail) {
139 milestones_tail = g_slist_append (milestones_tail, str_copy);
140 milestones_tail = milestones_tail->next;
142 milestones_head = milestones_tail = g_slist_append (NULL, str_copy);
145 g_assert (milestones_head != NULL && milestones_tail != NULL);
154 e_debug_logv (gboolean is_milestone,
166 if (!(is_milestone || is_domain_enabled (domain)))
169 str = g_strdup_vprintf (format, args);
170 gettimeofday (&tv, NULL);
172 tm = *localtime (&tv.tv_sec);
174 debug_str = g_strdup_printf (
175 "%p;%04d/%02d/%02d;%02d:%02d:%02d.%04d;(%s);%s",
183 (gint) (tv.tv_usec / 100),
187 add_to_ring (debug_str);
189 add_to_milestones (debug_str);
196 * e_debug_log_load_configuration:
201 e_debug_log_load_configuration (const gchar *filename,
210 g_assert (filename != NULL);
211 g_assert (error == NULL || *error == NULL);
213 key_file = g_key_file_new ();
215 if (!g_key_file_load_from_file (
216 key_file, filename, G_KEY_FILE_NONE, error)) {
217 g_key_file_free (key_file);
224 strings = g_key_file_get_string_list (
225 key_file, KEY_FILE_GROUP,
226 KEY_FILE_DOMAINS_KEY, &num_strings, &my_error);
228 g_error_free (my_error);
232 for (i = 0; i < num_strings; i++)
233 strings[i] = g_strstrip (strings[i]);
235 e_debug_log_enable_domains (
236 (const gchar **) strings, num_strings);
237 g_strfreev (strings);
240 /* Number of lines */
243 num = g_key_file_get_integer (
244 key_file, KEY_FILE_GROUP,
245 KEY_FILE_MAX_LINES_KEY, &my_error);
247 g_error_free (my_error);
249 e_debug_log_set_max_lines (num);
251 g_key_file_free (key_file);
256 * e_debug_log_enable_domains:
261 e_debug_log_enable_domains (const gchar **domains,
266 g_assert (domains != NULL);
267 g_assert (n_domains >= 0);
272 domains_hash = g_hash_table_new (g_str_hash, g_str_equal);
274 for (i = 0; i < n_domains; i++) {
275 g_assert (domains[i] != NULL);
277 if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
278 continue; /* user actions are always enabled */
280 if (g_hash_table_lookup (domains_hash, domains[i]) == NULL) {
283 domain = g_strdup (domains[i]);
284 g_hash_table_insert (domains_hash, domain, domain);
292 * e_debug_log_disable_domains:
297 e_debug_log_disable_domains (const gchar **domains,
302 g_assert (domains != NULL);
303 g_assert (n_domains >= 0);
308 for (i = 0; i < n_domains; i++) {
311 g_assert (domains[i] != NULL);
313 if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
314 continue; /* user actions are always enabled */
316 domain = g_hash_table_lookup (domains_hash, domains[i]);
318 g_hash_table_remove (domains_hash, domain);
322 } /* else, there is nothing to disable */
328 * e_debug_log_is_domain_enabled:
333 e_debug_log_is_domain_enabled (const gchar *domain)
337 g_assert (domain != NULL);
340 retval = is_domain_enabled (domain);
346 struct domains_dump_closure {
352 domains_foreach_dump_cb (gpointer key,
356 struct domains_dump_closure *closure;
362 closure->domains[closure->num_domains] = domain;
363 closure->num_domains++;
367 make_key_file_from_configuration (void)
370 struct domains_dump_closure closure;
373 key_file = g_key_file_new ();
378 num_domains = g_hash_table_size (domains_hash);
379 if (num_domains != 0) {
380 closure.domains = g_new (gchar *, num_domains);
381 closure.num_domains = 0;
383 g_hash_table_foreach (
385 domains_foreach_dump_cb,
387 g_assert (num_domains == closure.num_domains);
389 g_key_file_set_string_list (
392 KEY_FILE_DOMAINS_KEY,
393 (const gchar * const *) closure.domains,
394 closure.num_domains);
395 g_free (closure.domains);
401 g_key_file_set_integer (
402 key_file, KEY_FILE_GROUP,
403 KEY_FILE_MAX_LINES_KEY, ring_buffer_max_lines);
409 write_string (const gchar *filename,
414 if (fputs (str, file) == EOF) {
421 g_file_error_from_errno (saved_errno),
422 "error when writing to log file %s", filename);
431 dump_configuration (const gchar *filename,
443 "This configuration for the debug log can be re-created\n"
444 "by putting the following in ~/evolution-data-server-debug-log.conf\n"
445 "(use ';' to separate domain names):\n\n", error)) {
451 key_file = make_key_file_from_configuration ();
453 data = g_key_file_to_data (key_file, &length, error);
457 if (!write_string (filename, file, data, error)) {
463 g_key_file_free (key_file);
468 dump_milestones (const gchar *filename,
474 if (!write_string (filename, file, "===== BEGIN MILESTONES =====\n", error))
477 for (l = milestones_head; l; l = l->next) {
481 if (!(write_string (filename, file, str, error)
482 && write_string (filename, file, "\n", error)))
486 if (!write_string (filename, file, "===== END MILESTONES =====\n", error))
493 dump_ring_buffer (const gchar *filename,
500 if (!write_string (filename, file, "===== BEGIN RING BUFFER =====\n", error))
503 if (ring_buffer_num_lines == ring_buffer_max_lines)
504 start_index = ring_buffer_next_index;
508 for (i = 0; i < ring_buffer_num_lines; i++) {
511 idx = (start_index + i) % ring_buffer_max_lines;
513 if (!(write_string (filename, file, ring_buffer[idx], error)
514 && write_string (filename, file, "\n", error))) {
519 if (!write_string (filename, file, "===== END RING BUFFER =====\n", error))
531 e_debug_log_dump (const gchar *filename,
537 g_assert (error == NULL || *error == NULL);
543 file = fopen (filename, "w");
551 g_file_error_from_errno (saved_errno),
552 "could not open log file %s", filename);
556 if (!(dump_milestones (filename, file, error)
557 && dump_ring_buffer (filename, file, error)
558 && dump_configuration (filename, file, error))) {
566 if (fclose (file) != 0) {
571 if (error && *error) {
572 g_error_free (*error);
579 g_file_error_from_errno (saved_errno),
580 "error when closing log file %s", filename);
591 * e_debug_log_dump_to_dated_file:
596 e_debug_log_dump_to_dated_file (GError **error)
604 g_assert (error == NULL || *error == NULL);
607 tm = *localtime (&t);
609 basename = g_strdup_printf (
610 "e-debug-log-%04d-%02d-%02d-%02d-%02d-%02d.txt",
617 filename = g_build_filename (g_get_home_dir (), basename, NULL);
619 retval = e_debug_log_dump (filename, error);
628 * e_debug_log_set_max_lines:
633 e_debug_log_set_max_lines (gint num_lines)
638 g_assert (num_lines > 0);
642 if (num_lines == ring_buffer_max_lines)
645 new_buffer = g_new0 (gchar *, num_lines);
647 lines_to_copy = MIN (num_lines, ring_buffer_num_lines);
653 if (ring_buffer_num_lines == ring_buffer_max_lines)
655 (ring_buffer_next_index +
656 ring_buffer_max_lines - lines_to_copy) %
657 ring_buffer_max_lines;
659 start_index = ring_buffer_num_lines - lines_to_copy;
661 g_assert (start_index >= 0 && start_index < ring_buffer_max_lines);
663 for (i = 0; i < lines_to_copy; i++) {
666 idx = (start_index + i) % ring_buffer_max_lines;
668 new_buffer[i] = ring_buffer[idx];
669 ring_buffer[idx] = NULL;
672 for (i = 0; i < ring_buffer_max_lines; i++)
673 g_free (ring_buffer[i]);
675 g_free (ring_buffer);
678 ring_buffer = new_buffer;
679 ring_buffer_next_index = lines_to_copy;
680 ring_buffer_num_lines = lines_to_copy;
681 ring_buffer_max_lines = num_lines;
689 * e_debug_log_get_max_lines:
694 e_debug_log_get_max_lines (void)
699 retval = ring_buffer_max_lines;
711 e_debug_log_clear (void)
720 for (i = 0; i < ring_buffer_max_lines; i++) {
721 g_free (ring_buffer[i]);
722 ring_buffer[i] = NULL;
725 ring_buffer_next_index = 0;
726 ring_buffer_num_lines = 0;