diff options
Diffstat (limited to 'src/mm-log.c')
-rw-r--r-- | src/mm-log.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/mm-log.c b/src/mm-log.c new file mode 100644 index 0000000..bcf806a --- /dev/null +++ b/src/mm-log.c @@ -0,0 +1,233 @@ +/* -*- 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 <config.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> + +#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) { + time_t secs; + suseconds_t 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 = "debug"; + else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) + prefix = "info"; + else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { + prefix = "warn"; + syslog_priority = LOG_WARNING; + } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { + prefix = "err"; + 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); +} + |