From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 746A7459D7; Thu, 19 Sep 2024 17:08:12 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id DDCD2433BE; Thu, 19 Sep 2024 17:06:36 +0200 (CEST) Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) by mails.dpdk.org (Postfix) with ESMTP id 34ADD433A3 for ; Thu, 19 Sep 2024 17:06:31 +0200 (CEST) Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-71781f42f75so889589b3a.1 for ; Thu, 19 Sep 2024 08:06:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1726758390; x=1727363190; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wuSjSa35Zp6DKCw2curz41jubbYWUKlY61Ld4E16Nmo=; b=ZHcjnLxPayVM43Mki21EEwYhMmUDrnNnPOVaq1qu9QWq+bAd5w3GS8u8A6uXQVs2H8 JReP7kb9sEGBVpvVtv3eVKykq9I0RlJ/aMtYNTPbroRaJINKQqie7ZsCRS1LP67nG3QN 4RnRMa0Uv0U85AmVXcCg0NBWTZrdGGllv8qdn3KJqLqvFpwxG65q8eo67HJjCYgiLfnn kk6/wyMkTK/TQopFDvXfXu4p7RXWt+gORqhxMsCwCrIKGJ9dmHcC4kpAI7mrkv5Y0yl4 qf+oG6QrRC2aE45v6MivJg3vKbSc+u55LRBAWmrOcDpgPbQ5JQYkQwXFdRlvV+28GZEq C6YA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726758390; x=1727363190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wuSjSa35Zp6DKCw2curz41jubbYWUKlY61Ld4E16Nmo=; b=FqZxXjc+9uhRH/5fWqx0M7XwSma+hb5VGiaR7NkWvEsCO7mFH022axHf6xfncC380p 2NnJJ6KaKZaVvLPKSI3fYEhdTqBXW+QhCQLyQMoEoARW9os03NHuT6plsAUaYlkryVxL bLKyCgMv8aoBvMQ2Ebj+MG5hkiwuEHTcBM0rhgR43GBUq9fxmOfaJ+UFrb2sCP9XzrYE axbaYNlAkBqc1vlSJjqe1zEDbE0S/MFendft++5RZXOXVjYRR7VJshFACx9iIO3eAL7I 3B9ud2rW+tzPUjv5sgBtVshzm/gn3HK1nkot4hIA7bqDd11HY5tn88M7ywVpDle/fNcj n+zA== X-Gm-Message-State: AOJu0YxzLZ6Sn4iW1XxR1Bi7CaaNwkzXvA0Wg+86LDGfSyTvKGxrlnDc /OOK4hkvOQXLaEAv3j8lOd6+05EEuv0aQsBFnwTbLcCct0ckP+16TEKbtCURyzf04oI2hFzpAH9 O X-Google-Smtp-Source: AGHT+IG7zBmV9ZklhYPZ73nw+tb4pQOEQfjiSZQRCDsZD9BGJcgJ2jmtcSIxS5lrAcjdfKqWyPDWqg== X-Received: by 2002:a05:6a00:4613:b0:710:5a64:30d5 with SMTP id d2e1a72fcca58-71926065766mr44740247b3a.6.1726758390253; Thu, 19 Sep 2024 08:06:30 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71944adfd3dsm8620841b3a.92.2024.09.19.08.06.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Sep 2024 08:06:29 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger , =?UTF-8?q?Morten=20Br=C3=B8rup?= , Bruce Richardson Subject: [PATCH v25 13/15] log: add support for systemd journal Date: Thu, 19 Sep 2024 08:04:19 -0700 Message-ID: <20240919150609.5281-14-stephen@networkplumber.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240919150609.5281-1-stephen@networkplumber.org> References: <20200814173441.23086-1-stephen@networkplumber.org> <20240919150609.5281-1-stephen@networkplumber.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 Acked-by: Morten Brørup Acked-by: Bruce Richardson --- doc/guides/prog_guide/log_lib.rst | 14 ++ lib/eal/common/eal_common_options.c | 15 +++ lib/eal/common/eal_options.h | 2 + lib/log/log.c | 4 +- lib/log/log_internal.h | 3 + lib/log/log_journal.c | 200 ++++++++++++++++++++++++++++ lib/log/log_private.h | 16 +++ lib/log/meson.build | 4 + lib/log/version.map | 1 + 9 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 lib/log/log_journal.c diff --git a/doc/guides/prog_guide/log_lib.rst b/doc/guides/prog_guide/log_lib.rst index abaedc7212..476dedb097 100644 --- a/doc/guides/prog_guide/log_lib.rst +++ b/doc/guides/prog_guide/log_lib.rst @@ -100,6 +100,20 @@ option. There are three possible settings for this option: If ``--syslog`` option is not specified, then only console (stderr) will be used. +Messages can be redirected to systemd journal which is an enhanced version of syslog with the ``--log-journal`` option. + +There are three possible settings for this option: + +*auto* + If stderr is redirected to journal by ``systemd`` service + then use journal socket to instead of stderr for log. + This is the default. + +*never* + Do not try to use journal. + +*always* + Always try to direct messages to journal socket. Using Logging APIs to Generate Log Messages diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index ac1aadbef4..6184d27ad3 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -74,6 +74,7 @@ eal_long_options[] = { {OPT_IOVA_MODE, 1, NULL, OPT_IOVA_MODE_NUM }, {OPT_LCORES, 1, NULL, OPT_LCORES_NUM }, {OPT_LOG_LEVEL, 1, NULL, OPT_LOG_LEVEL_NUM }, + {OPT_LOG_JOURNAL, 2, NULL, OPT_LOG_JOURNAL_NUM }, {OPT_LOG_TIMESTAMP, 2, NULL, OPT_LOG_TIMESTAMP_NUM }, {OPT_TRACE, 1, NULL, OPT_TRACE_NUM }, {OPT_TRACE_DIR, 1, NULL, OPT_TRACE_DIR_NUM }, @@ -1617,6 +1618,7 @@ eal_log_level_parse(int argc, char * const argv[]) switch (opt) { case OPT_LOG_LEVEL_NUM: case OPT_SYSLOG_NUM: + case OPT_LOG_JOURNAL_NUM: case OPT_LOG_TIMESTAMP_NUM: if (eal_parse_common_option(opt, optarg, internal_conf) < 0) return -1; @@ -1840,6 +1842,16 @@ eal_parse_common_option(int opt, const char *optarg, break; #endif +#ifdef RTE_EXEC_ENV_LINUX + case OPT_LOG_JOURNAL_NUM: + if (eal_log_journal(optarg) < 0) { + EAL_LOG(ERR, "invalid parameters for --" + OPT_LOG_JOURNAL); + return -1; + } + break; +#endif + case OPT_LOG_LEVEL_NUM: if (eal_parse_log_level(optarg) < 0) { EAL_LOG(ERR, @@ -2217,6 +2229,9 @@ eal_common_usage(void) " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" #ifndef RTE_EXEC_ENV_WINDOWS " --"OPT_SYSLOG"[=] Enable use of syslog\n" +#endif +#ifdef RTE_EXEC_ENV_LINUX + " --"OPT_LOG_JOURNAL"[=] Enable use of systemd journal\n" #endif " --"OPT_LOG_LEVEL"= Set global log level\n" " --"OPT_LOG_LEVEL"=:\n" diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h index e24c9eca53..c5a1c70288 100644 --- a/lib/eal/common/eal_options.h +++ b/lib/eal/common/eal_options.h @@ -35,6 +35,8 @@ enum { OPT_LCORES_NUM, #define OPT_LOG_LEVEL "log-level" OPT_LOG_LEVEL_NUM, +#define OPT_LOG_JOURNAL "log-journal" + OPT_LOG_JOURNAL_NUM, #define OPT_LOG_TIMESTAMP "log-timestamp" OPT_LOG_TIMESTAMP_NUM, #define OPT_TRACE "trace" diff --git a/lib/log/log.c b/lib/log/log.c index f748551bc0..96fa651e81 100644 --- a/lib/log/log.c +++ b/lib/log/log.c @@ -511,7 +511,9 @@ eal_log_init(const char *id) is_terminal = isatty(STDERR_FILENO); #endif - if (log_syslog_enabled(is_terminal)) + if (log_journal_enabled()) + log_journal_open(id); + else if (log_syslog_enabled(is_terminal)) log_syslog_open(id, is_terminal); else if (log_timestamp_enabled()) rte_logs.print_func = log_print_with_timestamp; diff --git a/lib/log/log_internal.h b/lib/log/log_internal.h index 7c7d44eed2..82fdc21ac2 100644 --- a/lib/log/log_internal.h +++ b/lib/log/log_internal.h @@ -29,6 +29,9 @@ int eal_log_save_pattern(const char *pattern, uint32_t level); __rte_internal int eal_log_syslog(const char *name); +__rte_internal +int eal_log_journal(const char *opt); + /* * Convert log level to string. */ diff --git a/lib/log/log_journal.c b/lib/log/log_journal.c new file mode 100644 index 0000000000..2475174fce --- /dev/null +++ b/lib/log/log_journal.c @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log_internal.h" +#include "log_private.h" + +static enum { + LOG_JOURNAL_NEVER = 0, /* do not use journal */ + LOG_JOURNAL_AUTO, /* use if stderr is set to journal */ + LOG_JOURNAL_ALWAYS, /* always try to use journal */ +} log_journal_opt = LOG_JOURNAL_AUTO; + +static int log_journal_fd = -1; + +int +eal_log_journal(const char *str) +{ + if (str == NULL || strcmp(str, "auto") == 0) + log_journal_opt = LOG_JOURNAL_AUTO; + else if (strcmp(str, "always") == 0) + log_journal_opt = LOG_JOURNAL_ALWAYS; + else if (strcmp(str, "never") == 0) + log_journal_opt = LOG_JOURNAL_NEVER; + else + return -1; + return 0; +} + +/* + * send structured message using journal protocol + * See: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/ + */ +static int +journal_send(const char *buf, size_t len) +{ + struct iovec iov[4]; + unsigned int n = 0; + int priority = rte_log_cur_msg_loglevel() - 1; + char msg[] = "MESSAGE="; + char newline = '\n'; + char pbuf[16]; /* "PRIORITY=N\n" */ + + iov[n].iov_base = msg; + iov[n++].iov_len = strlen(msg); + + iov[n].iov_base = (char *)(uintptr_t)buf; + iov[n++].iov_len = len; + + /* if message doesn't end with newline, one will be applied. */ + if (buf[len - 1] != '\n') { + iov[n].iov_base = &newline; + iov[n++].iov_len = 1; + } + + /* priority value between 0 ("emerg") and 7 ("debug") */ + iov[n].iov_base = pbuf; + iov[n++].iov_len = snprintf(pbuf, sizeof(pbuf), + "PRIORITY=%d\n", priority); + + return writev(log_journal_fd, iov, n); +} + + +/* wrapper for log stream to put messages into journal */ +static ssize_t +journal_log_write(__rte_unused void *c, const char *buf, size_t size) +{ + return journal_send(buf, size); +} + +static int +journal_log_close(__rte_unused void *c) +{ + close(log_journal_fd); + log_journal_fd = -1; + return 0; +} + +static cookie_io_functions_t journal_log_func = { + .write = journal_log_write, + .close = journal_log_close, +}; + +/* + * 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 +is_journal(int fd) +{ + char *jenv, *endp = NULL; + struct stat st; + unsigned long dev, ino; + + jenv = getenv("JOURNAL_STREAM"); + if (jenv == NULL) + return false; + + if (fstat(fd, &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(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) { + perror("socket"); + return -1; + } + + if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) < 0) { + perror("connect"); + close(s); + return -1; + } + + return s; +} + +bool log_journal_enabled(void) +{ + if (log_journal_opt == LOG_JOURNAL_NEVER) + return false; + + if (log_journal_opt == LOG_JOURNAL_AUTO && + !is_journal(STDERR_FILENO)) + return false; + + log_journal_fd = open_journal(); + if (log_journal_fd < 0) + return false; + + return true; +} + +void log_journal_open(const char *id) +{ + FILE *log_stream; + char *syslog_id = NULL; + ssize_t len; + + /* Send identifier as first message */ + len = asprintf(&syslog_id, "SYSLOG_IDENTIFIER=%s\nSYSLOG_PID=%u", + id, getpid()); + if (len == 0) + goto error; + + if (write(log_journal_fd, syslog_id, len) != len) { + perror("write"); + goto error; + } + + free(syslog_id); + + /* redirect other log messages to journal */ + log_stream = fopencookie(NULL, "w", journal_log_func); + if (log_stream != NULL) { + rte_openlog_stream(log_stream); + return; + } + +error: + free(syslog_id); + close(log_journal_fd); + log_journal_fd = -1; +} diff --git a/lib/log/log_private.h b/lib/log/log_private.h index 532a481340..d8602681b2 100644 --- a/lib/log/log_private.h +++ b/lib/log/log_private.h @@ -34,4 +34,20 @@ bool log_syslog_enabled(bool is_tty); void log_syslog_open(const char *id, bool is_terminal); #endif +#ifdef RTE_EXEC_ENV_LINUX +bool log_journal_enabled(void); +void log_journal_open(const char *id); +#else +static inline bool +log_journal_enabled(void) +{ + return false; +} + +static inline void +log_journal_open(const char *id __rte_unused) +{ +} +#endif /* !RTE_EXEC_ENV_LINUX */ + #endif /* LOG_PRIVATE_H */ diff --git a/lib/log/meson.build b/lib/log/meson.build index 4ac232786e..86e4452b19 100644 --- a/lib/log/meson.build +++ b/lib/log/meson.build @@ -11,4 +11,8 @@ if not is_windows sources += files('log_syslog.c') endif +if is_linux + sources += files('log_journal.c') +endif + headers = files('rte_log.h') diff --git a/lib/log/version.map b/lib/log/version.map index 8be6907840..800d3943bc 100644 --- a/lib/log/version.map +++ b/lib/log/version.map @@ -26,6 +26,7 @@ INTERNAL { global: eal_log_init; + eal_log_journal; # WINDOWS_NO_EXPORT eal_log_level2str; eal_log_save_pattern; eal_log_save_regexp; -- 2.45.2