[v12,12/14] log: add support for systemd journal

Message ID 20240325205405.669897-13-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 success coding style OK

Commit Message

Stephen Hemminger March 25, 2024, 8:47 p.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/

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

Patch

diff --git a/lib/log/log.c b/lib/log/log.c
index d8974c66db..0f7bdb3f25 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -17,6 +17,10 @@ 
 #include <rte_os_shim.h>
 #else
 #include <syslog.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #endif
 
 #include <rte_log.h>
@@ -56,6 +60,7 @@  static struct rte_logs {
 	FILE *file;     /**< Output file set by rte_openlog_stream, or NULL. */
 #ifndef RTE_EXEC_ENV_WINDOWS
 	enum eal_log_syslog syslog_opt;
+	int journal_fd;
 #endif
 	log_print_t print_func;
 
@@ -758,6 +763,112 @@  static cookie_io_functions_t syslog_log_func = {
 	.write = syslog_log_write,
 	.close = syslog_log_close,
 };
+
+
+/*
+ * 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 not add one */
+	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);
+}
+
+/*
+ * 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;
+}
+
+/* Connect to systemd's journal service */
+static int
+open_journal(const char *id)
+{
+	char *syslog_id = NULL;
+	struct sockaddr_un sun = {
+		.sun_family = AF_UNIX,
+		.sun_path = "/run/systemd/journal/socket",
+	};
+	ssize_t len;
+	int s;
+
+	s = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0)
+		return -1;
+
+	if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) < 0)
+		goto error;
+
+	/* Send syslog identifier as first message */
+	len = asprintf(&syslog_id, "SYSLOG_IDENTIFIER=%s\n", id);
+	if (len == 0)
+		goto error;
+
+	if (write(s, syslog_id, len) != len)
+		goto error;
+
+	return s;
+error:
+	close(s);
+	return -1;
+}
 #endif
 
 
@@ -765,11 +876,24 @@  static cookie_io_functions_t syslog_log_func = {
 static void
 log_output_selection(const char *id)
 {
+#ifdef RTE_EXEC_ENV_WINDOWS
 	RTE_SET_USED(id);
-
-#ifndef RTE_EXEC_ENV_WINDOWS
+#else
 	bool is_terminal = isatty(STDERR_FILENO);
 
+	/* If stderr is redirected to systemd journal then upgrade */
+	if (using_journal()) {
+		int jfd = open_journal(id);
+
+		if (jfd < 0) {
+			RTE_LOG_LINE(NOTICE, EAL, "Cannot connect to journal: %s",
+				     strerror(errno));
+		} else {
+			rte_logs.print_func = journal_print;
+			return;
+		}
+	}
+
 	if (!(rte_logs.syslog_opt == EAL_LOG_SYSLOG_NONE ||
 	      (rte_logs.syslog_opt == EAL_LOG_SYSLOG_AUTO && is_terminal))) {
 		int flags = LOG_NDELAY | LOG_PID;