/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ /* calls-log.c * * Copyright 2021 Purism SPC * * Author(s): * Mohammed Sadiq * * SPDX-License-Identifier: GPL-3.0-or-later */ #define _GNU_SOURCE #include "calls-log.h" #include #include #include #include #define DEFAULT_DOMAIN_PREFIX "Calls" static char *domains; static guint verbosity; static gboolean any_domain; static gboolean stderr_is_journal; static void log_str_append_log_domain (GString *log_str, const char *log_domain, gboolean color) { static const char *colors[] = { "\033[1;32m", "\033[1;33m", "\033[1;35m", "\033[1;36m", "\033[1;91m", "\033[1;92m", "\033[1;93m", "\033[1;94m", "\033[1;95m", "\033[1;96m", }; guint i; g_assert (log_domain && *log_domain); i = g_str_hash (log_domain) % G_N_ELEMENTS (colors); if (color) g_string_append (log_str, colors[i]); g_string_append_printf (log_str, "%20s", log_domain); if (color) g_string_append (log_str, "\033[0m"); } static const char * get_log_level_prefix (GLogLevelFlags log_level, gboolean use_color) { /* Ignore custom flags set */ log_level = log_level & ~CALLS_LOG_DETAILED; if (use_color) { switch ((int) log_level) { /* Same colors as used in GLib */ case G_LOG_LEVEL_ERROR: return " \033[1;31mERROR\033[0m"; case G_LOG_LEVEL_CRITICAL: return "\033[1;35mCRITICAL\033[0m"; case G_LOG_LEVEL_WARNING: return " \033[1;33mWARNING\033[0m"; case G_LOG_LEVEL_MESSAGE: return " \033[1;32mMESSAGE\033[0m"; case G_LOG_LEVEL_INFO: return " \033[1;32mINFO\033[0m"; case G_LOG_LEVEL_DEBUG: return " \033[1;32mDEBUG\033[0m"; case CALLS_LOG_LEVEL_TRACE: return " \033[1;36mTRACE\033[0m"; default: return " UNKNOWN"; } } else { switch ((int) log_level) { case G_LOG_LEVEL_ERROR: return " ERROR"; case G_LOG_LEVEL_CRITICAL: return "CRITICAL"; case G_LOG_LEVEL_WARNING: return " WARNING"; case G_LOG_LEVEL_MESSAGE: return " MESSAGE"; case G_LOG_LEVEL_INFO: return " INFO"; case G_LOG_LEVEL_DEBUG: return " DEBUG"; case CALLS_LOG_LEVEL_TRACE: return " TRACE"; default: return " UNKNOWN"; } } } static GLogWriterOutput calls_log_write (GLogLevelFlags log_level, const char *log_domain, const char *log_message, const GLogField *fields, gsize n_fields, gpointer user_data) { g_autoptr (GString) log_str = NULL; FILE *stream; gboolean can_color; if (stderr_is_journal && g_log_writer_journald (log_level, fields, n_fields, user_data) == G_LOG_WRITER_HANDLED) return G_LOG_WRITER_HANDLED; if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) stream = stderr; else stream = stdout; log_str = g_string_new (NULL); /* Add local time */ { char buffer[32]; struct tm tm_now; time_t sec_now; gint64 now; now = g_get_real_time (); sec_now = now / G_USEC_PER_SEC; tm_now = *localtime (&sec_now); strftime (buffer, sizeof (buffer), "%H:%M:%S", &tm_now); g_string_append_printf (log_str, "%s.%04d ", buffer, (int) ((now % G_USEC_PER_SEC) / 100)); } can_color = g_log_writer_supports_color (fileno (stream)); log_str_append_log_domain (log_str, log_domain, can_color); g_string_append_printf (log_str, "[%5d]:", getpid ()); g_string_append_printf (log_str, "%s: ", get_log_level_prefix (log_level, can_color)); if (log_level & CALLS_LOG_DETAILED) { const char *code_func = NULL, *code_line = NULL; for (guint i = 0; i < n_fields; i++) { const GLogField *field = &fields[i]; if (!code_func && g_strcmp0 (field->key, "CODE_FUNC") == 0) code_func = field->value; else if (!code_line && g_strcmp0 (field->key, "CODE_LINE") == 0) code_line = field->value; if (code_func && code_line) break; } if (code_func) { g_string_append_printf (log_str, "%s():", code_func); if (code_line) g_string_append_printf (log_str, "%s:", code_line); g_string_append_c (log_str, ' '); } } g_string_append (log_str, log_message); fprintf (stream, "%s\n", log_str->str); fflush (stream); return G_LOG_WRITER_HANDLED; } static GLogWriterOutput calls_log_handler (GLogLevelFlags log_level, const GLogField *fields, gsize n_fields, gpointer user_data) { const char *log_domain = NULL; const char *log_message = NULL; /* If domain is “all” show logs upto debug regardless of the verbosity */ switch ((int) log_level) { case G_LOG_LEVEL_MESSAGE: if (any_domain && domains) break; if (verbosity < 1) return G_LOG_WRITER_HANDLED; break; case G_LOG_LEVEL_INFO: if (any_domain && domains) break; if (verbosity < 2) return G_LOG_WRITER_HANDLED; break; case G_LOG_LEVEL_DEBUG: if (any_domain && domains) break; if (verbosity < 3) return G_LOG_WRITER_HANDLED; break; case CALLS_LOG_LEVEL_TRACE: if (verbosity < 4) return G_LOG_WRITER_HANDLED; break; default: break; } for (guint i = 0; (!log_domain || !log_message) && i < n_fields; i++) { const GLogField *field = &fields[i]; if (g_strcmp0 (field->key, "GLIB_DOMAIN") == 0) log_domain = field->value; else if (g_strcmp0 (field->key, "MESSAGE") == 0) log_message = field->value; } if (!log_domain) log_domain = "**"; /* Skip logs from other domains if verbosity level is low */ if (any_domain && !domains && verbosity < 5 && log_level > G_LOG_LEVEL_MESSAGE && !strcasestr (log_domain, DEFAULT_DOMAIN_PREFIX)) return G_LOG_WRITER_HANDLED; /* GdkPixbuf logs are too much verbose, skip unless asked not to. */ if (log_level >= G_LOG_LEVEL_MESSAGE && verbosity < 7 && g_strcmp0 (log_domain, "GdkPixbuf") == 0 && (!domains || !strcasestr (domains, log_domain))) return G_LOG_WRITER_HANDLED; if (!log_message) log_message = "(NULL) message"; if (any_domain || strcasestr (domains, log_domain)) return calls_log_write (log_level, log_domain, log_message, fields, n_fields, user_data); return G_LOG_WRITER_HANDLED; } static void calls_log_finalize (void) { g_clear_pointer (&domains, g_free); } void calls_log_init (void) { static gsize initialized = 0; if (g_once_init_enter (&initialized)) { domains = g_strdup (g_getenv ("G_MESSAGES_DEBUG")); if (domains && !*domains) g_clear_pointer (&domains, g_free); if (!domains || g_str_equal (domains, "all")) any_domain = TRUE; stderr_is_journal = g_log_writer_is_journald (fileno (stderr)); g_log_set_writer_func (calls_log_handler, NULL, NULL); g_once_init_leave (&initialized, 1); atexit (calls_log_finalize); } } void calls_log_increase_verbosity (void) { verbosity++; } guint calls_log_get_verbosity (void) { return verbosity; } int calls_log_set_verbosity (guint new_verbosity) { int diff = new_verbosity - verbosity; if (new_verbosity == verbosity) return 0; verbosity = new_verbosity; return diff; }