diff --git a/doc/man3/flux_log.adoc b/doc/man3/flux_log.adoc index bbc51aab0535..abc20ff19bce 100644 --- a/doc/man3/flux_log.adoc +++ b/doc/man3/flux_log.adoc @@ -23,8 +23,9 @@ void flux_log_set_procid (flux_t *h, const char *s); DESCRIPTION ----------- -`flux_log()` creates RFC 5424 format log messages and sends them -to the Flux message broker on 'h' for handling. +`flux_log()` creates RFC 5424 format log messages. The log messages +are sent to the Flux message broker on 'h' for handling if it is +specified. If 'h' is NULL, the log message is output to stderr. The 'level' parameter should be set to one of the syslog(3) severity levels, which are, in order of decreasing importance: @@ -43,9 +44,9 @@ the 'level' to direct `flux_log()` to wait for a response from the broker indicating whether the log request was accepted or not. Otherwise, log requests are "fire and forget". -Log messages are are added to the broker's circular buffer which -can be accessed with flux-dmesg(3). From there, a message's disposition -is up to the broker's log configuration. +When 'h' is specified, log messages are are added to the broker's +circular buffer which can be accessed with flux-dmesg(3). From there, +a message's disposition is up to the broker's log configuration. `flux_log_set_procid()` may be used to override the default procid, which is initialized to the calling process's PID. @@ -61,8 +62,9 @@ A Flux log message is formatted as a Flux request with a "raw" payload, as defined by Flux RFC 3. The raw payload is formatted according to Internet RFC 5424. -The following Syslog header fields are set in a Flux log messages when it is -created within `flux_log()`: +If the Flux handle 'h' is specified, the following Syslog header +fields are set in a Flux log messages when it is created within +`flux_log()`: PRI:: Set to the user-specified severity level combined with the facility, diff --git a/src/common/libflux/flog.c b/src/common/libflux/flog.c index 8eae5cbc7835..4dba9d62df69 100644 --- a/src/common/libflux/flog.c +++ b/src/common/libflux/flog.c @@ -134,7 +134,7 @@ static int log_rpc (flux_t *h, const char *buf, int len, int flags) int flux_vlog (flux_t *h, int level, const char *fmt, va_list ap) { - logctx_t *ctx = getctx (h); + logctx_t *ctx; int saved_errno = errno; uint32_t rank; int len; @@ -143,7 +143,15 @@ int flux_vlog (flux_t *h, int level, const char *fmt, va_list ap) struct stdlog_header hdr; int rpc_flags = FLUX_RPC_NORESPONSE; - if (!ctx) { + if (!h) { + char buf[FLUX_MAX_LOGBUF + 1]; + const char *lstr = stdlog_severity_to_string (LOG_PRI (level)); + + (void)vsnprintf (buf, sizeof (buf), fmt, ap); + return fprintf (stderr, "%s: %s\n", lstr, buf); + } + + if (!(ctx = getctx (h))) { errno = ENOMEM; goto fatal; } diff --git a/src/common/libflux/flog.h b/src/common/libflux/flog.h index 284a21e0d2a8..60943b65b78d 100644 --- a/src/common/libflux/flog.h +++ b/src/common/libflux/flog.h @@ -29,6 +29,8 @@ void flux_log_set_appname (flux_t *h, const char *s); void flux_log_set_procid (flux_t *h, const char *s); /* Log a message at the specified level, as defined for syslog(3). + * + * Flux handle is optional, if set to NULL output to stderr. */ int flux_vlog (flux_t *h, int level, const char *fmt, va_list ap); int flux_log (flux_t *h, int level, const char *fmt, ...) @@ -37,6 +39,8 @@ int flux_log (flux_t *h, int level, const char *fmt, ...) /* Log a message at LOG_ERR level, appending a colon, space, and error string. * The system 'errno' is assumed to be valid and contain an error code * that can be decoded with zmq_strerror(3). + * + * Flux handle is optional, if set to NULL output to stderr. */ void flux_log_verror (flux_t *h, const char *fmt, va_list ap); void flux_log_error (flux_t *h, const char *fmt, ...) diff --git a/t/Makefile.am b/t/Makefile.am index 8a8bb046ce39..f1085aec7274 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -164,6 +164,7 @@ check_PROGRAMS = \ loop/reactor.t \ loop/reduce.t \ loop/log.t \ + loop/logstderr \ rpc/rpc.t \ rpc/mrpc.t \ rolemask/loop.t \ @@ -237,6 +238,11 @@ loop_log_t_SOURCES = loop/log.c loop_log_t_CPPFLAGS = $(test_cppflags) loop_log_t_LDADD = $(test_ldadd) $(LIBDL) +loop_logstderr_SOURCES = loop/logstderr.c +loop_logstderr_CPPFLAGS = $(test_cppflags) +loop_logstderr_LDADD = \ + $(test_ldadd) $(LIBDL) $(LIBUTIL) + loop_reactor_t_SOURCES = loop/reactor.c loop_reactor_t_CPPFLAGS = $(test_cppflags) loop_reactor_t_LDADD = $(test_ldadd) $(LIBDL) diff --git a/t/loop/logstderr.c b/t/loop/logstderr.c new file mode 100644 index 000000000000..a0d96a9f6420 --- /dev/null +++ b/t/loop/logstderr.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int main (int argc, char *argv[]) +{ + errno = EPERM; + flux_log (NULL, LOG_WARNING, "hello"); + + errno = ENOENT; + flux_log_error (NULL, "world"); + + return (0); +} + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ + diff --git a/t/t0001-basic.t b/t/t0001-basic.t index c9ef42d74695..7c8ef689ecfc 100755 --- a/t/t0001-basic.t +++ b/t/t0001-basic.t @@ -355,4 +355,11 @@ test_expect_success 'instance can stop cleanly with subscribers (#1025)' ' flux start ${ARGS} -s2 --bootstrap=selfpmi bash -c "nohup flux event sub hb &" ' +# test for issue #1191 +test_expect_success 'passing NULL to flux_log functions logs to stderr (#1191)' ' + ${FLUX_BUILD_DIR}/t/loop/logstderr > std.out 2> std.err && + grep "warning: hello" std.err && + grep "err: world: No such file or directory" std.err +' + test_done