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