From df371e0a77e5b30f5ebddd0902fc8dd46b349849 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sat, 13 Apr 2024 10:59:05 -0700 Subject: fixes #543 Add logging support framework --- docs/man/CMakeLists.txt | 4 + docs/man/libnng.3.adoc | 10 ++ docs/man/nng_log.3.adoc | 62 ++++++++ docs/man/nng_log_set_facility.3.adoc | 47 ++++++ docs/man/nng_log_set_level.adoc | 42 ++++++ docs/man/nng_log_set_logger.adoc | 52 +++++++ include/nng/nng.h | 89 ++++++++++- src/core/CMakeLists.txt | 3 + src/core/log.c | 284 +++++++++++++++++++++++++++++++++++ src/core/log_test.c | 188 +++++++++++++++++++++++ 10 files changed, 779 insertions(+), 2 deletions(-) create mode 100644 docs/man/nng_log.3.adoc create mode 100644 docs/man/nng_log_set_facility.3.adoc create mode 100644 docs/man/nng_log_set_level.adoc create mode 100644 docs/man/nng_log_set_logger.adoc create mode 100644 src/core/log.c create mode 100644 src/core/log_test.c diff --git a/docs/man/CMakeLists.txt b/docs/man/CMakeLists.txt index 9e3e8128..ba3508d2 100644 --- a/docs/man/CMakeLists.txt +++ b/docs/man/CMakeLists.txt @@ -115,6 +115,10 @@ if (NNG_ENABLE_DOC) nng_listener_set nng_listener_setopt nng_listener_start + nng_log + nng_log_set_facility + nng_log_set_loevel + nng_log_set_logger nng_msg_alloc nng_msg_append nng_msg_body diff --git a/docs/man/libnng.3.adoc b/docs/man/libnng.3.adoc index 7153b14f..3e2900aa 100644 --- a/docs/man/libnng.3.adoc +++ b/docs/man/libnng.3.adoc @@ -283,6 +283,16 @@ universal resource locators (URLS). |xref:nng_url_parse.3.adoc[nng_url_parse()]|create URL structure from string |=== +=== Logging Support + +Common functionality for message logging. + +|=== +|xref:nng_log.3.adoc[nng_log()]|log a message +|xref:nng_log_facility.3.adoc[nng_log_set_facility()]|set log facility +|xref:nng_log_level.3.adoc[nng_log_set_level()]|set log level +|xref:nng_log_logger.3.adoc[nng_log_set_logger()]|set logging handler +|=== === Supplemental API diff --git a/docs/man/nng_log.3.adoc b/docs/man/nng_log.3.adoc new file mode 100644 index 00000000..852f765b --- /dev/null +++ b/docs/man/nng_log.3.adoc @@ -0,0 +1,62 @@ += nng_log(3) +// +// Copyright 2024 Staysail Systems, Inc. +// +// This document is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +== NAME + +nng_log - log messages + +== SYNOPSIS + +[source, c] +---- +#include + +void nng_log_err(const char *msgid, const char *msg, ...); +void nng_log_warn(const char *msgid, const char *msg, ...); +void nng_log_notice(const char *msgid, const char *msg, ...); +void nng_log_info(const char *msgid, const char *msg, ...); +void nng_log_debug(const char *msgid, const char *msg, ...); + +void nng_log_auth(nng_log_level level, const char *msgid, const char *msg, ...); +---- + +== DESCRIPTION + +These functions are used to post a message to system or application logs. + +The first five forms all post a message at the severity indicated by the function name. +The _msgid_ should be a short message identifier that should indicate the message in question. +A `NULL` value for _msgid_ canbe used as well. + +Message identifiers can be used to assist in filtering logs. +These should uniquely identify the nature of the problem, whe possible, to assist in trouble-shooting. +They should also be short. +Eight characters or less is ideal, and more than sixteen is strongly discouraged. + +The message is formatting as if by `sprintf`, using `msg` as the format, and remaining arguments as arguments to the format. + +The final function, `nng_log_auth`, is used for posting authentication related messages which might be treated specially, such as be storing them in a separate secured log file. +It takes the severity as a level in _level_. +The severity can be one of the following values: + +* `NNG_LOG_ERR` +* `NNG_LOG_WARN` +* `NNG_LOG_NOTICE` +* `NNG_LOG_INFO` +* `NNG_LOG_DEBUG` + +The message itself is handled according to the logging facility set up with xref:nng_mg_set_logger.3.adoc[`nng_log_set_logger`]. +Message delivery is best effort, and messages may be suppressed based on the priority set with xref:nng_log_set_level.3.adoc[`nng_log_set_level`]. + +== SEE ALSO + +xref:nng_log_set_facility.3.adoc[nng_log_set_facility(3)], +xref:nng_log_set_level.3.adoc[nng_log_set_level(3)], +xref:nng_log_set_logger.3.adoc[nng_log_set_logger(3)] diff --git a/docs/man/nng_log_set_facility.3.adoc b/docs/man/nng_log_set_facility.3.adoc new file mode 100644 index 00000000..ecd6ab76 --- /dev/null +++ b/docs/man/nng_log_set_facility.3.adoc @@ -0,0 +1,47 @@ += nng_log_set_facility(3) +// +// Copyright 2024 Staysail Systems, Inc. +// +// This document is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +== NAME + +nng_log_set_facility - set facility used for log messages + +== SYNOPSIS + +[source, c] +---- +#include + +typedef enum nng_log_facility { + NNG_LOG_USER = 1, + NNG_LOG_DAEMON = 3, + NNG_LOG_AUTH = 10, + NNG_LOG_LOCAL0 = 16, + NNG_LOG_LOCAL1 = 17, + NNG_LOG_LOCAL2 = 18, + NNG_LOG_LOCAL3 = 19, + NNG_LOG_LOCAL4 = 20, + NNG_LOG_LOCAL5 = 21, + NNG_LOG_LOCAL6 = 22, + NNG_LOG_LOCAL7 = 23, +} nng_log_facility; + +void nng_log_set_facility(nng_log_facility facility); +---- + +== DESCRIPTION + +The `nng_log_set_facility` is used to set the _facility_ of the application posting logs. +This is used to assist with directing log content when handled by services such as `syslog`. + +Note that while the log levels used here overlap with common levels used by the syslog facility on POSIX systems, applications should not rely on this. + +== SEE ALSO + +xref:nng_log_set_level.3.adoc[nng_log_set_level(3)] diff --git a/docs/man/nng_log_set_level.adoc b/docs/man/nng_log_set_level.adoc new file mode 100644 index 00000000..012f93f8 --- /dev/null +++ b/docs/man/nng_log_set_level.adoc @@ -0,0 +1,42 @@ += nng_log_set_level(3) +// +// Copyright 2024 Staysail Systems, Inc. +// +// This document is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +== NAME + +nng_log_set_level - set minimum level for logging messagse + +== SYNOPSIS + +[source, c] +---- +#include + +typedef enum nng_log_level { + NNG_LOG_NONE = 0, // used for filters only, NNG suppresses these + NNG_LOG_ERR = 3, + NNG_LOG_WARN = 4, + NNG_LOG_NOTICE = 5, + NNG_LOG_INFO = 6, + NNG_LOG_DEBUG = 7 +} nng_log_level; + +void nng_log_set_level(nng_log_level level); +---- + +== DESCRIPTION + +The `nng_log_set_level` function is used to set the minimum severity to _level_ for processing log messages. +Any messages with a less severe rating are not processed and simply are discarded. +Use `NNG_LOG_NONE` to suppress all log messages. +Use `NNG_LOG_DEBUG` to receive all log messages. + +== SEE ALSO + +xref:nng_log_set_facility.3.adoc[nng_log_set_facility(3)] diff --git a/docs/man/nng_log_set_logger.adoc b/docs/man/nng_log_set_logger.adoc new file mode 100644 index 00000000..4f3cafc8 --- /dev/null +++ b/docs/man/nng_log_set_logger.adoc @@ -0,0 +1,52 @@ += nng_log_set_logger(3) +// +// Copyright 2024 Staysail Systems, Inc. +// +// This document is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +== NAME + +nng_log_set_logger - set logging handler + +== SYNOPSIS + +[source, c] +---- +#include + +typedef void (*nng_logger)(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg); + +void nng_null_logger(nng_log_level, nng_log_facility, const char *, const char *); +void nng_stderr_logger(nng_log_level, nng_log_facility, const char *, const char *); +void nng_system_logger(nng_log_level, nng_log_facility, const char *, const char *); + +void nng_log_set_logger(nng_logger logger); +---- + +== DESCRIPTION + +The `nng_log_set_logger` is used to set the base logging function to _logger_. +The _logger_ may be a user defined function to process log messages. +Only a single logger may be registered at a time. +If needed, the logger should make copies of either _msgid_ or _msg_, as those may not be valid once the logger function returns. + +The `nng_null_logger` function is an implementation of `nng_logger` that simply discards the content. + +The `nng_stderr_logger` function is an implementation that logs messages to the standard error stream. +It will attempt to colorize messages by the severity, if the standard error is a terminal device. +This can be supressed by setting either the `NO_COLOR` or `NNG_LOG_NO_COLOR` environment variables. + +The `nng_system_logger` attempts to use an appropriate system facility to log messages. +For POSIX systems, this means using `syslog` to process the messages. +For other the `nng_stderr_log` may be used as a fallback. + +== SEE ALSO + +xref:nng_log_set_facility.3.adoc[nng_log_set_facility(3)], +xref:nng_log_set_level.3.adoc[nng_log_set_level(3)], +xref:nng_log.3.adoc[nng_log(3)] diff --git a/include/nng/nng.h b/include/nng/nng.h index babb2eb8..7b196e66 100644 --- a/include/nng/nng.h +++ b/include/nng/nng.h @@ -72,7 +72,7 @@ extern "C" { // NNG_PROTOCOL_NUMBER is used by protocol headers to calculate their // protocol number from a major and minor number. Applications should // probably not need to use this. -#define NNG_PROTOCOL_NUMBER(maj, min) (((x) * 16) + (y)) +#define NNG_PROTOCOL_NUMBER(maj, min) (((x) *16) + (y)) // Types common to nng. @@ -617,7 +617,7 @@ NNG_DECL void nng_aio_finish(nng_aio *, int); // final argument is passed to the cancelfn. The final argument of the // cancellation function is the error number (will not be zero) corresponding // to the reason for cancellation, e.g. NNG_ETIMEDOUT or NNG_ECANCELED. -typedef void (*nng_aio_cancelfn)(nng_aio *, void *, int); +typedef void (*nng_aio_cancelfn)(nng_aio *, void *, int); NNG_DECL void nng_aio_defer(nng_aio *, nng_aio_cancelfn, void *); // nng_aio_sleep does a "sleeping" operation, basically does nothing @@ -1466,6 +1466,91 @@ enum { NNG_INIT_MAX_POLLER_THREADS, }; +// Logging support. + +// Log levels. These correspond to RFC 5424 (syslog) levels. +// NNG never only uses priorities 3 - 7. +// +// Note that LOG_EMER is 0, but we don't let applications submit' +// such messages, so this is a useful value to prevent logging altogether. +typedef enum nng_log_level { + NNG_LOG_NONE = 0, // used for filters only, NNG suppresses these + NNG_LOG_ERR = 3, + NNG_LOG_WARN = 4, + NNG_LOG_NOTICE = 5, + NNG_LOG_INFO = 6, + NNG_LOG_DEBUG = 7 +} nng_log_level; + +// Facilities. Also from RFC 5424. +// Not all values are enumerated here. Values not enumerated here +// should be assumed reserved for system use, and not available for +// NNG or general applications. +typedef enum nng_log_facility { + NNG_LOG_USER = 1, + NNG_LOG_DAEMON = 3, + NNG_LOG_AUTH = 10, // actually AUTHPRIV, for sensitive logs + NNG_LOG_LOCAL0 = 16, + NNG_LOG_LOCAL1 = 17, + NNG_LOG_LOCAL2 = 18, + NNG_LOG_LOCAL3 = 19, + NNG_LOG_LOCAL4 = 20, + NNG_LOG_LOCAL5 = 21, + NNG_LOG_LOCAL6 = 22, + NNG_LOG_LOCAL7 = 23, +} nng_log_facility; + +// Logging function, which may be supplied by application code. Only +// one logging function may be registered. The level and facility are +// as above. The message ID is chosen by the submitter - internal NNG +// messages will have MSGIDs starting with "NNG-". The MSGID should be +// not more than 8 characters, though this is not a hard requirement. +// Loggers are required ot make a copy of the msgid and message if required, +// because the values will not be valid once the logger returns. +typedef void (*nng_logger)(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg); + +// Discard logger, simply throws logs away. +extern void nng_null_logger( + nng_log_level, nng_log_facility, const char *, const char *); + +// Very simple, prints formatted messages to stderr. +extern void nng_stderr_logger( + nng_log_level, nng_log_facility, const char *, const char *); + +// Performs an appropriate logging function for the system. On +// POSIX systems it uses syslog(3). Details vary by system, and the +// logging may be influenced by other APIs not provided by NNG, such as +// openlog() for POSIX systems. This may be nng_stderr_logger on +// other systems. +extern void nng_system_logger( + nng_log_level, nng_log_facility, const char *, const char *); + +// Set the default facility to use when logging. NNG uses NNG_LOG_USER by +// default. +extern void nng_log_set_facility(nng_log_facility facility); + +// Set the default logging level. Use NNG_LOG_DEBUG to get everything. +// Use NNG_LOG_NONE to prevent logging altogether. Logs that are less +// severe (numeric level is higher) will be discarded. +extern void nng_log_set_level(nng_log_level level); + +// Register a logger. +extern void nng_log_set_logger(nng_logger logger); + +// Log a message. The msg is formatted using following arguments as per +// sprintf. The msgid may be NULL. +extern void nng_log_err(const char *msgid, const char *msg, ...); +extern void nng_log_warn(const char *msgid, const char *msg, ...); +extern void nng_log_notice(const char *msgid, const char *msg, ...); +extern void nng_log_info(const char *msgid, const char *msg, ...); +extern void nng_log_debug(const char *msgid, const char *msg, ...); + +// Log an authentication related message. These will use the NNG_LOG_AUTH +// facility. +extern void nng_log_auth( + nng_log_level level, const char *msgid, const char *msg, ...); + #ifdef __cplusplus } #endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 009d6bb0..48b25265 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -14,6 +14,7 @@ nng_check_sym(strlcpy string.h NNG_HAVE_STRLCPY) nng_check_sym(strnlen string.h NNG_HAVE_STRNLEN) nng_check_sym(strcasecmp string.h NNG_HAVE_STRCASECMP) nng_check_sym(strncasecmp string.h NNG_HAVE_STRNCASECMP) +nng_check_sym(localtime_r time.h NNG_HAVE_LOCALTIME_R) nng_sources( defs.h @@ -38,6 +39,7 @@ nng_sources( listener.h lmq.c lmq.h + log.c message.c message.h msgqueue.c @@ -80,6 +82,7 @@ nng_test(errors_test) nng_test(id_test) nng_test(init_test) nng_test(list_test) +nng_test(log_test) nng_test(message_test) nng_test(reconnect_test) nng_test(sock_test) diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 00000000..b560f6c7 --- /dev/null +++ b/src/core/log.c @@ -0,0 +1,284 @@ + +// Copyright 2024 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "nng/nng.h" +#include "nng_impl.h" +#include +#include +#include +#ifdef NNG_PLATFORM_WINDOWS +#include +#endif +#ifdef NNG_PLATFORM_POSIX +#include +#include +#endif +#include + +static nng_log_level log_level = NNG_LOG_NOTICE; +static nng_log_facility log_facility = NNG_LOG_USER; +static nng_logger log_logger = nng_null_logger; + +void +nng_log_set_facility(nng_log_facility facility) +{ + log_facility = facility; +} + +void +nng_log_set_level(nng_log_level level) +{ + log_level = level; +} + +void +nng_log_set_logger(nng_logger logger) +{ + if (logger == NULL) { + logger = nng_null_logger; + } + log_logger = logger; +} + +void +nng_null_logger(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg) +{ + NNI_ARG_UNUSED(level); + NNI_ARG_UNUSED(facility); + NNI_ARG_UNUSED(msgid); + NNI_ARG_UNUSED(msg); + return; +} + +void +nng_stderr_logger(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg) +{ + const char *sgr, *sgr0; + // Initial implementation. + bool colors = false; + const char *level_str; + time_t now; + char when[64]; + NNI_ARG_UNUSED(facility); + +#ifdef NNG_PLATFORM_WINDOWS + // NB: We are blithely assuming the user has a modern console. + colors = _isatty(_fileno(stderr)); +#elif defined(NNG_PLATFORM_POSIX) + // Only assuming we can use colors (and attributes) if stderr is a tty + // and $TERM is reasonable. We assume the terminal supports ECMA-48, + // which is true on every reasonable system these days. + colors = isatty(fileno(stderr)) && (getenv("TERM") != NULL) && + (getenv("TERM")[0] != 0); +#else + now = 0; + colors = false; +#endif + + // Escape hatch to prevent colorizing logs if we have to. Users on + // legacy Windows can set this, or on ancient HP terminals or + // something. Also in the same way that no-color.org proposes. + // The reason for both is to allow suppression *only* for NNG. There + // is no good reason to enable it to override the presence of NO_COLOR. + if ((getenv("NNG_LOG_NO_COLOR") != NULL) || + (getenv("NO_COLOR") != NULL)) { + colors = false; + } + now = time(NULL); +#ifdef NNG_HAVE_LOCALTIME_R + struct tm tm; + // No timezone offset, not strictly ISO8601 compliant + strftime(when, sizeof(when), "%Y-%m-%d %T", localtime_r(&now, &tm)); +#else + strftime(when, sizeof(when), "%Y-%m-%d %T", localtime(&now)); +#endif + + switch (level) { + case NNG_LOG_ERR: + sgr = "\x1b[31m"; // red + sgr0 = "\x1b[0m"; + level_str = "ERROR"; + break; + case NNG_LOG_WARN: + sgr = "\x1b[33m"; // yellow + sgr0 = "\x1b[0m"; + level_str = "WARN"; + break; + case NNG_LOG_NOTICE: + sgr = "\x1b[1m"; // bold + sgr0 = "\x1b[0m"; + level_str = "NOTICE"; + break; + case NNG_LOG_DEBUG: + sgr = "\x1b[36m"; // cyan + sgr0 = "\x1b[0m"; + level_str = "DEBUG"; + break; + case NNG_LOG_INFO: + sgr = ""; + sgr0 = ""; + level_str = "INFO"; + break; + default: + sgr = ""; + sgr0 = ""; + level_str = "NONE"; + break; + } + + if (!colors) { + sgr = ""; + sgr0 = ""; + } + + (void) fprintf(stderr, "%s[%-6s]: %s: %s%s%s%s\n", sgr, level_str, + when, msgid ? msgid : "", msgid ? ": " : "", msg, sgr0); +} + +void +nng_system_logger(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg) +{ +#ifdef NNG_PLATFORM_POSIX + int pri; + switch (level) { + case NNG_LOG_ERR: + pri = LOG_ERR; + break; + case NNG_LOG_WARN: + pri = LOG_WARNING; + break; + case NNG_LOG_NOTICE: + pri = LOG_NOTICE; + break; + case NNG_LOG_INFO: + pri = LOG_INFO; + break; + case NNG_LOG_DEBUG: + pri = LOG_DEBUG; + break; + default: + pri = LOG_INFO; + break; + } + switch (facility) { + case NNG_LOG_DAEMON: + pri |= LOG_DAEMON; + break; + case NNG_LOG_USER: + pri |= LOG_USER; + break; + case NNG_LOG_AUTH: + pri |= LOG_AUTHPRIV; + break; + case NNG_LOG_LOCAL0: + pri |= LOG_LOCAL0; + break; + case NNG_LOG_LOCAL1: + pri |= LOG_LOCAL1; + break; + case NNG_LOG_LOCAL2: + pri |= LOG_LOCAL2; + break; + case NNG_LOG_LOCAL3: + pri |= LOG_LOCAL3; + break; + case NNG_LOG_LOCAL4: + pri |= LOG_LOCAL4; + break; + case NNG_LOG_LOCAL5: + pri |= LOG_LOCAL5; + break; + case NNG_LOG_LOCAL6: + pri |= LOG_LOCAL6; + break; + case NNG_LOG_LOCAL7: + pri |= LOG_LOCAL7; + break; + } + + if (msgid) { + syslog(pri, "%s: %s", msgid, msg); + } else { + syslog(pri, "%s", msg); + } +#else + // everyone else just goes to stderr for now + nng_stderr_logger(level, facility, msgid, msg); +#endif +} + +static void +nni_vlog(nng_log_level level, nng_log_facility facility, const char *msgid, + const char *msg, va_list ap) +{ + // nobody allowed to log at LOG_EMERG or using LOG_KERN + if (level > log_level || log_level == 0 || facility == 0) { + return; + } + char formatted[512]; + vsnprintf(formatted, sizeof(formatted), msg, ap); + log_logger(level, facility, msgid, formatted); +} + +void +nng_log_debug(const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(NNG_LOG_DEBUG, log_facility, msgid, msg, ap); + va_end(ap); +} + +void +nng_log_info(const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(NNG_LOG_INFO, log_facility, msgid, msg, ap); + va_end(ap); +} + +void +nng_log_notice(const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(NNG_LOG_NOTICE, log_facility, msgid, msg, ap); + va_end(ap); +} + +void +nng_log_warn(const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(NNG_LOG_WARN, log_facility, msgid, msg, ap); + va_end(ap); +} + +void +nng_log_err(const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(NNG_LOG_ERR, log_facility, msgid, msg, ap); + va_end(ap); +} + +void +nng_log_auth(nng_log_level level, const char *msgid, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + nni_vlog(level, NNG_LOG_AUTH, msgid, msg, ap); + va_end(ap); +} diff --git a/src/core/log_test.c b/src/core/log_test.c new file mode 100644 index 00000000..6aa1dd53 --- /dev/null +++ b/src/core/log_test.c @@ -0,0 +1,188 @@ +// +// Copyright 2024 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include +#include + +#include +#include + +#ifdef NNG_PLATFORM_POSIX +#include +#endif + +void +test_log_stderr(void) +{ + nng_log_set_logger(nng_stderr_logger); + nng_log_set_level(NNG_LOG_DEBUG); + nng_log_info(NULL, "something wicked"); + nng_log_err(NULL, "This is an error message"); + nng_log_warn(NULL, "This is a warning message"); + nng_log_notice(NULL, "This is a notice message"); + nng_log_info(NULL, "This is an info message"); + nng_log_debug(NULL, "This is a debug message"); + nng_log_notice("TESTMSG", "This notice has a msg id"); +#ifdef NNG_PLATFORM_POSIX + setenv("NO_COLOR", "", 1); + nng_log_err("MONO", "Uncolored messages"); + unsetenv("NO_COLOR"); + setenv("NNG_LOG_NO_COLOR", "", 1); + nng_log_err("MONO", "Also uncolored messages"); +#endif + // these are intentionally unreasonably large + nng_log_set_level((nng_log_level) 100); + nng_log_auth(99, "WTF", "This should be NONE"); +} + +typedef struct test_log_entry { + nng_log_level level; + nng_log_facility facility; + const char *msgid; + char msg[128]; +} test_log_entry; + +typedef struct { + test_log_entry entries[16]; + int count; +} test_logs; + +void +custom_logger_base(test_logs *logs, nng_log_level level, + nng_log_facility facility, const char *msgid, const char *msg) +{ + test_log_entry *entry; + + if (logs->count >= 16) { + return; + } + entry = &logs->entries[logs->count++]; + entry->level = level; + entry->facility = facility; + entry->msgid = msgid; // ok for constant strings + snprintf(entry->msg, sizeof(entry->msg), "%s", msg); +} + +static test_logs test_logs_priority; +void +test_log_priority_logger(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg) +{ + custom_logger_base(&test_logs_priority, level, facility, msgid, msg); +} + +void +test_log_priority(void) +{ + nng_log_set_logger(test_log_priority_logger); + nng_log_set_level(NNG_LOG_WARN); + nng_log_debug(NULL, "This should be filtered"); + nng_log_err("ERR", "This gets through"); + nng_log_notice("NOT", "This gets filtered"); + nng_log_warn("WRN", "This makes it"); + nng_log_info("INF", "Filtered!"); + nng_log_err("ERR", "Another error message"); + nng_log_auth(NNG_LOG_ERR, "AUTH", "authentication err sample message"); + nng_log_set_level(NNG_LOG_NONE); + nng_log_err("ERR", "Yet Another error message - filtered"); + NUTS_ASSERT(test_logs_priority.count == 4); + NUTS_ASSERT(strcmp(test_logs_priority.entries[0].msgid, "ERR") == 0); + NUTS_ASSERT(test_logs_priority.entries[0].level == NNG_LOG_ERR); + NUTS_ASSERT(strcmp(test_logs_priority.entries[1].msgid, "WRN") == 0); + NUTS_ASSERT(test_logs_priority.entries[1].level == NNG_LOG_WARN); + NUTS_ASSERT(strcmp(test_logs_priority.entries[2].msgid, "ERR") == 0); + NUTS_ASSERT(test_logs_priority.entries[2].level == NNG_LOG_ERR); + NUTS_ASSERT(strcmp(test_logs_priority.entries[3].msgid, "AUTH") == 0); + NUTS_ASSERT(test_logs_priority.entries[3].level == NNG_LOG_ERR); + NUTS_ASSERT(test_logs_priority.entries[3].facility == NNG_LOG_AUTH); +} + +static test_logs test_logs_facility; +void +test_log_facility_logger(nng_log_level level, nng_log_facility facility, + const char *msgid, const char *msg) +{ + custom_logger_base(&test_logs_facility, level, facility, msgid, msg); +} + +void +test_log_facility(void) +{ + nng_log_set_logger(test_log_facility_logger); + nng_log_set_facility(NNG_LOG_LOCAL2); + nng_log_set_level(NNG_LOG_WARN); + nng_log_debug(NULL, "This should be filtered"); + nng_log_err("001", "This is local2"); + nng_log_set_facility(NNG_LOG_DAEMON); + nng_log_warn("002", "This is Daemon"); + + NUTS_ASSERT(test_logs_facility.count == 2); + NUTS_ASSERT(strcmp(test_logs_facility.entries[0].msgid, "001") == 0); + NUTS_ASSERT(test_logs_facility.entries[0].level == NNG_LOG_ERR); + NUTS_ASSERT(test_logs_facility.entries[0].facility == NNG_LOG_LOCAL2); + NUTS_ASSERT(strcmp(test_logs_facility.entries[1].msgid, "002") == 0); + NUTS_ASSERT(test_logs_facility.entries[1].facility == NNG_LOG_DAEMON); + NUTS_ASSERT(test_logs_facility.entries[1].level == NNG_LOG_WARN); +} + +void +test_log_null_logger(void) +{ + nng_log_set_logger(nng_null_logger); + nng_log_set_level(NNG_LOG_DEBUG); + nng_log_debug(NULL, "This should be dropped"); + nng_log_err("001", "This is local2"); + nng_log_warn("002", "This is also dropped"); + + // Lets also try setting it to NULL + nng_log_set_logger(nng_null_logger); + nng_log_warn("003", "This is also dropped"); +} + +void +test_log_system_logger(void) +{ + nng_log_set_logger(nng_system_logger); + nng_log_set_level(NNG_LOG_DEBUG); + nng_log_debug(NULL, "This is a test message, ignore me"); + nng_log_set_facility(NNG_LOG_DAEMON); + nng_log_debug(NULL, "This is a test message (DAEMON), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL0); + nng_log_debug(NULL, "This is a test message (LOCAL0), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL1); + nng_log_debug(NULL, "This is a test message (LOCAL1), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL2); + nng_log_debug(NULL, "This is a test message (LOCAL2), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL3); + nng_log_debug(NULL, "This is a test message (LOCAL3), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL4); + nng_log_debug(NULL, "This is a test message (LOCAL4), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL5); + nng_log_debug(NULL, "This is a test message (LOCAL5), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL6); + nng_log_debug(NULL, "This is a test message (LOCAL6), ignore me"); + nng_log_set_facility(NNG_LOG_LOCAL7); + nng_log_debug(NULL, "This is a test message (LOCAL7), ignore me"); + + nng_log_set_facility(NNG_LOG_USER); + nng_log_debug(NULL, "This is a test message (LOCAL7), ignore me"); + nng_log_err("TEST", "This is only a test (ERR). Ignore me."); + nng_log_warn("TEST", "This is only a test (WARN). Ignore me."); + nng_log_notice("TEST", "This is only a test (NOTICE). Ignore me."); + nng_log_info("TEST", "This is only a test (INFO). Ignore me."); +} + +TEST_LIST = { + { "log stderr", test_log_stderr }, + { "log priority", test_log_priority }, + { "log facility", test_log_facility }, + { "log null logger", test_log_null_logger }, + { "log system logger", test_log_system_logger }, + { NULL, NULL }, +}; -- cgit v1.2.3-70-g09d2