summary refs log tree commit diff
path: root/src/nm-logging.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/nm-logging.c
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/nm-logging.c')
-rw-r--r--src/nm-logging.c566
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);
 }
+