[v11,8/9] log: add support for systemd journal

Message ID 20240324024109.306614-9-stephen@networkplumber.org (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Logging unification and enhancements |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Stephen Hemminger March 24, 2024, 2:33 a.m. UTC
  If DPDK application is being run as a systemd service, then
it can use the journal protocol which allows putting more information
in the log such as priority and other information.

The use of journal protocol is automatically detected and
handled.  Rather than having a dependency on libsystemd,
just use the protocol directly as defined in:
	https://systemd.io/JOURNAL_NATIVE_PROTOCOL/

The journal protocol supports more information that could
be added later.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/log/log.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 136 insertions(+), 5 deletions(-)
  

Patch

diff --git a/lib/log/log.c b/lib/log/log.c
index bd488dff1b..4cfa160e3b 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -13,15 +13,20 @@ 
 #include <sys/queue.h>
 #include <unistd.h>
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+#include <rte_os_shim.h>
+#else
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
 #include <rte_log.h>
 #include <rte_per_lcore.h>
 
 #include "log_internal.h"
 
-#ifdef RTE_EXEC_ENV_WINDOWS
-#include <rte_os_shim.h>
-#endif
-
 struct rte_log_dynamic_type {
 	const char *name;
 	uint32_t loglevel;
@@ -39,11 +44,13 @@  enum eal_log_time_format {
 typedef int (*log_print_t)(FILE *f, uint32_t level, const char *fmt, va_list ap);
 static int log_print(FILE *f, uint32_t level, const char *format, va_list ap);
 
+
 /** The rte_log structure. */
 static struct rte_logs {
 	uint32_t type;  /**< Bitfield with enabled logs. */
 	uint32_t level; /**< Log level. */
 	FILE *file;     /**< Output file set by rte_openlog_stream, or NULL. */
+	int journal_fd;	/**< Journal file descriptor if using */
 	log_print_t print_func;
 
 	enum eal_log_time_format time_format;
@@ -681,6 +688,116 @@  log_print_with_timestamp(FILE *f, uint32_t level,
 	return log_print(f, level, format, ap);
 }
 
+#ifdef RTE_EXEC_ENV_LINUX
+/*
+ * send message using journal protocol to journald
+ */
+__rte_format_printf(3, 0)
+static int
+journal_print(FILE *f __rte_unused, uint32_t level, const char *format, va_list ap)
+{
+	struct iovec iov[3];
+	char *buf = NULL;
+	size_t len;
+	char msg[] = "MESSAGE=";
+	char *prio;
+
+	iov[0].iov_base = msg;
+	iov[0].iov_len = strlen(msg);
+
+	len = vasprintf(&buf, format, ap);
+	if (len == 0)
+		return 0;
+
+	/* check that message ends with newline */
+	if (buf[len - 1] != '\n') {
+		char *clone  = alloca(len + 1);
+		if (clone == NULL)
+			return 0;
+		memcpy(clone, buf, len);
+		clone[len++] = '\n';
+		buf = clone;
+	}
+
+	iov[1].iov_base = buf;
+	iov[1].iov_len = len;
+
+	/* priority value between 0 ("emerg") and 7 ("debug") */
+	len = asprintf(&prio, "PRIORITY=%i\n", level - 1);
+	iov[2].iov_base = prio;
+	iov[2].iov_len = len;
+
+	return writev(rte_logs.journal_fd, iov, 3);
+}
+
+static void
+journal_send_id(int fd, const char *id)
+{
+	char *syslog_id = NULL;
+	size_t len;
+
+	len = asprintf(&syslog_id, "SYSLOG_IDENTIFIER=%s\n", id);
+	if (len > 0)
+		write(fd, syslog_id, len);
+
+}
+
+/*
+ * Check if stderr is going to system journal.
+ * This is the documented way to handle systemd journal
+ *
+ * See: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/
+ *
+ */
+static bool
+using_journal(void)
+{
+	char *jenv, *endp = NULL;
+	struct stat st;
+	unsigned long dev, ino;
+
+	jenv = getenv("JOURNAL_STREAM");
+	if (jenv == NULL)
+		return false;
+
+	if (fstat(STDERR_FILENO, &st) < 0)
+		return false;
+
+	/* systemd sets colon-separated list of device and inode number */
+	dev = strtoul(jenv, &endp, 10);
+	if (endp == NULL || *endp != ':')
+		return false;	/* missing colon */
+
+	ino = strtoul(endp + 1, NULL, 10);
+
+	return dev == st.st_dev && ino == st.st_ino;
+}
+
+/*
+ * If we are being run as systemd service and stderr is going to journal
+ * then upgrade to use journal protocol.
+ */
+static int
+open_journal(void)
+{
+	struct sockaddr_un sun = {
+		.sun_family = AF_UNIX,
+		.sun_path = "/run/systemd/journal/socket",
+	};
+	int s;
+
+	s = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0)
+		return -1;
+
+	if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
+		close(s);
+		return -1;
+	}
+	return s;
+}
+#endif
+
 /* initialize logging */
 void
 eal_log_init(const char *id __rte_unused)
@@ -689,8 +806,22 @@  eal_log_init(const char *id __rte_unused)
 	if (rte_logs.file != NULL)
 		return;
 
-	if (rte_logs.time_format != EAL_LOG_TIMESTAMP_NONE)
+#ifdef RTE_EXEC_ENV_LINUX
+	if (using_journal()) {
+		int jfd = open_journal();
+
+		if (jfd < 0) {
+			RTE_LOG_LINE(NOTICE, EAL, "Cannot connect to journal");
+		} else {
+			rte_logs.journal_fd = jfd;
+			rte_logs.print_func = journal_print;
+			journal_send_id(jfd, id);
+		}
+	} else
+#endif
+	if (rte_logs.time_format != EAL_LOG_TIMESTAMP_NONE) {
 		rte_logs.print_func = log_print_with_timestamp;
+	}
 
 #if RTE_LOG_DP_LEVEL >= RTE_LOG_DEBUG
 	RTE_LOG(NOTICE, EAL,