1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif /* HAVE_CONFIG_H */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30
31 #include <glib.h>
32
33 #include "cut-logger.h"
34 #include "cut-enum-types.h"
35 #include "../gcutter/gcut-marshalers.h"
36
37 #define DEFAULT_KEY "default"
38 #define DEFAULT_LEVEL \
39 (CUT_LOG_LEVEL_CRITICAL | \
40 CUT_LOG_LEVEL_ERROR | \
41 CUT_LOG_LEVEL_WARNING | \
42 CUT_LOG_LEVEL_MESSAGE)
43 #define DEFAULT_ITEM \
44 (CUT_LOG_ITEM_TIME)
45
46 #define CUT_LOGGER_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
48 CUT_TYPE_LOGGER, \
49 CutLoggerPrivate))
50
51 #define INTERNAL_INTERESTING_LOG_LEVEL \
52 (cut_logger_get_interesting_level(singleton_cut_logger))
53
54 #define INTERNAL_LOG(level, format, ...) do { \
55 if (INTERNAL_INTERESTING_LOG_LEVEL & (level)) { \
56 cut_logger_log(singleton_cut_logger, \
57 CUT_LOG_DOMAIN, \
58 (level), \
59 __FILE__, \
60 __LINE__, \
61 G_STRFUNC, \
62 format, ## __VA_ARGS__); \
63 } \
64 } while (0)
65
66
67 typedef struct _CutLoggerPrivate CutLoggerPrivate;
68 struct _CutLoggerPrivate
69 {
70 CutLogLevelFlags target_level;
71 CutLogItemFlags target_item;
72 GHashTable *interesting_levels;
73 CutLogLevelFlags interesting_level;
74 };
75
76 enum
77 {
78 PROP_0,
79 PROP_TARGET_LEVEL,
80 PROP_TARGET_ITEM,
81 PROP_INTERESTING_LEVEL
82 };
83
84 enum
85 {
86 LOG,
87 LAST_SIGNAL
88 };
89
90 static gint signals[LAST_SIGNAL] = {0};
91
92 static CutLogger *singleton_cut_logger = NULL;
93
94 G_DEFINE_TYPE(CutLogger, cut_logger, G_TYPE_OBJECT);
95
96 static void dispose (GObject *object);
97 static void set_property (GObject *object,
98 guint prop_id,
99 const GValue *value,
100 GParamSpec *pspec);
101 static void get_property (GObject *object,
102 guint prop_id,
103 GValue *value,
104 GParamSpec *pspec);
105
106 static void
cut_logger_class_init(CutLoggerClass * klass)107 cut_logger_class_init (CutLoggerClass *klass)
108 {
109 GObjectClass *gobject_class;
110 GParamSpec *spec;
111
112 gobject_class = G_OBJECT_CLASS(klass);
113
114 gobject_class->dispose = dispose;
115 gobject_class->set_property = set_property;
116 gobject_class->get_property = get_property;
117
118 spec = g_param_spec_flags("target-level",
119 "Target log level",
120 "The target log level",
121 CUT_TYPE_LOG_LEVEL_FLAGS,
122 CUT_LOG_LEVEL_DEFAULT,
123 G_PARAM_READWRITE);
124 g_object_class_install_property(gobject_class, PROP_TARGET_LEVEL, spec);
125
126 spec = g_param_spec_flags("target-item",
127 "Target log item",
128 "The target log item",
129 CUT_TYPE_LOG_ITEM_FLAGS,
130 CUT_LOG_ITEM_DEFAULT,
131 G_PARAM_READWRITE);
132 g_object_class_install_property(gobject_class, PROP_TARGET_ITEM, spec);
133
134 spec = g_param_spec_flags("interesting-level",
135 "Interesting log level",
136 "The interesting log level",
137 CUT_TYPE_LOG_LEVEL_FLAGS,
138 DEFAULT_LEVEL,
139 G_PARAM_READABLE);
140 g_object_class_install_property(gobject_class, PROP_INTERESTING_LEVEL, spec);
141
142 signals[LOG] =
143 g_signal_new("log",
144 G_TYPE_FROM_CLASS(klass),
145 G_SIGNAL_RUN_LAST,
146 G_STRUCT_OFFSET(CutLoggerClass, log),
147 NULL, NULL,
148 _gcut_marshal_VOID__STRING_FLAGS_STRING_UINT_STRING_POINTER_STRING,
149 G_TYPE_NONE, 7,
150 G_TYPE_STRING, CUT_TYPE_LOG_LEVEL_FLAGS, G_TYPE_STRING,
151 G_TYPE_UINT, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
152
153 g_type_class_add_private(gobject_class, sizeof(CutLoggerPrivate));
154 }
155
156 static void
cut_logger_init(CutLogger * logger)157 cut_logger_init (CutLogger *logger)
158 {
159 CutLoggerPrivate *priv;
160
161 priv = CUT_LOGGER_GET_PRIVATE(logger);
162 priv->target_level = CUT_LOG_LEVEL_DEFAULT;
163 priv->interesting_levels = g_hash_table_new_full(g_str_hash, g_str_equal,
164 g_free, NULL);
165 priv->interesting_level = DEFAULT_LEVEL;
166 g_hash_table_insert(priv->interesting_levels,
167 g_strdup(DEFAULT_KEY),
168 GUINT_TO_POINTER(priv->interesting_level));
169 }
170
171 static void
dispose(GObject * object)172 dispose (GObject *object)
173 {
174 CutLoggerPrivate *priv;
175
176 priv = CUT_LOGGER_GET_PRIVATE(object);
177
178 if (priv->interesting_levels) {
179 g_hash_table_unref(priv->interesting_levels);
180 priv->interesting_levels = NULL;
181 }
182
183 G_OBJECT_CLASS(cut_logger_parent_class)->dispose(object);
184 }
185
186 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)187 set_property (GObject *object,
188 guint prop_id,
189 const GValue *value,
190 GParamSpec *pspec)
191 {
192 CutLoggerPrivate *priv;
193
194 priv = CUT_LOGGER_GET_PRIVATE(object);
195 switch (prop_id) {
196 case PROP_TARGET_LEVEL:
197 priv->target_level = g_value_get_flags(value);
198 break;
199 case PROP_TARGET_ITEM:
200 priv->target_item = g_value_get_flags(value);
201 break;
202 default:
203 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
204 break;
205 }
206 }
207
208 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)209 get_property (GObject *object,
210 guint prop_id,
211 GValue *value,
212 GParamSpec *pspec)
213 {
214 CutLoggerPrivate *priv;
215
216 priv = CUT_LOGGER_GET_PRIVATE(object);
217 switch (prop_id) {
218 case PROP_TARGET_LEVEL:
219 g_value_set_flags(value, priv->target_level);
220 break;
221 case PROP_TARGET_ITEM:
222 g_value_set_flags(value, priv->target_item);
223 break;
224 case PROP_INTERESTING_LEVEL:
225 g_value_set_flags(value, priv->interesting_level);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
229 break;
230 }
231 }
232
233 GQuark
cut_flags_error_quark(void)234 cut_flags_error_quark (void)
235 {
236 return g_quark_from_static_string("cut-flags-error-quark");
237 }
238
239 GQuark
cut_enum_error_quark(void)240 cut_enum_error_quark (void)
241 {
242 return g_quark_from_static_string("cut-enum-error-quark");
243 }
244
245 static guint
flags_from_string(GType flags_type,const gchar * flags_string,guint base_flags,GError ** error)246 flags_from_string (GType flags_type,
247 const gchar *flags_string,
248 guint base_flags,
249 GError **error)
250 {
251 gchar **split_names, **names;
252 GFlagsClass *flags_class;
253 guint flags = 0;
254 gboolean append = TRUE;
255 GString *unknown_strings = NULL;
256
257 if (!flags_string) {
258 g_set_error(error,
259 CUT_FLAGS_ERROR,
260 CUT_FLAGS_ERROR_NULL_NAME,
261 "flags name is NULL (<%s>)",
262 g_type_name(flags_type));
263 return 0;
264 }
265
266 switch (flags_string[0]) {
267 case '+':
268 flags = base_flags;
269 flags_string++;
270 break;
271 case '-':
272 append = FALSE;
273 flags = base_flags;
274 flags_string++;
275 break;
276 default:
277 break;
278 }
279
280 split_names = g_strsplit(flags_string, "|", 0);
281 flags_class = g_type_class_ref(flags_type);
282 for (names = split_names; *names; names++) {
283 gchar *name = *names;
284 gboolean local_append = append;
285
286 switch (name[0]) {
287 case '+':
288 local_append = TRUE;
289 name++;
290 break;
291 case '-':
292 local_append = FALSE;
293 name++;
294 break;
295 default:
296 break;
297 }
298
299 if (g_str_equal(name, "all")) {
300 if (local_append) {
301 flags |= flags_class->mask;
302 } else {
303 flags = 0;
304 }
305 } else {
306 GFlagsValue *value;
307
308 value = g_flags_get_value_by_nick(flags_class, name);
309 if (value) {
310 if (local_append) {
311 flags |= value->value;
312 } else {
313 flags &= ~(value->value);
314 }
315 } else {
316 if (!unknown_strings)
317 unknown_strings = g_string_new(NULL);
318 g_string_append_printf(unknown_strings,
319 "%s<%s>",
320 unknown_strings->len > 0 ? "|" : "",
321 name);
322 }
323 }
324 }
325 g_type_class_unref(flags_class);
326 g_strfreev(split_names);
327
328 if (unknown_strings) {
329 g_set_error(error,
330 CUT_FLAGS_ERROR,
331 CUT_FLAGS_ERROR_UNKNOWN_NAMES,
332 "unknown flag names: [%s](<%s>)",
333 unknown_strings->str,
334 g_type_name(flags_type));
335 g_string_free(unknown_strings, TRUE);
336 }
337
338 return flags;
339 }
340
341 CutLogLevelFlags
cut_log_level_flags_from_string(const gchar * level_name,CutLogLevelFlags base_flags,GError ** error)342 cut_log_level_flags_from_string (const gchar *level_name,
343 CutLogLevelFlags base_flags,
344 GError **error)
345 {
346 if (g_str_equal(level_name, "all")) {
347 return CUT_LOG_LEVEL_ALL;
348 } else {
349 if (base_flags == CUT_LOG_LEVEL_DEFAULT &&
350 level_name &&
351 (level_name[0] == '+' || level_name[0] == '-')) {
352 base_flags = DEFAULT_LEVEL;
353 }
354 return flags_from_string(CUT_TYPE_LOG_LEVEL_FLAGS,
355 level_name,
356 base_flags,
357 error);
358 }
359 }
360
361 static gint
enum_from_string(GType enum_type,const gchar * enum_string,GError ** error)362 enum_from_string (GType enum_type,
363 const gchar *enum_string,
364 GError **error)
365 {
366 GEnumClass *enum_class;
367 GEnumValue *enum_value;
368 gint value = 0;
369
370 if (!enum_string) {
371 g_set_error(error,
372 CUT_ENUM_ERROR,
373 CUT_ENUM_ERROR_NULL_NAME,
374 "enum name is NULL (<%s>)",
375 g_type_name(enum_type));
376 return 0;
377 }
378
379 enum_class = g_type_class_ref(enum_type);
380 enum_value = g_enum_get_value_by_nick(enum_class, enum_string);
381 if (enum_value) {
382 value = enum_value->value;
383 } else {
384 g_set_error(error,
385 CUT_ENUM_ERROR,
386 CUT_ENUM_ERROR_UNKNOWN_NAME,
387 "unknown enum name: <%s>(<%s>)",
388 enum_string,
389 g_type_name(enum_type));
390 }
391 g_type_class_unref(enum_class);
392
393 return value;
394 }
395
396 CutLogItemFlags
cut_log_item_flags_from_string(const gchar * item_name,CutLogItemFlags base_flags,GError ** error)397 cut_log_item_flags_from_string (const gchar *item_name,
398 CutLogItemFlags base_flags,
399 GError **error)
400 {
401 if (base_flags == CUT_LOG_ITEM_DEFAULT &&
402 item_name &&
403 (item_name[0] == '+' || item_name[0] == '-')) {
404 base_flags = DEFAULT_ITEM;
405 }
406 return flags_from_string(CUT_TYPE_LOG_ITEM_FLAGS,
407 item_name,
408 base_flags,
409 error);
410 }
411
412 #define BLACK_COLOR "\033[01;30m"
413 #define BLACK_BACK_COLOR "\033[40m"
414 #define RED_COLOR "\033[01;31m"
415 #define RED_BACK_COLOR "\033[41m"
416 #define GREEN_COLOR "\033[01;32m"
417 #define GREEN_BACK_COLOR "\033[01;42m"
418 #define YELLOW_COLOR "\033[01;33m"
419 #define YELLOW_BACK_COLOR "\033[01;43m"
420 #define BLUE_COLOR "\033[01;34m"
421 #define BLUE_BACK_COLOR "\033[01;44m"
422 #define MAGENTA_COLOR "\033[01;35m"
423 #define MAGENTA_BACK_COLOR "\033[01;45m"
424 #define CYAN_COLOR "\033[01;36m"
425 #define CYAN_BACK_COLOR "\033[01;46m"
426 #define WHITE_COLOR "\033[01;37m"
427 #define WHITE_BACK_COLOR "\033[01;47m"
428 #define NORMAL_COLOR "\033[00m"
429
430 static void
log_message_colorize_console(GString * log,CutLogLevelFlags level,const gchar * message)431 log_message_colorize_console (GString *log,
432 CutLogLevelFlags level,
433 const gchar *message)
434 {
435 const gchar *color = NULL;
436
437 switch (level) {
438 case CUT_LOG_LEVEL_ERROR:
439 color = WHITE_COLOR RED_BACK_COLOR;
440 break;
441 case CUT_LOG_LEVEL_CRITICAL:
442 color = YELLOW_COLOR RED_BACK_COLOR;
443 break;
444 case CUT_LOG_LEVEL_WARNING:
445 color = WHITE_COLOR YELLOW_BACK_COLOR;
446 break;
447 case CUT_LOG_LEVEL_MESSAGE:
448 color = WHITE_COLOR GREEN_BACK_COLOR;
449 break;
450 case CUT_LOG_LEVEL_INFO:
451 color = WHITE_COLOR CYAN_BACK_COLOR;
452 break;
453 case CUT_LOG_LEVEL_DEBUG:
454 color = WHITE_COLOR BLUE_BACK_COLOR;
455 break;
456 case CUT_LOG_LEVEL_TRACE:
457 color = WHITE_COLOR MAGENTA_BACK_COLOR;
458 break;
459 default:
460 color = NULL;
461 break;
462 }
463
464 if (color)
465 g_string_append_printf(log, "%s%s%s", color, message, NORMAL_COLOR);
466 else
467 g_string_append(log, message);
468 }
469
470 static gboolean
guess_console_color_usability(void)471 guess_console_color_usability (void)
472 {
473 const gchar *term;
474
475 term = g_getenv("TERM");
476 if (term && (g_str_has_suffix(term, "term") ||
477 g_str_has_suffix(term, "term-color") ||
478 g_str_equal(term, "screen") ||
479 g_str_equal(term, "linux")))
480 return TRUE;
481
482 return FALSE;
483 }
484
485 static void
log_message(GString * log,CutLogLevelFlags level,const gchar * message)486 log_message (GString *log, CutLogLevelFlags level, const gchar *message)
487 {
488 const gchar *colorize_type;
489 CutLogColorize colorize = CUT_LOG_COLORIZE_DEFAULT;
490
491 colorize_type = g_getenv("CUT_LOG_COLORIZE");
492 if (colorize_type)
493 colorize = enum_from_string(CUT_TYPE_LOG_COLORIZE,
494 colorize_type,
495 NULL);
496
497 if (colorize == CUT_LOG_COLORIZE_DEFAULT) {
498
499 if (isatty(STDOUT_FILENO) && guess_console_color_usability()) {
500 colorize = CUT_LOG_COLORIZE_CONSOLE;
501 } else {
502 colorize = CUT_LOG_COLORIZE_NONE;
503 }
504 }
505
506 switch (colorize) {
507 case CUT_LOG_COLORIZE_CONSOLE:
508 log_message_colorize_console(log, level, message);
509 break;
510 default:
511 g_string_append(log, message);
512 break;
513 }
514 }
515
516 static inline void
check_cut_debug(CutLogLevelFlags level)517 check_cut_debug (CutLogLevelFlags level)
518 {
519 if (CUT_LOG_LEVEL_CRITICAL <= level &&
520 level <= CUT_LOG_LEVEL_WARNING) {
521 const gchar *cut_debug;
522
523 cut_debug = g_getenv("CUT_DEBUG");
524 if (cut_debug &&
525 (strcmp(cut_debug, "fatal-warnings") == 0 ||
526 strcmp(cut_debug, "fatal_warnings") == 0)) {
527 abort();
528 }
529 }
530 }
531
532 void
cut_logger_default_log_handler(CutLogger * logger,const gchar * domain,CutLogLevelFlags level,const gchar * file,guint line,const gchar * function,GTimeVal * time_value,const gchar * message,gpointer user_data)533 cut_logger_default_log_handler (CutLogger *logger, const gchar *domain,
534 CutLogLevelFlags level,
535 const gchar *file, guint line,
536 const gchar *function,
537 GTimeVal *time_value, const gchar *message,
538 gpointer user_data)
539 {
540 CutLoggerPrivate *priv;
541 CutLogLevelFlags target_level;
542 CutLogItemFlags target_item;
543 GString *log;
544
545 priv = CUT_LOGGER_GET_PRIVATE(logger);
546 target_level = cut_logger_get_resolved_target_level(logger);
547 if (!(level & target_level))
548 return;
549
550 check_cut_debug(level);
551
552 log = g_string_new(NULL);
553
554 target_item = priv->target_item;
555 if (target_item == CUT_LOG_ITEM_DEFAULT)
556 target_item = DEFAULT_ITEM;
557
558 if (target_item & CUT_LOG_ITEM_LEVEL) {
559 GFlagsClass *flags_class;
560
561 flags_class = g_type_class_ref(CUT_TYPE_LOG_LEVEL_FLAGS);
562 if (flags_class) {
563 if (level & flags_class->mask) {
564 guint i;
565 for (i = 0; i < flags_class->n_values; i++) {
566 GFlagsValue *value = flags_class->values + i;
567 if (level & value->value)
568 g_string_append_printf(log, "[%s]", value->value_nick);
569 }
570 }
571 g_type_class_unref(flags_class);
572 }
573 }
574
575 if (domain && (target_item & CUT_LOG_ITEM_DOMAIN))
576 g_string_append_printf(log, "[%s]", domain);
577
578 if (target_item & CUT_LOG_ITEM_TIME) {
579 gchar *time_string;
580
581 time_string = g_time_val_to_iso8601(time_value);
582 g_string_append_printf(log, "[%s]", time_string);
583 g_free(time_string);
584 }
585
586 if (target_item & CUT_LOG_ITEM_NAME) {
587 if (log->len > 0)
588 g_string_append(log, " ");
589 g_string_append_printf(log, "[%s]", g_get_prgname());
590 }
591
592 if (target_item & CUT_LOG_ITEM_PID) {
593 if (log->len > 0 && !(target_item & CUT_LOG_ITEM_NAME))
594 g_string_append(log, " ");
595 g_string_append_printf(log, "[%u]", getpid());
596 }
597
598 if (file && (target_item & CUT_LOG_ITEM_LOCATION)) {
599 if (log->len > 0)
600 g_string_append(log, " ");
601 g_string_append_printf(log, "%s:%d: ", file, line);
602 if (function)
603 g_string_append_printf(log, "%s(): ", function);
604 } else {
605 if (log->len > 0)
606 g_string_append(log, ": ");
607 }
608
609 log_message(log, level, message);
610 g_string_append(log, "\n");
611 g_print("%s", log->str);
612 g_string_free(log, TRUE);
613 }
614
615 CutLogger *
cut_logger(void)616 cut_logger (void)
617 {
618 if (!singleton_cut_logger) {
619 GError *error = NULL;
620
621 singleton_cut_logger = cut_logger_new();
622 cut_logger_connect_default_handler(singleton_cut_logger);
623 if (!cut_logger_set_target_level_by_string(singleton_cut_logger,
624 g_getenv("CUT_LOG_LEVEL"),
625 &error)) {
626 INTERNAL_LOG(CUT_LOG_LEVEL_WARNING,
627 "[logger][level][set][warning] %s", error->message);
628 g_error_free(error);
629 error = NULL;
630 }
631 if (!cut_logger_set_target_item_by_string(singleton_cut_logger,
632 g_getenv("CUT_LOG_ITEM"),
633 &error)) {
634 INTERNAL_LOG(CUT_LOG_LEVEL_WARNING,
635 "[logger][item][set][warning] %s", error->message);
636 g_error_free(error);
637 error = NULL;
638 }
639 }
640
641 return singleton_cut_logger;
642 }
643
644 CutLogger *
cut_logger_new(void)645 cut_logger_new (void)
646 {
647 return g_object_new(CUT_TYPE_LOGGER,
648 NULL);
649 }
650
651 void
cut_logger_log(CutLogger * logger,const gchar * domain,CutLogLevelFlags level,const gchar * file,guint line,const gchar * function,const gchar * format,...)652 cut_logger_log (CutLogger *logger,
653 const gchar *domain, CutLogLevelFlags level,
654 const gchar *file, guint line, const gchar *function,
655 const gchar *format, ...)
656 {
657 va_list args;
658
659 va_start(args, format);
660 cut_logger_log_va_list(logger, domain, level, file, line, function,
661 format, args);
662 va_end(args);
663 }
664
665 void
cut_logger_log_va_list(CutLogger * logger,const gchar * domain,CutLogLevelFlags level,const gchar * file,guint line,const gchar * function,const gchar * format,va_list args)666 cut_logger_log_va_list (CutLogger *logger,
667 const gchar *domain, CutLogLevelFlags level,
668 const gchar *file, guint line, const gchar *function,
669 const gchar *format, va_list args)
670 {
671 GTimeVal time_value;
672 gchar *message;
673
674 g_get_current_time(&time_value);
675
676 message = g_strdup_vprintf(format, args);
677 g_signal_emit(logger, signals[LOG], 0,
678 domain, level, file, line, function, &time_value, message);
679 g_free(message);
680 }
681
682 CutLogLevelFlags
cut_logger_get_target_level(CutLogger * logger)683 cut_logger_get_target_level (CutLogger *logger)
684 {
685 return CUT_LOGGER_GET_PRIVATE(logger)->target_level;
686 }
687
688 CutLogLevelFlags
cut_logger_get_resolved_target_level(CutLogger * logger)689 cut_logger_get_resolved_target_level (CutLogger *logger)
690 {
691 CutLogLevelFlags target_level;
692
693 target_level = CUT_LOGGER_GET_PRIVATE(logger)->target_level;
694 if (target_level == CUT_LOG_LEVEL_DEFAULT)
695 target_level = DEFAULT_LEVEL;
696 return target_level;
697 }
698
699 void
cut_logger_set_target_level(CutLogger * logger,CutLogLevelFlags level)700 cut_logger_set_target_level (CutLogger *logger,
701 CutLogLevelFlags level)
702 {
703 CutLogLevelFlags interesting_level = level;
704
705 CUT_LOGGER_GET_PRIVATE(logger)->target_level = level;
706 if (interesting_level == CUT_LOG_LEVEL_DEFAULT)
707 interesting_level = DEFAULT_LEVEL;
708 cut_logger_set_interesting_level(logger, DEFAULT_KEY, interesting_level);
709 }
710
711 gboolean
cut_logger_set_target_level_by_string(CutLogger * logger,const gchar * level_name,GError ** error)712 cut_logger_set_target_level_by_string (CutLogger *logger,
713 const gchar *level_name,
714 GError **error)
715 {
716 CutLogLevelFlags level;
717
718 if (level_name) {
719 GError *local_error = NULL;
720 CutLogLevelFlags current_level;
721 current_level = cut_logger_get_target_level(logger);
722 level = cut_log_level_flags_from_string(level_name,
723 current_level,
724 &local_error);
725 if (local_error) {
726 g_propagate_error(error, local_error);
727 return FALSE;
728 }
729 } else {
730 level = CUT_LOG_LEVEL_DEFAULT;
731 }
732 cut_logger_set_target_level(logger, level);
733 return TRUE;
734 }
735
736 static void
update_interesting_level(gpointer key,gpointer value,gpointer user_data)737 update_interesting_level (gpointer key, gpointer value, gpointer user_data)
738 {
739 CutLogLevelFlags *level = user_data;
740
741 *level |= GPOINTER_TO_UINT(value);
742 }
743
744 void
cut_logger_set_interesting_level(CutLogger * logger,const gchar * name,CutLogLevelFlags level)745 cut_logger_set_interesting_level (CutLogger *logger,
746 const gchar *name,
747 CutLogLevelFlags level)
748 {
749 CutLoggerPrivate *priv;
750
751 priv = CUT_LOGGER_GET_PRIVATE(logger);
752
753 g_hash_table_insert(priv->interesting_levels,
754 g_strdup(name),
755 GUINT_TO_POINTER(level));
756 priv->interesting_level = 0;
757 g_hash_table_foreach(priv->interesting_levels,
758 update_interesting_level,
759 &(priv->interesting_level));
760 }
761
762 CutLogLevelFlags
cut_logger_get_interesting_level(CutLogger * logger)763 cut_logger_get_interesting_level (CutLogger *logger)
764 {
765 return CUT_LOGGER_GET_PRIVATE(logger)->interesting_level;
766 }
767
768 CutLogItemFlags
cut_logger_get_target_item(CutLogger * logger)769 cut_logger_get_target_item (CutLogger *logger)
770 {
771 return CUT_LOGGER_GET_PRIVATE(logger)->target_item;
772 }
773
774
775 void
cut_logger_set_target_item(CutLogger * logger,CutLogItemFlags item)776 cut_logger_set_target_item (CutLogger *logger,
777 CutLogItemFlags item)
778 {
779 CUT_LOGGER_GET_PRIVATE(logger)->target_item = item;
780 }
781
782 gboolean
cut_logger_set_target_item_by_string(CutLogger * logger,const gchar * item_name,GError ** error)783 cut_logger_set_target_item_by_string (CutLogger *logger,
784 const gchar *item_name,
785 GError **error)
786 {
787 CutLogItemFlags item;
788
789 if (item_name) {
790 GError *local_error = NULL;
791 CutLogItemFlags current_item;
792 current_item = cut_logger_get_target_item(logger);
793 item = cut_log_item_flags_from_string(item_name,
794 current_item,
795 &local_error);
796 if (local_error) {
797 g_propagate_error(error, local_error);
798 return FALSE;
799 }
800 } else {
801 item = CUT_LOG_ITEM_DEFAULT;
802 }
803 cut_logger_set_target_item(logger, item);
804 return TRUE;
805 }
806
807 void
cut_logger_connect_default_handler(CutLogger * logger)808 cut_logger_connect_default_handler (CutLogger *logger)
809 {
810 g_signal_connect(logger, "log",
811 G_CALLBACK(cut_logger_default_log_handler), NULL);
812 }
813
814 void
cut_logger_disconnect_default_handler(CutLogger * logger)815 cut_logger_disconnect_default_handler (CutLogger *logger)
816 {
817 g_signal_handlers_disconnect_by_func(
818 logger, G_CALLBACK(cut_logger_default_log_handler), NULL);
819 }
820
821 void
cut_glib_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)822 cut_glib_log_handler (const gchar *log_domain,
823 GLogLevelFlags log_level,
824 const gchar *message,
825 gpointer user_data)
826 {
827 CutLogger *logger = user_data;
828
829 if (!logger)
830 logger = cut_logger();
831
832 #define LOG(level) \
833 cut_logger_log(logger, "glib-log", \
834 CUT_LOG_LEVEL_ ## level, \
835 NULL, 0, NULL, \
836 "%s%s%s %s", \
837 log_domain ? "[" : "", \
838 log_domain ? log_domain : "", \
839 log_domain ? "]" : "", \
840 message)
841
842 switch (log_level & G_LOG_LEVEL_MASK) {
843 case G_LOG_LEVEL_ERROR:
844 LOG(ERROR);
845 break;
846 case G_LOG_LEVEL_CRITICAL:
847 LOG(CRITICAL);
848 break;
849 case G_LOG_LEVEL_WARNING:
850 LOG(WARNING);
851 break;
852 case G_LOG_LEVEL_MESSAGE:
853 LOG(MESSAGE);
854 break;
855 case G_LOG_LEVEL_INFO:
856 LOG(INFO);
857 break;
858 case G_LOG_LEVEL_DEBUG:
859 LOG(DEBUG);
860 break;
861 default:
862 break;
863 }
864 #undef LOG
865 }
866
867 /*
868 vi:ts=4:nowrap:ai:expandtab:sw=4
869 */
870