diff --git a/configure.ac b/configure.ac index b69ea2ea9..c15d7c6d7 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,17 @@ AC_SEARCH_LIBS([clock_gettime], [rt],,[AC_MSG_ERROR([cannot find clock_gettime() AC_SUBST([rt_LIBS],[$LIBS]) AX_RESTORE_FLAGS +USE_JOURNAL="no" +PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes],[have_systemd=no]) +if test "x$enable_systemd_journal" = "xyes" ; then + if test "x$have_systemd" = "xyes" ; then + AC_DEFINE_UNQUOTED(USE_JOURNAL, 1, [Use systemd journal logging]) + USE_JOURNAL="yes" + else + echo "systemd libraries not found, will just use syslog" + fi +fi + # look for testing harness "check" PKG_CHECK_MODULES([CHECK], [check >= 0.9.4],[with_check=yes],[with_check=no]) AM_CONDITIONAL(HAVE_CHECK, test "${with_check}" = "yes") @@ -538,6 +549,9 @@ AC_ARG_WITH([socket-dir], [ SOCKETDIR="$withval" ], [ SOCKETDIR="$localstatedir/run" ]) +AC_ARG_ENABLE([systemd-journal], + [AS_HELP_STRING([--enable-systemd-journal],[Allow use of systemd journal instead of syslog])]) + AC_ARG_WITH([force-sockets-config-file], [AS_HELP_STRING([--with-force-sockets-config-file=FILE],[config file to force IPC to use filesystem sockets (Linux & Cygwin only) @<:@SYSCONFDIR/libqb/force-filesystem-sockets@:>@])], [ FORCESOCKETSFILE="$withval" ], @@ -701,7 +715,6 @@ AC_SUBST([FORCESOCKETSFILE]) AC_DEFINE_UNQUOTED([SOCKETDIR], "$(eval echo ${SOCKETDIR})", [Socket directory]) AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory]) AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [quarterback built-in features]) - AC_DEFINE_UNQUOTED([FORCESOCKETSFILE], "$(eval echo ${FORCESOCKETSFILE})", [for sockets config file]) # version parsing (for qbconfig.h) @@ -763,6 +776,7 @@ AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ SOCKETDIR = ${SOCKETDIR}]) AC_MSG_RESULT([ Features = ${PACKAGE_FEATURES}]) +AC_MSG_RESULT([ Use systemd journal = ${USE_JOURNAL}]) AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE build info:]) AC_MSG_RESULT([ Optimization = ${OPT_CFLAGS}]) diff --git a/include/qb/qblog.h b/include/qb/qblog.h index 004bb4918..a2366fcb1 100644 --- a/include/qb/qblog.h +++ b/include/qb/qblog.h @@ -455,6 +455,7 @@ enum qb_log_conf { QB_LOG_CONF_IDENT, QB_LOG_CONF_MAX_LINE_LEN, QB_LOG_CONF_ELLIPSIS, + QB_LOG_CONF_USE_JOURNAL, }; enum qb_log_filter_type { diff --git a/lib/Makefile.am b/lib/Makefile.am index dbde4de74..3fc036b88 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -77,8 +77,8 @@ EXTRA_libqb_la_DEPENDENCIES = qblog_script.ld qblog_script.la EXTRA_DIST = qblog_script.ld.in qblog_script.la.in qblog_script_noop.ld libqb_la_SOURCES = $(source_to_lint) unix.c -libqb_la_CFLAGS = $(PTHREAD_CFLAGS) -libqb_la_LIBADD = $(LTLIBOBJS) $(dlopen_LIBS) $(PTHREAD_LIBS) $(socket_LIBS) $(rt_LIBS) +libqb_la_CFLAGS = $(PTHREAD_CFLAGS) $(SYSTEMD_CFLAGS) +libqb_la_LIBADD = $(LTLIBOBJS) $(dlopen_LIBS) $(PTHREAD_LIBS) $(socket_LIBS) $(rt_LIBS) $(SYSTEMD_LIBS) AM_LDFLAGS = $(LDFLAGS_COPY:-Bsymbolic-functions=) diff --git a/lib/libqb.pc.in b/lib/libqb.pc.in index a18685cba..3819b93fa 100644 --- a/lib/libqb.pc.in +++ b/lib/libqb.pc.in @@ -8,5 +8,5 @@ Version: @PACKAGE_VERSION@ Description: libqb Requires: Libs: -L${libdir} -lqb -Libs.private: @LIBS@ +Libs.private: @LIBS@ @SYSTEMD_LIBS@ Cflags: -I${includedir} diff --git a/lib/log.c b/lib/log.c index 2cdc106ba..952c42753 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1134,6 +1134,17 @@ qb_log_ctl2(int32_t t, enum qb_log_conf c, qb_log_ctl2_arg_t arg_not4directuse) case QB_LOG_CONF_ELLIPSIS: conf[t].ellipsis = arg_i32; break; + case QB_LOG_CONF_USE_JOURNAL: +#ifdef USE_JOURNAL + if (t == QB_LOG_SYSLOG) { + conf[t].use_journal = arg_i32; + } else { + rc = -EINVAL; + } +#else + rc = -EOPNOTSUPP; +#endif + break; default: rc = -EINVAL; diff --git a/lib/log_int.h b/lib/log_int.h index b460b1255..ef8811703 100644 --- a/lib/log_int.h +++ b/lib/log_int.h @@ -39,6 +39,7 @@ struct qb_log_target { int32_t file_sync; int32_t debug; int32_t extended; + int32_t use_journal; size_t size; size_t max_line_length; int32_t ellipsis; diff --git a/lib/log_syslog.c b/lib/log_syslog.c index cf86368d2..14f3f9e7a 100644 --- a/lib/log_syslog.c +++ b/lib/log_syslog.c @@ -23,6 +23,11 @@ #ifdef HAVE_SYSLOG_H #include #endif /* HAVE_SYSLOG_H */ +#ifdef USE_JOURNAL +#define SD_JOURNAL_SUPPRESS_LOCATION +#include +#endif + #include "log_int.h" static void @@ -57,7 +62,21 @@ _syslog_logger(int32_t target, if (final_priority < LOG_EMERG) { final_priority = LOG_EMERG; } - syslog(final_priority, "%s", output_buffer); +#ifdef USE_JOURNAL + if (t->use_journal) { + sd_journal_send("PRIORITY=%d", final_priority, + "CODE_LINE=%d", cs->lineno, + "CODE_FILE=%s", cs->filename, + "CODE_FUNC=%s", cs->function, + "SYSLOG_IDENTIFIER=%s", t->name, + "MESSAGE=%s", output_buffer, + NULL); + } else { +#endif + syslog(final_priority, "%s", output_buffer); +#ifdef USE_JOURNAL + } +#endif if (t->max_line_length > QB_LOG_MAX_LEN) { free(output_buffer); } @@ -66,15 +85,22 @@ _syslog_logger(int32_t target, static void _syslog_close(int32_t target) { - closelog(); + struct qb_log_target *t = qb_log_target_get(target); + + if (!t->use_journal) { + closelog(); + } } static void _syslog_reload(int32_t target) { struct qb_log_target *t = qb_log_target_get(target); - closelog(); - openlog(t->name, LOG_PID, t->facility); + + if (!t->use_journal) { + closelog(); + openlog(t->name, LOG_PID, t->facility); + } } int32_t @@ -84,6 +110,8 @@ qb_log_syslog_open(struct qb_log_target *t) t->reload = _syslog_reload; t->close = _syslog_close; - openlog(t->name, LOG_PID, t->facility); + if (!t->use_journal) { + openlog(t->name, LOG_PID, t->facility); + } return 0; } diff --git a/tests/check_log.c b/tests/check_log.c index 2713b8df0..a3f987606 100644 --- a/tests/check_log.c +++ b/tests/check_log.c @@ -30,6 +30,10 @@ #include #include +#ifdef USE_JOURNAL +#include +#endif + #include "_syslog_override.h" extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap); @@ -976,8 +980,55 @@ START_TEST(test_zero_tags) } END_TEST +#ifdef USE_JOURNAL +START_TEST(test_journal) +{ + int rc; + const char *msg; + size_t len; + pid_t log_pid; + sd_journal *jnl; + int count = 0; + + qb_log_init("check_log", LOG_USER, LOG_DEBUG); + qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE); + rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_USE_JOURNAL, 1); + ck_assert_int_eq(rc, 0); + qb_log(LOG_ERR, "Test message 1 from libqb"); + + qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); + rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_USE_JOURNAL, 1); + ck_assert_int_eq(rc, -EINVAL); + sleep(1); + + /* Check it reached the journal */ + rc = sd_journal_open(&jnl, 0); + ck_assert_int_eq(rc, 0); + rc = sd_journal_seek_tail(jnl); + ck_assert_int_eq(rc, 0); + SD_JOURNAL_FOREACH_BACKWARDS(jnl) { + rc = sd_journal_get_data(jnl, "_PID", (const void **)&msg, &len); + ck_assert_int_eq(rc, 0); + sscanf(msg, "_PID=%d", &log_pid); + fprintf(stderr, "PID message = '%s' - pid = %d (pid=%d, parent=%d)\n", msg, log_pid, getpid(), getppid()); + if (log_pid == getpid()) { + rc = sd_journal_get_data(jnl, "MESSAGE", (const void **)&msg, &len); + ck_assert_int_eq(rc, 0); + break; + } + if (++count > 20) { + break; + } + } + sd_journal_close(jnl); + ck_assert_int_lt(count, 20); +} +END_TEST +#else START_TEST(test_syslog) { + int rc; + qb_log_init("flip", LOG_USER, LOG_INFO); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE); @@ -989,9 +1040,14 @@ START_TEST(test_syslog) qb_log(LOG_ERR, "second as flop"); ck_assert_str_eq(_syslog_ident, "flop"); + /* This test only runs if USE_JOURNAL is undefined, so should always fail */ + rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_USE_JOURNAL, 1); + ck_assert_int_eq(rc, -EOPNOTSUPP); + qb_log_fini(); } END_TEST +#endif static Suite * log_suite(void) @@ -1015,7 +1071,15 @@ log_suite(void) #endif add_tcase(s, tc, test_extended_information); add_tcase(s, tc, test_zero_tags); +/* + * You can still use syslog and journal in a normal application, + * but the syslog_override code doesn't work when -lsystemd is present + */ +#ifdef USE_JOURNAL + add_tcase(s, tc, test_journal); +#else add_tcase(s, tc, test_syslog); +#endif return s; }