[PATCH v24 12/15] log: add optional support of syslog

Stephen Hemminger stephen at networkplumber.org
Wed Sep 18 22:52:11 CEST 2024


Log to syslog only if option is specified. And if syslog is used
then normally only log to syslog, don't duplicate output.
Also enables syslog support on FreeBSD.

Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
Acked-by: Morten Brørup <mb at smartsharesystems.com>
Acked-by: Bruce Richardson <bruce.richardson at intel.com>
Acked-by: Chengwen Feng <fengchengwen at huawei.com>
---
 app/test/test_eal_flags.c                     |  5 +-
 doc/guides/linux_gsg/linux_eal_parameters.rst | 27 ------
 doc/guides/prog_guide/log_lib.rst             | 17 ++++
 lib/eal/common/eal_common_options.c           |  5 +-
 lib/log/log.c                                 | 28 +++---
 lib/log/log_private.h                         | 17 ++++
 lib/log/log_syslog.c                          | 88 +++++++++++++++++++
 lib/log/meson.build                           |  4 +
 lib/log/version.map                           |  2 +-
 9 files changed, 150 insertions(+), 43 deletions(-)
 create mode 100644 lib/log/log_syslog.c

diff --git a/app/test/test_eal_flags.c b/app/test/test_eal_flags.c
index 40dd288d5f..06295030b2 100644
--- a/app/test/test_eal_flags.c
+++ b/app/test/test_eal_flags.c
@@ -987,9 +987,10 @@ test_misc_flags(void)
 	/* With empty --syslog */
 	const char *argv3[] = {prgname, prefix, mp_flag, "--syslog"};
 	/* With valid --syslog */
-	const char *argv4[] = {prgname, prefix, mp_flag, "--syslog", "always"};
+	const char *argv4[] = {prgname, prefix, mp_flag, "--syslog=both"};
 	/* With invalid --syslog */
-	const char *argv5[] = {prgname, prefix, mp_flag, "--syslog", "error"};
+	const char *argv5[] = {prgname, prefix, mp_flag, "--syslog=invalid"};
+
 	/* With no-sh-conf, also use no-huge to ensure this test runs on BSD */
 	const char *argv6[] = {prgname, "-m", DEFAULT_MEM_SIZE,
 			no_shconf, nosh_prefix, no_huge};
diff --git a/doc/guides/linux_gsg/linux_eal_parameters.rst b/doc/guides/linux_gsg/linux_eal_parameters.rst
index ea8f381391..d86f94d8a8 100644
--- a/doc/guides/linux_gsg/linux_eal_parameters.rst
+++ b/doc/guides/linux_gsg/linux_eal_parameters.rst
@@ -108,30 +108,3 @@ Memory-related options
 *   ``--match-allocations``
 
     Free hugepages back to system exactly as they were originally allocated.
-
-Other options
-~~~~~~~~~~~~~
-
-*   ``--syslog <syslog facility>``
-
-    Set syslog facility. Valid syslog facilities are::
-
-        auth
-        cron
-        daemon
-        ftp
-        kern
-        lpr
-        mail
-        news
-        syslog
-        user
-        uucp
-        local0
-        local1
-        local2
-        local3
-        local4
-        local5
-        local6
-        local7
diff --git a/doc/guides/prog_guide/log_lib.rst b/doc/guides/prog_guide/log_lib.rst
index 504eefe1d2..abaedc7212 100644
--- a/doc/guides/prog_guide/log_lib.rst
+++ b/doc/guides/prog_guide/log_lib.rst
@@ -83,6 +83,23 @@ To prefix all console messages with ISO format time the syntax is::
 
 	/path/to/app --log-timestamp=iso
 
