The journal is a component of systemd, that captures Syslog messages, Kernel log messages, initial RAM disk and early boot messages as well as messages written to STDOUT/STDERR of all services, indexes them and makes this available to the user. It can be used in parallel, or in place of a traditional syslog daemon, such as rsyslog or syslog-ng. The journal offers a couple of improvements over traditional logging facilities (e.g. advanced filtering capabilities). This patch adds support for logging directly to the journal using its native API. --- Changes for v2: - if systemd journal support is enabled the journal is the default log target - changed the configure option name: journal -> systemd-journal - fixed spelling error - proper return value checking of sd_journal_send() configure.ac | 18 ++++++++++ man/pulse-daemon.conf.5.xml.in | 11 +++--- man/pulseaudio.1.xml.in | 5 +-- src/Makefile.am | 5 +++ src/daemon/cmdline.c | 4 +++ src/daemon/main.c | 4 +++ src/pulsecore/log.c | 76 +++++++++++++++++++++++++++++++++++++----- src/pulsecore/log.h | 3 ++ 8 files changed, 110 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index 72e7695..d4b4817 100644 --- a/configure.ac +++ b/configure.ac @@ -1160,6 +1160,22 @@ AC_SUBST(HAVE_SYSTEMD) AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$HAVE_SYSTEMD" = x1]) AS_IF([test "x$HAVE_SYSTEMD" = "x1"], AC_DEFINE([HAVE_SYSTEMD], 1, [Have SYSTEMD?])) +#### journal support (optional) #### + +AC_ARG_ENABLE([systemd-journal], + AS_HELP_STRING([--disable-systemd-journal],[Disable optional systemd journal support])) + +AS_IF([test "x$enable_systemd_journal" != "xno"], + [PKG_CHECK_MODULES(JOURNAL, [ libsystemd-journal ], HAVE_SYSTEMD_JOURNAL=1, HAVE_SYSTEMD_JOURNAL=0)], + HAVE_SYSTEMD_JOURNAL=0) + +AS_IF([test "x$enable_systemd_journal" = "xyes" && test "x$HAVE_SYSTEMD_JOURNAL" = "x0"], + [AC_MSG_ERROR([*** Needed systemd journal support not found])]) + +AC_SUBST(HAVE_SYSTEMD_JOURNAL) +AM_CONDITIONAL([HAVE_SYSTEMD_JOURNAL], [test "x$HAVE_SYSTEMD_JOURNAL" = x1]) +AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], AC_DEFINE([HAVE_JOURNAL], 1, [Have JOURNAL?])) + #### Build and Install man pages #### AC_ARG_ENABLE([manpages], @@ -1405,6 +1421,7 @@ AS_IF([test "x$HAVE_XEN" = "x1"], ENABLE_XEN=yes, ENABLE_XEN=no) AS_IF([test "x$HAVE_DBUS" = "x1"], ENABLE_DBUS=yes, ENABLE_DBUS=no) AS_IF([test "x$HAVE_UDEV" = "x1"], ENABLE_UDEV=yes, ENABLE_UDEV=no) AS_IF([test "x$HAVE_SYSTEMD" = "x1"], ENABLE_SYSTEMD=yes, ENABLE_SYSTEMD=no) +AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no) AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no) AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no) AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no) @@ -1463,6 +1480,7 @@ echo " Enable udev: ${ENABLE_UDEV} Enable HAL->udev compat: ${ENABLE_HAL_COMPAT} Enable systemd login: ${ENABLE_SYSTEMD} + Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL} Enable TCP Wrappers: ${ENABLE_TCPWRAP} Enable libsamplerate: ${ENABLE_LIBSAMPLERATE} Enable IPv6: ${ENABLE_IPV6} diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in index c40d570..9596738 100644 --- a/man/pulse-daemon.conf.5.xml.in +++ b/man/pulse-daemon.conf.5.xml.in @@ -297,11 +297,12 @@ USA. <option> <p><opt>log-target=</opt> The default log target. Use either - <opt>stderr</opt>, <opt>syslog</opt>, <opt>auto</opt>, - <opt>file:PATH</opt> or <opt>newfile:PATH</opt>. <opt>auto</opt> is - equivalent to <opt>sylog</opt> in case <opt>daemonize</opt> is enabled, - otherwise to <opt>stderr</opt>. If set to <opt>file:PATH</opt>, logging - is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is + <opt>stderr</opt>, <opt>syslog</opt>, <opt>journal</opt> (optional), + <opt>auto</opt>, <opt>file:PATH</opt> or <opt>newfile:PATH</opt>. On traditional + systems <opt>auto</opt> is equivalent to <opt>syslog</opt>. On systemd-enabled + systems, auto is equivalent to <opt>journal</opt>, in case <opt>daemonize</opt> + is enabled, and to <opt>stderr</opt> otherwise. If set to <opt>file:PATH</opt>, + logging is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is otherwise the same as <opt>file:PATH</opt>, but existing files are never overwritten. If the specified file already exists, a suffix is added to the file name to avoid overwriting. Defaults to <opt>auto</opt>. The diff --git a/man/pulseaudio.1.xml.in b/man/pulseaudio.1.xml.in index d7d7458..114cd96 100644 --- a/man/pulseaudio.1.xml.in +++ b/man/pulseaudio.1.xml.in @@ -217,12 +217,13 @@ USA. </option> <option> - <p><opt>--log-target</opt><arg>={auto,syslog,stderr,file:PATH,newfile:PATH}</arg></p> + <p><opt>--log-target</opt><arg>={auto,syslog,journal,stderr,file:PATH,newfile:PATH}</arg></p> <optdesc><p>Specify the log target. If set to <arg>auto</arg> (which is the default), then logging is directed to syslog when <opt>--daemonize</opt> is passed, otherwise to - STDERR. If set to <arg>file:PATH</arg>, logging is directed to + STDERR. If set to <arg>journal</arg> logging is directed to the systemd + journal. If set to <arg>file:PATH</arg>, logging is directed to the file indicated by PATH. <arg>newfile:PATH</arg> is otherwise the same as file:PATH, but existing files are never overwritten. If the specified file already exists, a suffix is added to the diff --git a/src/Makefile.am b/src/Makefile.am index 0296b3c..c89cd6d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -687,6 +687,11 @@ libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS) libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS) endif +if HAVE_SYSTEMD_JOURNAL +libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(JOURNAL_FLAGS) +libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS += $(JOURNAL_LIBS) +endif + # proplist-util.h uses these header files, but not the library itself! libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS += $(GLIB20_CFLAGS) $(GTK30_CFLAGS) diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index 6361a3d..0fa8492 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -323,7 +323,11 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d case ARG_LOG_TARGET: if (pa_daemon_conf_set_log_target(conf, optarg) < 0) { +#ifdef HAVE_JOURNAL + pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'.")); +#else pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'.")); +#endif goto fail; } break; diff --git a/src/daemon/main.c b/src/daemon/main.c index 58cacf8..e01371e 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -817,7 +817,11 @@ int main(int argc, char *argv[]) { #endif if (!conf->log_target) { +#ifdef HAVE_JOURNAL + pa_log_target target = { .type = PA_LOG_JOURNAL, .file = NULL }; +#else pa_log_target target = { .type = PA_LOG_SYSLOG, .file = NULL }; +#endif pa_log_set_target(&target); } diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index 5535045..cf96dce 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -40,6 +40,10 @@ #include <syslog.h> #endif +#ifdef HAVE_JOURNAL +#include <systemd/sd-journal.h> +#endif + #include <pulse/gccmacro.h> #include <pulse/rtclock.h> #include <pulse/utf8.h> @@ -90,6 +94,18 @@ static const int level_to_syslog[] = { }; #endif +/* These are actually equivalent to the syslog ones + * but we don't want to depend on syslog.h */ +#ifdef HAVE_JOURNAL +static const int level_to_journal[] = { + [PA_LOG_ERROR] = 3, + [PA_LOG_WARN] = 4, + [PA_LOG_NOTICE] = 5, + [PA_LOG_INFO] = 6, + [PA_LOG_DEBUG] = 7 +}; +#endif + static const char level_to_char[] = { [PA_LOG_ERROR] = 'E', [PA_LOG_WARN] = 'W', @@ -129,6 +145,9 @@ int pa_log_set_target(pa_log_target *t) { switch (t->type) { case PA_LOG_STDERR: case PA_LOG_SYSLOG: +#ifdef HAVE_JOURNAL + case PA_LOG_JOURNAL: +#endif case PA_LOG_NULL: break; case PA_LOG_FILE: @@ -318,6 +337,20 @@ static void init_defaults(void) { } PA_ONCE_END; } +#ifdef HAVE_SYSLOG_H +static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) { + char *local_t; + + openlog(ident, LOG_PID, LOG_USER); + + if ((local_t = pa_utf8_to_locale(t))) + t = local_t; + + syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); + pa_xfree(local_t); +} +#endif + void pa_log_levelv_meta( pa_log_level_t level, const char*file, @@ -450,19 +483,35 @@ void pa_log_levelv_meta( } #ifdef HAVE_SYSLOG_H - case PA_LOG_SYSLOG: { - char *local_t; - - openlog(ident, LOG_PID, LOG_USER); + case PA_LOG_SYSLOG: + log_syslog(level, t, timestamp, location, bt); + break; +#endif - if ((local_t = pa_utf8_to_locale(t))) - t = local_t; +#ifdef HAVE_JOURNAL + case PA_LOG_JOURNAL: + if (sd_journal_send("MESSAGE=%s", t, + "PRIORITY=%i", level_to_journal[level], + "CODE_FILE=%s", file, + "CODE_FUNC=%s", func, + "CODE_LINE=%d", line, + NULL) < 0) { +#ifdef HAVE_SYSLOG_H + pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL }; - syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); - pa_xfree(local_t); + syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__, + "Error writing logs to the journal. Redirect log messages to syslog."); + log_syslog(level, t, timestamp, location, bt); +#else + pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL }; + saved_errno = errno; + fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console."); + fprintf(stderr, "%s %s\n", metadata, t); +#endif + pa_log_set_target(&new_target); + } break; - } #endif case PA_LOG_FILE: @@ -570,6 +619,10 @@ pa_log_target *pa_log_parse_target(const char *string) { t = pa_log_target_new(PA_LOG_STDERR, NULL); else if (pa_streq(string, "syslog")) t = pa_log_target_new(PA_LOG_SYSLOG, NULL); +#ifdef HAVE_JOURNAL + else if (pa_streq(string, "journal")) + t = pa_log_target_new(PA_LOG_JOURNAL, NULL); +#endif else if (pa_streq(string, "null")) t = pa_log_target_new(PA_LOG_NULL, NULL); else if (pa_startswith(string, "file:")) @@ -594,6 +647,11 @@ char *pa_log_target_to_string(const pa_log_target *t) { case PA_LOG_SYSLOG: string = pa_xstrdup("syslog"); break; +#ifdef HAVE_JOURNAL + case PA_LOG_JOURNAL: + string = pa_xstrdup("journal"); + break; +#endif case PA_LOG_NULL: string = pa_xstrdup("null"); break; diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index e7dca88..5e9611d 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -35,6 +35,9 @@ typedef enum pa_log_target_type { PA_LOG_STDERR, /* default */ PA_LOG_SYSLOG, +#ifdef HAVE_JOURNAL + PA_LOG_JOURNAL, /* systemd journal */ +#endif PA_LOG_NULL, /* to /dev/null */ PA_LOG_FILE, /* to a user specified file */ PA_LOG_NEWFILE, /* with an automatic suffix to avoid overwriting anything */ -- 1.8.4.2