From 1f7028e9d7b87a95613c9db6a64a2111d4899395 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Tue, 18 Aug 2015 22:29:54 -0400 Subject: [PATCH] pr_log: a new module that provides a simple run-time controlled logging interface A simple printf logging infra where levels are determined by the value of the "DEBUG" environment variable. This is loosely based on the interfaces & functionality of Linux's printk() and pr_*() wrapper macros. Note that the current implementation uses "" prefixes (where N is a syslog level in ascii), allowing other programs that parse log output (like systemd's journald) to know what the priority level is. Signed-off-by: Cody P Schafer Signed-off-by: Rusty Russell --- Makefile-ccan | 1 + ccan/pr_log/LICENSE | 1 + ccan/pr_log/_info | 40 +++++++++++++++++++ ccan/pr_log/pr_log.c | 48 +++++++++++++++++++++++ ccan/pr_log/pr_log.h | 72 ++++++++++++++++++++++++++++++++++ ccan/pr_log/test/run-debug.c | 38 ++++++++++++++++++ ccan/pr_log/test/run-disable.c | 37 +++++++++++++++++ ccan/pr_log/test/run.c | 43 ++++++++++++++++++++ 8 files changed, 280 insertions(+) create mode 120000 ccan/pr_log/LICENSE create mode 100644 ccan/pr_log/_info create mode 100644 ccan/pr_log/pr_log.c create mode 100644 ccan/pr_log/pr_log.h create mode 100644 ccan/pr_log/test/run-debug.c create mode 100644 ccan/pr_log/test/run-disable.c create mode 100644 ccan/pr_log/test/run.c diff --git a/Makefile-ccan b/Makefile-ccan index 99c49104..8d13a4f5 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -88,6 +88,7 @@ MODS_WITH_SRC := aga \ ogg_to_pcm \ opt \ order \ + pr_log \ ptrint \ ptr_valid \ pushpull \ diff --git a/ccan/pr_log/LICENSE b/ccan/pr_log/LICENSE new file mode 120000 index 00000000..dc314eca --- /dev/null +++ b/ccan/pr_log/LICENSE @@ -0,0 +1 @@ +../../licenses/LGPL-2.1 \ No newline at end of file diff --git a/ccan/pr_log/_info b/ccan/pr_log/_info new file mode 100644 index 00000000..7f4feb64 --- /dev/null +++ b/ccan/pr_log/_info @@ -0,0 +1,40 @@ +#include +#include "config.h" + +/** + * pr_log - print things with varying levels of importance + * + * pr_log is a "logger" styled similarly to Linux's printk() and pr_*() macros. + * The amount of debug output is controlled by the value of the `DEBUG` + * environment variable. + * + * It provides work-alikes for Linux's pr_devel, pr_debug, pr_info, etc macros. + * + * Example: + * #include + * + * int main(int argc, char *argv[]) + * { + * pr_debug("It's working\n"); + * pr_info("Really, it works\n"); + * pr_emerg("I'm serious %d\n", argc); + * return 0; + * } + * + * License: LGPL (v2.1 or any later version) + * Author: Cody P Schafer + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/compiler\n"); + printf("ccan/str\n"); + return 0; + } + + return 1; +} diff --git a/ccan/pr_log/pr_log.c b/ccan/pr_log/pr_log.c new file mode 100644 index 00000000..1de8cb9e --- /dev/null +++ b/ccan/pr_log/pr_log.c @@ -0,0 +1,48 @@ +/* Licensed under LGPLv2.1+ - see LICENSE file for details */ +#include "pr_log.h" + +#include +#include +#include +#include +#include +#include + +#include + +#define DEBUG_NEED_INIT INT_MIN +static int debug = DEBUG_NEED_INIT; + +bool debug_is(int lvl) +{ + return lvl <= debug_level(); +} + +int debug_level(void) +{ + if (debug != DEBUG_NEED_INIT) + return debug; + char *c = getenv("DEBUG"); + if (!c) { + debug = CCAN_PR_LOG_DEFAULT_LEVEL; + return debug; + } + + debug = atoi(c); + return debug; +} + +void pr_log_(char const *fmt, ...) +{ + int level = INT_MIN; + if (fmt[0] == '<' && cisdigit(fmt[1]) && fmt[2] == '>') + level = fmt[1] - '0'; + + if (!debug_is(level)) + return; + + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} diff --git a/ccan/pr_log/pr_log.h b/ccan/pr_log/pr_log.h new file mode 100644 index 00000000..8e125b82 --- /dev/null +++ b/ccan/pr_log/pr_log.h @@ -0,0 +1,72 @@ +/* Licensed under LGPLv2.1+ - see LICENSE file for details */ +#ifndef CCAN_PR_LOG_H_ +#define CCAN_PR_LOG_H_ + +#include +#include + +/* + * pr_emerg, pr_alert, pr_crit, pr_error, pr_warn, pr_notice, pr_info, pr_debug + * + * Each of these prints a format string only in the case where we're running at + * the selected debug level. + * + * Note that using these functions also causes a pre-pended log level to be + * included in the printed string. + * + * Log levels correspond to those used in syslog(3) . + */ +#define pr_emerg(...) pr_log_(LOG_EMERG __VA_ARGS__) +#define pr_alert(...) pr_log_(LOG_ALERT __VA_ARGS__) +#define pr_crit(...) pr_log_(LOG_CRIT __VA_ARGS__) +#define pr_error(...) pr_log_(LOG_ERROR __VA_ARGS__) +#define pr_warn(...) pr_log_(LOG_WARN __VA_ARGS__) +#define pr_notice(...) pr_log_(LOG_NOTICE __VA_ARGS__) +#define pr_info(...) pr_log_(LOG_INFO __VA_ARGS__) +#define pr_debug(...) pr_log_(LOG_DEBUG __VA_ARGS__) + +#ifdef DEBUG +# define pr_devel(...) pr_debug(__VA_ARGS__) +#else +static PRINTF_FMT(1,2) inline void pr_check_printf_args(const char *fmt, ...) +{ + (void)fmt; +} +# define pr_devel(...) pr_check_printf_args(__VA_ARGS__) +#endif + +#ifndef CCAN_PR_LOG_DEFAULT_LEVEL +# define CCAN_PR_LOG_DEFAULT_LEVEL 6 +#endif + +#define LOG_EMERG "<0>" +#define LOG_ALERT "<1>" +#define LOG_CRIT "<2>" +#define LOG_ERROR "<3>" +#define LOG_WARN "<4>" +#define LOG_NOTICE "<5>" +#define LOG_INFO "<6>" +#define LOG_DEBUG "<7>" + +#ifndef CCAN_PR_LOG_DISABLE +/** + * pr_log_ - print output based on the given logging level + * + * Example: + * + * pr_log_(LOG_EMERG "something went terribly wrong\n"); + * pr_log_(LOG_DEBUG "everything is fine\n"); + */ +void PRINTF_FMT(1,2) pr_log_(char const *fmt, ...); +bool debug_is(int lvl); +int debug_level(void); +#else +static PRINTF_FMT(1,2) inline void pr_log_(char const *fmt, ...) +{ + (void)fmt; +} +static inline bool debug_is(int lvl) { (void)lvl; return false; } +static inline int debug_level(void) { return -1; } +#endif + +#endif diff --git a/ccan/pr_log/test/run-debug.c b/ccan/pr_log/test/run-debug.c new file mode 100644 index 00000000..c898065b --- /dev/null +++ b/ccan/pr_log/test/run-debug.c @@ -0,0 +1,38 @@ +#define DEBUG 1 +#include +#include +#include + +int main(void) +{ + plan_tests(6); + debug = 3; + ok1(!debug_is(4)); + debug = 1; + ok1(debug_is(0)); + ok1(debug_is(1)); + ok1(!debug_is(2)); + ok1(!debug_is(3)); + + pr_emerg("Emerg\n"); + pr_alert("Alert\n"); + pr_crit("Crit\n"); + pr_error("Error\n"); + pr_warn("Warn\n"); + pr_notice("Notice\n"); + pr_info("Info\n"); + pr_debug("Debug\n"); + + pr_devel("Devel\n"); + + debug = INT_MIN; + setenv("DEBUG", "1", 1); + ok1(debug_is(1)); + + /* malformed check */ + pr_log_(":4> 1\n"); + pr_log_(" 2\n"); + pr_log_("<1} 3\n"); + + return exit_status(); +} diff --git a/ccan/pr_log/test/run-disable.c b/ccan/pr_log/test/run-disable.c new file mode 100644 index 00000000..599a7ed0 --- /dev/null +++ b/ccan/pr_log/test/run-disable.c @@ -0,0 +1,37 @@ +#define CCAN_PR_LOG_DISABLE 1 +#include +#include +#include + +int main(void) +{ + plan_tests(7); + ok1(!debug_is(4)); + ok1(!debug_is(0)); + ok1(!debug_is(1)); + ok1(!debug_is(2)); + ok1(!debug_is(3)); + + pr_emerg("Emerg\n"); + pr_alert("Alert\n"); + pr_crit("Crit\n"); + pr_error("Error\n"); + pr_warn("Warn\n"); + pr_notice("Notice\n"); + pr_info("Info\n"); + pr_debug("Debug\n"); + + pr_devel("Devel\n"); + + setenv("DEBUG", "1", 1); + ok1(!debug_is(1)); + + /* malformed check */ + pr_log_(":4> 1\n"); + pr_log_(" 2\n"); + pr_log_("<1} 3\n"); + + ok1(debug_level() == -1); + + return exit_status(); +} diff --git a/ccan/pr_log/test/run.c b/ccan/pr_log/test/run.c new file mode 100644 index 00000000..40c2b1af --- /dev/null +++ b/ccan/pr_log/test/run.c @@ -0,0 +1,43 @@ +#include +#include +#include + +int main(void) +{ + plan_tests(8); + debug = 3; + ok1(!debug_is(4)); + debug = 1; + ok1(debug_is(0)); + ok1(debug_is(1)); + ok1(!debug_is(2)); + ok1(!debug_is(3)); + + pr_emerg("Emerg\n"); + pr_alert("Alert\n"); + pr_crit("Crit\n"); + pr_error("Error\n"); + pr_warn("Warn\n"); + pr_notice("Notice\n"); + pr_info("Info\n"); + pr_debug("Debug\n"); + + pr_devel("Devel\n"); + + debug = INT_MIN; + setenv("DEBUG", "1", 1); + ok1(debug_is(1)); + + ok1(debug_level() == 1); + + /* malformed check */ + pr_log_(":4> 1\n"); + pr_log_(" 2\n"); + pr_log_("<1} 3\n"); + + debug = INT_MIN; + unsetenv("DEBUG"); + ok1(debug_level() == CCAN_PR_LOG_DEFAULT_LEVEL); + + return exit_status(); +} -- 2.39.2