+Log output
+~~~~~~~~~~
+
+If desired, messages can be redirected to syslog (on Linux and FreeBSD) with the ``--syslog``
+option. There are three possible settings for this option:
+
+*always*
+    Redirect all log output to syslog.
+
+*auto*
+    Use console if it is a terminal, and use syslog if is not.
+
+*both*
+    Print to both console and syslog.
+
+If ``--syslog`` option is not specified, then only console (stderr) will be used.
+
 
 
 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 ec3b5c3dfd..ac1aadbef4 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -91,7 +91,7 @@ eal_long_options[] = {
 	{OPT_PROC_TYPE,         1, NULL, OPT_PROC_TYPE_NUM        },
 	{OPT_SOCKET_MEM,        1, NULL, OPT_SOCKET_MEM_NUM       },
 	{OPT_SOCKET_LIMIT,      1, NULL, OPT_SOCKET_LIMIT_NUM     },
-	{OPT_SYSLOG,            1, NULL, OPT_SYSLOG_NUM           },
+	{OPT_SYSLOG,            2, NULL, OPT_SYSLOG_NUM           },
 	{OPT_VDEV,              1, NULL, OPT_VDEV_NUM             },
 	{OPT_VFIO_INTR,         1, NULL, OPT_VFIO_INTR_NUM        },
 	{OPT_VFIO_VF_TOKEN,     1, NULL, OPT_VFIO_VF_TOKEN_NUM    },
@@ -2215,6 +2215,9 @@ eal_common_usage(void)
 	       "                      (can be used multiple times)\n"
 	       "  --"OPT_VMWARE_TSC_MAP"    Use VMware TSC map instead of native RDTSC\n"
 	       "  --"OPT_PROC_TYPE"         Type of this process (primary|secondary|auto)\n"
+#ifndef RTE_EXEC_ENV_WINDOWS
+	       "  --"OPT_SYSLOG"[=<when>]   Enable use of syslog\n"
+#endif
 	       "  --"OPT_LOG_LEVEL"=<level> Set global log level\n"
 	       "  --"OPT_LOG_LEVEL"=<type-match>:<level>\n"
 	       "                      Set specific log level\n"
diff --git a/lib/log/log.c b/lib/log/log.c
index 6ab856272c..f748551bc0 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -12,18 +12,19 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <sys/queue.h>
+#include <unistd.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
 #include <rte_per_lcore.h>
 
-#include "log_internal.h"
-#include "log_private.h"
-
 #ifdef RTE_EXEC_ENV_WINDOWS
 #include <rte_os_shim.h>
 #endif
 
+#include "log_internal.h"
+#include "log_private.h"
+
 struct rte_log_dynamic_type {
 	const char *name;
 	uint32_t loglevel;
@@ -496,20 +497,23 @@ rte_log(uint32_t level, uint32_t logtype, const char *format, ...)
 	return ret;
 }
 
-/* Placeholder */
-int
-eal_log_syslog(const char *mode __rte_unused)
-{
-	return -1;
-}
-
 /*
  * Called by rte_eal_init
  */
 void
-eal_log_init(const char *id __rte_unused)
+eal_log_init(const char *id)
 {
-	if (log_timestamp_enabled())
+	bool is_terminal;
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+	is_terminal = _isatty(_fileno(stderr));
+#else
+	is_terminal = isatty(STDERR_FILENO);
+#endif
+
+	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;
 	else
 		rte_logs.print_func = vfprintf;
diff --git a/lib/log/log_private.h b/lib/log/log_private.h
index 67cfe72fc6..532a481340 100644
--- a/lib/log/log_private.h
+++ b/lib/log/log_private.h
@@ -17,4 +17,21 @@ ssize_t log_timestamp(char *tsbuf, size_t tsbuflen);
 __rte_format_printf(2, 0)
 int log_print_with_timestamp(FILE *f, const char *format, va_list ap);
 
+#ifdef RTE_EXEC_ENV_WINDOWS
+static inline bool
+log_syslog_enabled(bool is_tty __rte_unused)
+{
+	return false;
+}
+
+static inline void
+log_syslog_open(const char *id __rte_unused, bool is_terminal __rte_unused)
+{
+}
+#else
+
+bool log_syslog_enabled(bool is_tty);
+void log_syslog_open(const char *id, bool is_terminal);
+#endif
+
 #endif /* LOG_PRIVATE_H */
diff --git a/lib/log/log_syslog.c b/lib/log/log_syslog.c
new file mode 100644
index 0000000000..fb69921e05
--- /dev/null
+++ b/lib/log/log_syslog.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+#include "log_internal.h"
+#include "log_private.h"
+
+static enum {
+	LOG_SYSLOG_NONE = 0,	/* do not use syslog */
+	LOG_SYSLOG_AUTO,	/* use syslog only if not a terminal */
+	LOG_SYSLOG_ALWAYS,	/* always use syslog */
+	LOG_SYSLOG_BOTH,	/* log to both syslog and stderr */
+} log_syslog_opt;
+
+int
+eal_log_syslog(const char *str)
+{
+	if (str == NULL || strcmp(str, "auto") == 0)
+		log_syslog_opt = LOG_SYSLOG_AUTO;
+	else if (strcmp(str, "both") == 0)
+		log_syslog_opt = LOG_SYSLOG_BOTH;
+	else if (strcmp(str, "always") == 0)
+		log_syslog_opt = LOG_SYSLOG_ALWAYS;
+	else
+		return -1;
+	return 0;
+}
+
+bool
+log_syslog_enabled(bool is_terminal)
+{
+	switch (log_syslog_opt) {
+	default:
+		return false;
+
+	case LOG_SYSLOG_ALWAYS:
+	case LOG_SYSLOG_BOTH:
+		return true;
+
+	case LOG_SYSLOG_AUTO:
+		return !is_terminal;
+	}
+}
+
+/*
+ * When syslog is used, the log stream is redirected to a
+ * pseudo FILE handle that calls these functions.
+ */
+static ssize_t
+syslog_log_write(__rte_unused void *c, const char *buf, size_t size)
+{
+	/* Syslog error levels are from 0 to 7, so subtract 1 to convert */
+	syslog(rte_log_cur_msg_loglevel() - 1, "%.*s", (int)size, buf);
+	return size;
+}
+
+static int
+syslog_log_close(__rte_unused void *c)
+{
+	closelog();
+	return 0;
+}
+
+static cookie_io_functions_t syslog_log_func = {
+	.write = syslog_log_write,
+	.close = syslog_log_close,
+};
+
+void
+log_syslog_open(const char *id, bool is_terminal)
+{
+	int flags = LOG_NDELAY | LOG_PID;
+
+	if (log_syslog_opt == LOG_SYSLOG_BOTH)
+		flags |= LOG_PERROR;
+
+	openlog(id, flags, is_terminal ? LOG_USER : LOG_DAEMON);
+
+	/* redirect other log messages to syslog as well */
+	FILE *log_stream = fopencookie(NULL, "w", syslog_log_func);
+	if (log_stream != NULL)
+		rte_openlog_stream(log_stream);
+}
diff --git a/lib/log/meson.build b/lib/log/meson.build
index 04235f6ee5..4ac232786e 100644
--- a/lib/log/meson.build
+++ b/lib/log/meson.build
@@ -7,4 +7,8 @@ sources = files(
         'log_timestamp.c',
 )
 
+if not is_windows
+    sources += files('log_syslog.c')
+endif
+
 headers = files('rte_log.h')
diff --git a/lib/log/version.map b/lib/log/version.map
index 155f8b3bbe..8be6907840 100644
--- a/lib/log/version.map
+++ b/lib/log/version.map
@@ -29,7 +29,7 @@ INTERNAL {
 	eal_log_level2str;
 	eal_log_save_pattern;
 	eal_log_save_regexp;
-	eal_log_syslog;
+	eal_log_syslog; # WINDOWS_NO_EXPORT
 	eal_log_timestamp;
 	rte_eal_log_cleanup;
 };
-- 
2.45.2



More information about the dev mailing list