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 EFDBF43D8B; Sat, 30 Mar 2024 04:06:18 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 8232442D7D; Sat, 30 Mar 2024 04:05:05 +0100 (CET) Received: from mail-pf1-f180.google.com (mail-pf1-f180.google.com [209.85.210.180]) by mails.dpdk.org (Postfix) with ESMTP id D53E640A7A for ; Sat, 30 Mar 2024 04:04:52 +0100 (CET) Received: by mail-pf1-f180.google.com with SMTP id d2e1a72fcca58-6ea9a60f7f5so2098762b3a.3 for ; Fri, 29 Mar 2024 20:04:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1711767892; x=1712372692; 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=ww8CUWIQ8YnJlzWRLIjlLDsitSx2+/jhf75yL3uVx1w=; b=Bq5/5qeiGv4lwTuSjffmdqivgpQ4PLFCkaJY2nZJjzRUurD5SBUaf4dKWisV8Gt/Qr v3Bs8dPNY4rc4OeZZoF2t4H6l+oAmAUyV90EhrJguAihzsP7dP2d4c3pnsgfpLOKKaFL KIUd06x3mUd+OCkkbTejtJ1pWVb/c6WsuxI0FYI6kRoA+2sHVCbMPg/duhFhPgGCKYIh XhEl4WFHhLk0VSPRW7RGt4CJvzEk6GSc77vVnAGUpqseoHwn5xjRQQbejv+rFYPHyOBP 7bNm+TKrZZPLbKrcRbtrowAhhJOw0cYLBAEFYTipj9k1nEPR1yY50XahWf2YXFqEFyxv dnNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711767892; x=1712372692; 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=ww8CUWIQ8YnJlzWRLIjlLDsitSx2+/jhf75yL3uVx1w=; b=m9zxaJ4S8bwk8/rrlLD+IRP0WJLY5w6capx41fVSp5bqqAvOxt9Rb6NjcijTOArATc rihUTCGeoON1dN/dYSlaMzW5vXsMT4nw/GCYsoOt3pbIbVq7iE5RMqXAZA0EcgFmTHaI lHVFxHzj9azKCGk8OaOA4nThrBALozC84HXw8bHZQbrjxMWZuRhdxaHM3ymWBFaIDM1M CDBo3OZ3RiN/bmj2P/wY1WUc0AbBo4WfbL/4+sDLeToyEpoFcBbdmsb33n5F1L5lbwGC Y4TuCA0kRAwb6/J0gsKGU2lL3AddQsWWpxNHloWYKL9+kXQSJIZ1yd08Hx4oKnf2S5mn KuMA== X-Gm-Message-State: AOJu0YwpGw/S3WLZbWCSEJqoXXUyR6po2ezf0fghTVgGWnlmXb8nSmeq klY/VgYE16ceXCVOmsRwHnjwSEvWJxQJJteldLZt5W5+0RAnnj/0RzhEBFGCLDU8WcHrqu4L4Wi q X-Google-Smtp-Source: AGHT+IFftd4hSgivmSjjLy1q/zXTnawprDqzGqghAKiE03vS90zBzx7oJitrFKHnxYXXCtlYjjUB0Q== X-Received: by 2002:a05:6a00:1c99:b0:6ea:afd1:90e6 with SMTP id y25-20020a056a001c9900b006eaafd190e6mr3475571pfw.6.1711767892057; Fri, 29 Mar 2024 20:04:52 -0700 (PDT) Received: from hermes.local (204-195-123-203.wavecable.com. [204.195.123.203]) by smtp.gmail.com with ESMTPSA id o3-20020a056a00214300b006e6288ef4besm3655486pfk.54.2024.03.29.20.04.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 20:04:51 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [PATCH v19 14/15] log: add support for systemd journal Date: Fri, 29 Mar 2024 20:00:57 -0700 Message-ID: <20240330030429.4630-15-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240330030429.4630-1-stephen@networkplumber.org> References: <20200814173441.23086-1-stephen@networkplumber.org> <20240330030429.4630-1-stephen@networkplumber.org> MIME-Version: 1.0 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 --- doc/guides/prog_guide/log_lib.rst | 14 ++ lib/eal/common/eal_common_options.c | 11 ++ lib/eal/common/eal_options.h | 2 + lib/log/log.c | 5 + lib/log/log_internal.h | 3 + lib/log/log_journal.c | 216 ++++++++++++++++++++++++++++ lib/log/log_private.h | 4 + lib/log/log_stubs.c | 6 + lib/log/meson.build | 5 + lib/log/version.map | 1 + 10 files changed, 267 insertions(+) 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 9ca7db04aa..9a82118184 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; @@ -1843,6 +1845,14 @@ eal_parse_common_option(int opt, const char *optarg, return -1; } break; + + 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: @@ -2223,6 +2233,7 @@ 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" + " --"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 ddb8400d73..d97bde984e 100644 --- a/lib/log/log.c +++ b/lib/log/log.c @@ -515,6 +515,11 @@ eal_log_init(const char *id) #else bool is_terminal = isatty(STDERR_FILENO); +#ifdef RTE_EXEC_ENV_LINUX + if (log_journal_enabled(id)) + rte_logs.print_func = journal_print; + else +#endif if (log_syslog_enabled(is_terminal)) log_syslog_open(id, is_terminal); else 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..f7b5aafc9d --- /dev/null +++ b/lib/log/log_journal.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#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(uint32_t level, const char *buf, size_t len) +{ + struct iovec iov[3]; + const unsigned int niov = RTE_DIM(iov); + char msg[] = "MESSAGE="; + char pbuf[16]; /* "PRIORITY=N\n" */ + + iov[0].iov_base = msg; + iov[0].iov_len = strlen(msg); + + iov[1].iov_base = (char *)(uintptr_t)buf; + iov[1].iov_len = len; + + /* priority value between 0 ("emerg") and 7 ("debug") */ + iov[2].iov_base = pbuf; + iov[2].iov_len = snprintf(pbuf, sizeof(pbuf), + "PRIORITY=%i\n", level - 1); + + return writev(log_journal_fd, iov, niov); +} + +int +journal_print(FILE *f __rte_unused, uint32_t level, + const char *format, va_list ap) +{ + char *buf = NULL; + size_t len; + int ret; + + 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 *nbuf; + + nbuf = realloc(buf, len + 1); + if (nbuf == NULL) { + free(buf); + return 0; + } + buf = nbuf; + buf[len++] = '\n'; + } + + ret = journal_send(level, buf, len); + free(buf); + return ret; +} + +/* 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(rte_log_cur_msg_loglevel(), 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(const char *id) +{ + FILE *log_stream; + char *syslog_id = NULL; + ssize_t len; + + 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; + + /* 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 true; + } + +error: + free(syslog_id); + close(log_journal_fd); + log_journal_fd = -1; + return false; +} diff --git a/lib/log/log_private.h b/lib/log/log_private.h index 0e7d10ffca..988579fc08 100644 --- a/lib/log/log_private.h +++ b/lib/log/log_private.h @@ -18,5 +18,9 @@ int log_print_with_timestamp(FILE *f, uint32_t level, const char *format, va_lis bool log_syslog_enabled(bool is_tty); void log_syslog_open(const char *id, bool is_terminal); +bool log_journal_enabled(const char *id); + +__rte_format_printf(3, 0) +int journal_print(FILE *f, uint32_t level, const char *format, va_list ap); #endif /* LOG_PRIVATE_H */ diff --git a/lib/log/log_stubs.c b/lib/log/log_stubs.c index cb34217af8..803a6e9f6b 100644 --- a/lib/log/log_stubs.c +++ b/lib/log/log_stubs.c @@ -26,3 +26,9 @@ eal_log_syslog(const char *str __rte_unused) { return -1; } + +int +eal_log_journal(const char *str __rte_unused) +{ + return -1; +} diff --git a/lib/log/meson.build b/lib/log/meson.build index 37507299e7..5b9be7f6f1 100644 --- a/lib/log/meson.build +++ b/lib/log/meson.build @@ -7,10 +7,15 @@ sources = files( 'log_timestamp.c', ) +if is_linux + sources += files('log_journal.c') +endif + if is_windows sources += files('log_stubs.c') else sources += files('log_syslog.c') endif + headers = files('rte_log.h') diff --git a/lib/log/version.map b/lib/log/version.map index 14d6681a5f..7af97ece43 100644 --- a/lib/log/version.map +++ b/lib/log/version.map @@ -26,6 +26,7 @@ INTERNAL { global: eal_log_init; + eal_log_journal; eal_log_level2str; eal_log_save_pattern; eal_log_save_regexp; -- 2.43.0