diff options
Diffstat (limited to 'src/nm-logging.c')
| -rw-r--r-- | src/nm-logging.c | 566 |
1 files changed, 379 insertions, 187 deletions
diff --git a/src/nm-logging.c b/src/nm-logging.c index b8433a25..60ccb5cb 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -32,11 +32,15 @@ #include <strings.h> #include <string.h> -#include <glib/gi18n.h> +#if SYSTEMD_JOURNAL +#define SD_JOURNAL_SUPPRESS_LOCATION +#include <systemd/sd-journal.h> +#endif -#include "nm-glib-compat.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-errors.h" +#include "NetworkManagerUtils.h" +#include "nm-linux-platform.h" static void nm_log_handler (const gchar *log_domain, @@ -44,65 +48,91 @@ nm_log_handler (const gchar *log_domain, const gchar *message, gpointer ignored); -static NMLogLevel log_level = LOGL_INFO; -static char *log_domains; -static NMLogDomain logging[LOGL_MAX]; -static gboolean logging_set_up; -static gboolean syslog_opened; -static char *logging_domains_to_string; - typedef struct { NMLogDomain num; const char *name; } LogDesc; -static const char *level_names[LOGL_MAX] = { - [LOGL_TRACE] = "TRACE", - [LOGL_DEBUG] = "DEBUG", - [LOGL_INFO] = "INFO", - [LOGL_WARN] = "WARN", - [LOGL_ERR] = "ERR", +typedef struct { + const char *name; + const char *level_str; + int syslog_level; + GLogLevelFlags g_log_level; + gboolean full_details; +} LogLevelDesc; + +static struct { + NMLogLevel log_level; + NMLogDomain logging[_LOGL_N_REAL]; + gboolean logging_set_up; + enum { + LOG_BACKEND_GLIB, + LOG_BACKEND_SYSLOG, + LOG_BACKEND_JOURNAL, + LOG_BACKEND_JOURNAL_SYSLOG_STYLE, + } log_backend; + char *logging_domains_to_string; + const LogLevelDesc level_desc[_LOGL_N]; + +#define _DOMAIN_DESC_LEN 36 + /* Would be nice to use C99 flexible array member here, + * but that feature doesn't seem well supported. */ + const LogDesc domain_desc[_DOMAIN_DESC_LEN]; +} global = { + .log_level = LOGL_INFO, + .log_backend = LOG_BACKEND_GLIB, + .level_desc = { + [LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG, G_LOG_LEVEL_DEBUG, TRUE }, + [LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_INFO, G_LOG_LEVEL_DEBUG, TRUE }, + [LOGL_INFO] = { "INFO", "<info>", LOG_INFO, G_LOG_LEVEL_MESSAGE, FALSE }, + [LOGL_WARN] = { "WARN", "<warn>", LOG_WARNING, G_LOG_LEVEL_WARNING, FALSE }, + [LOGL_ERR] = { "ERR", "<error>", LOG_ERR, G_LOG_LEVEL_WARNING, TRUE }, + [_LOGL_OFF] = { "OFF", NULL, 0, 0, FALSE }, + [_LOGL_KEEP] = { "KEEP", NULL, 0, 0, FALSE }, + }, + .domain_desc = { + { LOGD_PLATFORM, "PLATFORM" }, + { LOGD_RFKILL, "RFKILL" }, + { LOGD_ETHER, "ETHER" }, + { LOGD_WIFI, "WIFI" }, + { LOGD_BT, "BT" }, + { LOGD_MB, "MB" }, + { LOGD_DHCP4, "DHCP4" }, + { LOGD_DHCP6, "DHCP6" }, + { LOGD_PPP, "PPP" }, + { LOGD_WIFI_SCAN, "WIFI_SCAN" }, + { LOGD_IP4, "IP4" }, + { LOGD_IP6, "IP6" }, + { LOGD_AUTOIP4, "AUTOIP4" }, + { LOGD_DNS, "DNS" }, + { LOGD_VPN, "VPN" }, + { LOGD_SHARING, "SHARING" }, + { LOGD_SUPPLICANT,"SUPPLICANT" }, + { LOGD_AGENTS, "AGENTS" }, + { LOGD_SETTINGS, "SETTINGS" }, + { LOGD_SUSPEND, "SUSPEND" }, + { LOGD_CORE, "CORE" }, + { LOGD_DEVICE, "DEVICE" }, + { LOGD_OLPC, "OLPC" }, + { LOGD_INFINIBAND,"INFINIBAND" }, + { LOGD_FIREWALL, "FIREWALL" }, + { LOGD_ADSL, "ADSL" }, + { LOGD_BOND, "BOND" }, + { LOGD_VLAN, "VLAN" }, + { LOGD_BRIDGE, "BRIDGE" }, + { LOGD_DBUS_PROPS,"DBUS_PROPS" }, + { LOGD_TEAM, "TEAM" }, + { LOGD_CONCHECK, "CONCHECK" }, + { LOGD_DCB, "DCB" }, + { LOGD_DISPATCH, "DISPATCH" }, + { LOGD_AUDIT, "AUDIT" }, + { 0, NULL } + /* keep _DOMAIN_DESC_LEN in sync */ + }, }; -static const LogDesc domain_descs[] = { - { LOGD_NONE, "NONE" }, - { LOGD_PLATFORM, "PLATFORM" }, - { LOGD_RFKILL, "RFKILL" }, - { LOGD_ETHER, "ETHER" }, - { LOGD_WIFI, "WIFI" }, - { LOGD_BT, "BT" }, - { LOGD_MB, "MB" }, - { LOGD_DHCP4, "DHCP4" }, - { LOGD_DHCP6, "DHCP6" }, - { LOGD_PPP, "PPP" }, - { LOGD_WIFI_SCAN, "WIFI_SCAN" }, - { LOGD_IP4, "IP4" }, - { LOGD_IP6, "IP6" }, - { LOGD_AUTOIP4, "AUTOIP4" }, - { LOGD_DNS, "DNS" }, - { LOGD_VPN, "VPN" }, - { LOGD_SHARING, "SHARING" }, - { LOGD_SUPPLICANT,"SUPPLICANT" }, - { LOGD_AGENTS, "AGENTS" }, - { LOGD_SETTINGS, "SETTINGS" }, - { LOGD_SUSPEND, "SUSPEND" }, - { LOGD_CORE, "CORE" }, - { LOGD_DEVICE, "DEVICE" }, - { LOGD_OLPC, "OLPC" }, - { LOGD_WIMAX, "WIMAX" }, - { LOGD_INFINIBAND,"INFINIBAND" }, - { LOGD_FIREWALL, "FIREWALL" }, - { LOGD_ADSL, "ADSL" }, - { LOGD_BOND, "BOND" }, - { LOGD_VLAN, "VLAN" }, - { LOGD_BRIDGE, "BRIDGE" }, - { LOGD_DBUS_PROPS,"DBUS_PROPS" }, - { LOGD_TEAM, "TEAM" }, - { LOGD_CONCHECK, "CONCHECK" }, - { LOGD_DCB, "DCB" }, - { LOGD_DISPATCH, "DISPATCH" }, - { 0, NULL } -}; +/* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */ +G_STATIC_ASSERT (sizeof (NMLogDomain) >= sizeof (guint64)); /* Combined domains */ #define LOGD_ALL_STRING "ALL" @@ -112,11 +142,15 @@ static const LogDesc domain_descs[] = { /************************************************************************/ +static char *_domains_to_string (gboolean include_level_override); + +/************************************************************************/ + static void _ensure_initialized (void) { - if (G_UNLIKELY (!logging_set_up)) - nm_logging_setup ("INFO", "DEFAULT", NULL, NULL); + if (G_UNLIKELY (!global.logging_set_up)) + nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); } static gboolean @@ -126,8 +160,8 @@ match_log_level (const char *level, { int i; - for (i = 0; i < LOGL_MAX; i++) { - if (!g_ascii_strcasecmp (level_names[i], level)) { + for (i = 0; i < G_N_ELEMENTS (global.level_desc); i++) { + if (!g_ascii_strcasecmp (global.level_desc[i].name, level)) { *out_level = i; return TRUE; } @@ -145,29 +179,39 @@ nm_logging_setup (const char *level, GError **error) { GString *unrecognized = NULL; - NMLogDomain new_logging[LOGL_MAX]; - NMLogLevel new_log_level = log_level; + NMLogDomain new_logging[G_N_ELEMENTS (global.logging)]; + NMLogLevel new_log_level = global.log_level; char **tmp, **iter; int i; + gboolean had_platform_debug; + gs_free char *domains_free = NULL; g_return_val_if_fail (!bad_domains || !*bad_domains, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - logging_set_up = TRUE; + /* domains */ + if (!domains || !*domains) { + domains = global.logging_set_up + ? (domains_free = _domains_to_string (FALSE)) + : LOGD_DEFAULT_STRING; + } - for (i = 0; i < LOGL_MAX; i++) + global.logging_set_up = TRUE; + + for (i = 0; i < G_N_ELEMENTS (new_logging); i++) new_logging[i] = 0; /* levels */ if (level && *level) { if (!match_log_level (level, &new_log_level, error)) return FALSE; + if (new_log_level == _LOGL_KEEP) { + new_log_level = global.log_level; + for (i = 0; i < G_N_ELEMENTS (new_logging); i++) + new_logging[i] = global.logging[i]; + } } - /* domains */ - if (!domains || !*domains) - domains = log_domains ? log_domains : "DEFAULT"; - tmp = g_strsplit_set (domains, ", ", 0); for (iter = tmp; iter && *iter; iter++) { const LogDesc *diter; @@ -203,48 +247,62 @@ nm_logging_setup (const char *level, /* Check for compatibility domains */ else if (!g_ascii_strcasecmp (*iter, "HW")) bits = LOGD_PLATFORM; + else if (!g_ascii_strcasecmp (*iter, "WIMAX")) + continue; else { - for (diter = &domain_descs[0]; diter->name; diter++) { + for (diter = &global.domain_desc[0]; diter->name; diter++) { if (!g_ascii_strcasecmp (diter->name, *iter)) { bits = diter->num; break; } } - } - if (!bits) { - if (!bad_domains) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, - _("Unknown log domain '%s'"), *iter); - return FALSE; - } + if (!bits) { + if (!bad_domains) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, + _("Unknown log domain '%s'"), *iter); + return FALSE; + } - if (unrecognized) - g_string_append (unrecognized, ", "); - else - unrecognized = g_string_new (NULL); - g_string_append (unrecognized, *iter); - continue; + if (unrecognized) + g_string_append (unrecognized, ", "); + else + unrecognized = g_string_new (NULL); + g_string_append (unrecognized, *iter); + continue; + } } - for (i = 0; i < domain_log_level; i++) - new_logging[i] &= ~bits; - for (i = domain_log_level; i < LOGL_MAX; i++) - new_logging[i] |= bits; + if (domain_log_level == _LOGL_KEEP) { + for (i = 0; i < G_N_ELEMENTS (new_logging); i++) + new_logging[i] = (new_logging[i] & ~bits) | (global.logging[i] & bits); + } else { + for (i = 0; i < G_N_ELEMENTS (new_logging); i++) { + if (i < domain_log_level) + new_logging[i] &= ~bits; + else + new_logging[i] |= bits; + } + } } g_strfreev (tmp); - if (log_domains != (char *)domains) { - g_free (log_domains); - log_domains = g_strdup (domains); - } + g_clear_pointer (&global.logging_domains_to_string, g_free); + + had_platform_debug = nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM); - g_clear_pointer (&logging_domains_to_string, g_free); + global.log_level = new_log_level; + for (i = 0; i < G_N_ELEMENTS (new_logging); i++) + global.logging[i] = new_logging[i]; - log_level = new_log_level; - for (i = 0; i < LOGL_MAX; i++) - logging[i] = new_logging[i]; + if ( had_platform_debug + && !nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { + /* when debug logging is enabled, platform will cache all access to + * sysctl. When the user disables debug-logging, we want to clear that + * cache right away. */ + _nm_linux_platform_sysctl_clear_cache (); + } if (unrecognized) *bad_domains = g_string_free (unrecognized, FALSE); @@ -255,7 +313,7 @@ nm_logging_setup (const char *level, const char * nm_logging_level_to_string (void) { - return level_names[log_level]; + return global.level_desc[global.log_level].name; } const char * @@ -267,10 +325,10 @@ nm_logging_all_levels_to_string (void) int i; str = g_string_new (NULL); - for (i = 0; i < LOGL_MAX; i++) { + for (i = 0; i < G_N_ELEMENTS (global.level_desc); i++) { if (str->len) g_string_append_c (str, ','); - g_string_append (str, level_names[i]); + g_string_append (str, global.level_desc[i].name); } } @@ -282,45 +340,54 @@ nm_logging_domains_to_string (void) { _ensure_initialized (); - if (G_UNLIKELY (!logging_domains_to_string)) { - const LogDesc *diter; - GString *str; - int i; + if (G_UNLIKELY (!global.logging_domains_to_string)) + global.logging_domains_to_string = _domains_to_string (TRUE); - /* We don't just return g_strdup (log_domains) because we want to expand - * "DEFAULT" and "ALL". - */ + return global.logging_domains_to_string; +} - str = g_string_sized_new (75); - for (diter = &domain_descs[0]; diter->name; diter++) { - /* If it's set for any lower level, it will also be set for LOGL_ERR */ - if (!(diter->num & logging[LOGL_ERR])) - continue; +static char * +_domains_to_string (gboolean include_level_override) +{ + const LogDesc *diter; + GString *str; + int i; - if (str->len) - g_string_append_c (str, ','); - g_string_append (str, diter->name); + /* We don't just return g_strdup (global.log_domains) because we want to expand + * "DEFAULT" and "ALL". + */ - /* Check if it's logging at a lower level than the default. */ - for (i = 0; i < log_level; i++) { - if (diter->num & logging[i]) { - g_string_append_printf (str, ":%s", level_names[i]); - break; - } + str = g_string_sized_new (75); + for (diter = &global.domain_desc[0]; diter->name; diter++) { + /* If it's set for any lower level, it will also be set for LOGL_ERR */ + if (!(diter->num & global.logging[LOGL_ERR])) + continue; + + if (str->len) + g_string_append_c (str, ','); + g_string_append (str, diter->name); + + if (!include_level_override) + continue; + + /* Check if it's logging at a lower level than the default. */ + for (i = 0; i < global.log_level; i++) { + if (diter->num & global.logging[i]) { + g_string_append_printf (str, ":%s", global.level_desc[i].name); + break; } - /* Check if it's logging at a higher level than the default. */ - if (!(diter->num & logging[log_level])) { - for (i = log_level + 1; i < LOGL_MAX; i++) { - if (diter->num & logging[i]) { - g_string_append_printf (str, ":%s", level_names[i]); - break; - } + } + /* Check if it's logging at a higher level than the default. */ + if (!(diter->num & global.logging[global.log_level])) { + for (i = global.log_level + 1; i < G_N_ELEMENTS (global.logging); i++) { + if (diter->num & global.logging[i]) { + g_string_append_printf (str, ":%s", global.level_desc[i].name); + break; } } } - logging_domains_to_string = g_string_free (str, FALSE); } - return logging_domains_to_string; + return g_string_free (str, FALSE); } const char * @@ -332,7 +399,7 @@ nm_logging_all_domains_to_string (void) const LogDesc *diter; str = g_string_new (LOGD_DEFAULT_STRING); - for (diter = &domain_descs[0]; diter->name; diter++) { + for (diter = &global.domain_desc[0]; diter->name; diter++) { g_string_append_c (str, ','); g_string_append (str, diter->name); if (diter->num == LOGD_DHCP6) @@ -349,12 +416,40 @@ nm_logging_all_domains_to_string (void) gboolean nm_logging_enabled (NMLogLevel level, NMLogDomain domain) { - g_return_val_if_fail (level < LOGL_MAX, FALSE); + if ((guint) level >= G_N_ELEMENTS (global.logging)) + g_return_val_if_reached (FALSE); _ensure_initialized (); - return !!(logging[level] & domain); + return !!(global.logging[level] & domain); +} + +#if SYSTEMD_JOURNAL +__attribute__((__format__ (__printf__, 4, 5))) +static void +_iovec_set_format (struct iovec *iov, gboolean *iov_free, int i, const char *format, ...) +{ + va_list ap; + char *str; + + va_start (ap, format); + str = g_strdup_vprintf (format, ap); + va_end (ap); + + iov[i].iov_base = str; + iov[i].iov_len = strlen (str); + iov_free[i] = TRUE; +} + +static void +_iovec_set_string (struct iovec *iov, gboolean *iov_free, int i, const char *str, gsize len) +{ + iov[i].iov_base = (char *) str; + iov[i].iov_len = len; + iov_free[i] = FALSE; } +#define _iovec_set_literal_string(iov, iov_free, i, str) _iovec_set_string ((iov), (iov_free), (i), (""str""), STRLEN (str)) +#endif void _nm_log_impl (const char *file, @@ -370,14 +465,13 @@ _nm_log_impl (const char *file, char *msg; char *fullmsg = NULL; GTimeVal tv; - int syslog_level = LOG_INFO; - int g_log_level = G_LOG_LEVEL_INFO; - g_return_if_fail (level < LOGL_MAX); + if ((guint) level >= G_N_ELEMENTS (global.logging)) + g_return_if_reached (); _ensure_initialized (); - if (!(logging[level] & domain)) + if (!(global.logging[level] & domain)) return; /* Make sure that %m maps to the specified error */ @@ -391,44 +485,106 @@ _nm_log_impl (const char *file, msg = g_strdup_vprintf (fmt, args); va_end (args); - switch (level) { - case LOGL_TRACE: - g_get_current_time (&tv); - syslog_level = LOG_DEBUG; - g_log_level = G_LOG_LEVEL_DEBUG; - fullmsg = g_strdup_printf ("<trace> [%ld.%06ld] [%s:%u] %s(): %s", tv.tv_sec, tv.tv_usec, file, line, func, msg); - break; - case LOGL_DEBUG: - g_get_current_time (&tv); - syslog_level = LOG_INFO; - g_log_level = G_LOG_LEVEL_DEBUG; - fullmsg = g_strdup_printf ("<debug> [%ld.%06ld] [%s:%u] %s(): %s", tv.tv_sec, tv.tv_usec, file, line, func, msg); - break; - case LOGL_INFO: - syslog_level = LOG_INFO; - g_log_level = G_LOG_LEVEL_MESSAGE; - fullmsg = g_strconcat ("<info> ", msg, NULL); - break; - case LOGL_WARN: - syslog_level = LOG_WARNING; - g_log_level = G_LOG_LEVEL_WARNING; - fullmsg = g_strconcat ("<warn> ", msg, NULL); - break; - case LOGL_ERR: - syslog_level = LOG_ERR; - /* g_log_level is still WARNING, because ERROR is fatal */ - g_log_level = G_LOG_LEVEL_WARNING; - g_get_current_time (&tv); - fullmsg = g_strdup_printf ("<error> [%ld.%06ld] [%s:%u] %s(): %s", tv.tv_sec, tv.tv_usec, file, line, func, msg); + switch (global.log_backend) { +#if SYSTEMD_JOURNAL + case LOG_BACKEND_JOURNAL: + case LOG_BACKEND_JOURNAL_SYSLOG_STYLE: + { + gint64 now, boottime; +#define _NUM_MAX_FIELDS_SYSLOG_FACILITY 10 +#define _NUM_FIELDS (10 + _NUM_MAX_FIELDS_SYSLOG_FACILITY) + int i_field = 0; + struct iovec iov[_NUM_FIELDS]; + gboolean iov_free[_NUM_FIELDS]; + + now = nm_utils_get_monotonic_timestamp_ns (); + boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1); + + _iovec_set_format (iov, iov_free, i_field++, "PRIORITY=%d", global.level_desc[level].syslog_level); + if ( global.log_backend == LOG_BACKEND_JOURNAL_SYSLOG_STYLE + && global.level_desc[level].full_details) { + g_get_current_time (&tv); + _iovec_set_format (iov, iov_free, i_field++, "MESSAGE=%-7s [%ld.%06ld] [%s:%u] %s(): %s", global.level_desc[level].level_str, tv.tv_sec, tv.tv_usec, file, line, func, msg); + } else + _iovec_set_format (iov, iov_free, i_field++, "MESSAGE=%-7s %s", global.level_desc[level].level_str, msg); + _iovec_set_literal_string (iov, iov_free, i_field++, "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN); + _iovec_set_format (iov, iov_free, i_field++, "SYSLOG_PID=%ld", (long) getpid ()); + { + const LogDesc *diter; + int i_domain = _NUM_MAX_FIELDS_SYSLOG_FACILITY; + const char *s_domain_1 = NULL; + GString *s_domain_all = NULL; + NMLogDomain dom_all = domain; + NMLogDomain dom = dom_all & global.logging[level]; + + for (diter = &global.domain_desc[0]; diter->name; diter++) { + if (!NM_FLAGS_HAS (dom_all, diter->num)) + continue; + + /* construct a list of all domains (not only the enabled ones). + * Note that in by far most cases, there is only one domain present. + * Hence, save the construction of the GString. */ + dom_all &= ~diter->num; + if (!s_domain_1) + s_domain_1 = diter->name; + else { + if (!s_domain_all) + s_domain_all = g_string_new (s_domain_1); + g_string_append_c (s_domain_all, ','); + g_string_append (s_domain_all, diter->name); + } + + if (NM_FLAGS_HAS (dom, diter->num)) { + if (i_domain > 0) { + /* SYSLOG_FACILITY is specified multiple times for each domain that is actually enabled. */ + _iovec_set_format (iov, iov_free, i_field++, "SYSLOG_FACILITY=%s", diter->name); + i_domain--; + } + dom &= ~diter->num; + } + if (!dom && !dom_all) + break; + } + if (s_domain_all) { + _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_DOMAINS=%s", s_domain_all->str); + g_string_free (s_domain_all, TRUE); + } else + _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_DOMAINS=%s", s_domain_1); + } + _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_LEVEL=%s", global.level_desc[level].name); + _iovec_set_format (iov, iov_free, i_field++, "CODE_FUNC=%s", func); + _iovec_set_format (iov, iov_free, i_field++, "CODE_FILE=%s", file); + _iovec_set_format (iov, iov_free, i_field++, "CODE_LINE=%u", line); + _iovec_set_format (iov, iov_free, i_field++, "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000)); + _iovec_set_format (iov, iov_free, i_field++, "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000)); + if (error != 0) + _iovec_set_format (iov, iov_free, i_field++, "ERRNO=%d", error); + + nm_assert (i_field <= G_N_ELEMENTS (iov)); + + sd_journal_sendv (iov, i_field); + + for (; i_field > 0; ) { + i_field--; + if (iov_free[i_field]) + g_free (iov[i_field].iov_base); + } + } break; +#endif default: - g_assert_not_reached (); - } + if (global.level_desc[level].full_details) { + g_get_current_time (&tv); + fullmsg = g_strdup_printf ("%-7s [%ld.%06ld] [%s:%u] %s(): %s", global.level_desc[level].level_str, tv.tv_sec, tv.tv_usec, file, line, func, msg); + } else + fullmsg = g_strdup_printf ("%-7s %s", global.level_desc[level].level_str, msg); - if (syslog_opened) - syslog (syslog_level, "%s", fullmsg); - else - g_log (G_LOG_DOMAIN, g_log_level, "%s", fullmsg); + if (global.log_backend == LOG_BACKEND_SYSLOG) + syslog (global.level_desc[level].syslog_level, "%s", fullmsg); + else + g_log (G_LOG_DOMAIN, global.level_desc[level].g_log_level, "%s", fullmsg); + break; + } g_free (msg); g_free (fullmsg); @@ -442,7 +598,7 @@ nm_log_handler (const gchar *log_domain, const gchar *message, gpointer ignored) { - int syslog_priority; + int syslog_priority; switch (level & G_LOG_LEVEL_MASK) { case G_LOG_LEVEL_ERROR: @@ -466,30 +622,66 @@ nm_log_handler (const gchar *log_domain, break; } - syslog (syslog_priority, "%s", message); + switch (global.log_backend) { +#if SYSTEMD_JOURNAL + case LOG_BACKEND_JOURNAL: + case LOG_BACKEND_JOURNAL_SYSLOG_STYLE: + { + gint64 now, boottime; + + now = nm_utils_get_monotonic_timestamp_ns (); + boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1); + + sd_journal_send ("PRIORITY=%d", syslog_priority, + "MESSAGE=%s", str_if_set (message, ""), + "SYSLOG_IDENTIFIER=%s", G_LOG_DOMAIN, + "SYSLOG_PID=%ld", (long) getpid (), + "SYSLOG_FACILITY=GLIB", + "GLIB_DOMAIN=%s", str_if_set (log_domain, ""), + "GLIB_LEVEL=%d", (int) (level & G_LOG_LEVEL_MASK), + "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000), + "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000), + NULL); + } + break; +#endif + default: + syslog (syslog_priority, "%s", str_if_set (message, "")); + break; + } } void -nm_logging_syslog_openlog (gboolean debug) +nm_logging_syslog_openlog (const char *logging_backend) { - if (debug) - openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER); - else - openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON); + if (global.log_backend != LOG_BACKEND_GLIB) + g_return_if_reached (); - if (!syslog_opened) { - syslog_opened = TRUE; + if (!logging_backend) + logging_backend = ""NM_CONFIG_LOGGING_BACKEND_DEFAULT; - g_log_set_handler (G_LOG_DOMAIN, - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - nm_log_handler, - NULL); + if (strcmp (logging_backend, "debug") == 0) { + global.log_backend = LOG_BACKEND_SYSLOG; + openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER); +#if SYSTEMD_JOURNAL + } else if (strcmp (logging_backend, "syslog") != 0) { + if (strcmp (logging_backend, "journal-syslog-style") != 0) + global.log_backend = LOG_BACKEND_JOURNAL; + else + global.log_backend = LOG_BACKEND_JOURNAL_SYSLOG_STYLE; + + /* ensure we read a monotonic timestamp. Reading the timestamp the first + * time causes a logging message. We don't want to do that during _nm_log_impl. */ + nm_utils_get_monotonic_timestamp_ns (); +#endif + } else { + global.log_backend = LOG_BACKEND_SYSLOG; + openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON); } -} -void -nm_logging_syslog_closelog (void) -{ - if (syslog_opened) - closelog (); + g_log_set_handler (G_LOG_DOMAIN, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + nm_log_handler, + NULL); } + |