/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * * Copyright (C) 2011 Red Hat, Inc. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "mm-log.h" enum { TS_FLAG_NONE = 0, TS_FLAG_WALL, TS_FLAG_REL }; static gboolean ts_flags = TS_FLAG_NONE; static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR; static GTimeVal rel_start = { 0, 0 }; static int logfd = -1; typedef struct { guint32 num; const char *name; } LogDesc; static const LogDesc level_descs[] = { { LOGL_ERR, "ERR" }, { LOGL_WARN | LOGL_ERR, "WARN" }, { LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" }, { LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" }, { 0, NULL } }; void _mm_log (const char *loc, const char *func, guint32 level, const char *fmt, ...) { va_list args; char *msg; GTimeVal tv; char tsbuf[100] = { 0 }; char msgbuf[512] = { 0 }; int syslog_priority = LOG_INFO; const char *prefix = NULL; ssize_t ign; if (!(log_level & level)) return; va_start (args, fmt); msg = g_strdup_vprintf (fmt, args); va_end (args); if (ts_flags == TS_FLAG_WALL) { g_get_current_time (&tv); snprintf (&tsbuf[0], sizeof (tsbuf), " [%09ld.%06ld]", tv.tv_sec, tv.tv_usec); } else if (ts_flags == TS_FLAG_REL) { glong secs; glong usecs; g_get_current_time (&tv); secs = tv.tv_sec - rel_start.tv_sec; usecs = tv.tv_usec - rel_start.tv_usec; if (usecs < 0) { secs--; usecs += 1000000; } snprintf (&tsbuf[0], sizeof (tsbuf), " [%06ld.%06ld]", secs, usecs); } if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) prefix = ""; else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) prefix = " "; else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { prefix = " "; syslog_priority = LOG_WARNING; } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { prefix = ""; syslog_priority = LOG_ERR; } else g_warn_if_reached (); if (prefix) { if (log_level & LOGL_DEBUG) snprintf (msgbuf, sizeof (msgbuf), "%s%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); else snprintf (msgbuf, sizeof (msgbuf), "%s%s %s\n", prefix, tsbuf, msg); if (logfd < 0) syslog (syslog_priority, "%s", msgbuf); else { ign = write (logfd, msgbuf, strlen (msgbuf)); if (ign) {} /* whatever; really shut up about unused result */ fsync (logfd); /* Make sure output is dumped to disk immediately */ } } g_free (msg); } static void log_handler (const gchar *log_domain, GLogLevelFlags level, const gchar *message, gpointer ignored) { int syslog_priority; ssize_t ign; switch (level) { case G_LOG_LEVEL_ERROR: syslog_priority = LOG_CRIT; break; case G_LOG_LEVEL_CRITICAL: syslog_priority = LOG_ERR; break; case G_LOG_LEVEL_WARNING: syslog_priority = LOG_WARNING; break; case G_LOG_LEVEL_MESSAGE: syslog_priority = LOG_NOTICE; break; case G_LOG_LEVEL_DEBUG: syslog_priority = LOG_DEBUG; break; case G_LOG_LEVEL_INFO: default: syslog_priority = LOG_INFO; break; } if (logfd < 0) syslog (syslog_priority, "%s", message); else { ign = write (logfd, message, strlen (message)); if (ign) {} /* whatever; really shut up about unused result */ } } gboolean mm_log_setup (const char *level, const char *log_file, gboolean show_timestamps, gboolean rel_timestamps, GError **error) { /* levels */ if (level && strlen (level)) { gboolean found = FALSE; const LogDesc *diter; for (diter = &level_descs[0]; diter->name; diter++) { if (!strcasecmp (diter->name, level)) { log_level = diter->num; found = TRUE; break; } } if (!found) { g_set_error (error, 0, 0, "Unknown log level '%s'", level); return FALSE; } } if (show_timestamps) ts_flags = TS_FLAG_WALL; else if (rel_timestamps) ts_flags = TS_FLAG_REL; /* Grab start time for relative timestamps */ g_get_current_time (&rel_start); if (log_file == NULL) openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON); else { logfd = open (log_file, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (logfd < 0) { g_set_error (error, 0, 0, "Failed to open log file: (%d) %s", errno, strerror (errno)); return FALSE; } } g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL); return TRUE; } void mm_log_usr1 (void) { } void mm_log_shutdown (void) { if (logfd < 0) closelog (); else close (logfd); }