[ccan] [PATCH v2] pr_log: a new module that provides a simple run-time controlled logging interface

Cody P Schafer dev at codyps.com
Wed Aug 19 12:29:54 AEST 2015


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 "<N>" 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 <dev at codyps.com>
---

I've updated this to:

 - Make it more clear that the pr_<level>() macros are the expected interface
 - remove some old prefix experiments from the docs
 - make ccanlint pass the module fully (with some added tests)

---
 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 99c4910..8d13a4f 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 0000000..dc314ec
--- /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 0000000..7f4feb6
--- /dev/null
+++ b/ccan/pr_log/_info
@@ -0,0 +1,40 @@
+#include <string.h>
+#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 <ccan/pr_log/pr_log.h>
+ *
+ *	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 <dev at codyps.com>
+ */
+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 0000000..1de8cb9
--- /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 <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <ccan/str/str.h>
+
+#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 0000000..8e125b8
--- /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 <stdbool.h>
+#include <ccan/compiler/compiler.h>
+
+/*
+ * 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 0000000..c898065
--- /dev/null
+++ b/ccan/pr_log/test/run-debug.c
@@ -0,0 +1,38 @@
+#define DEBUG 1
+#include <ccan/pr_log/pr_log.h>
+#include <ccan/pr_log/pr_log.c>
+#include <ccan/tap/tap.h>
+
+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 0000000..599a7ed
--- /dev/null
+++ b/ccan/pr_log/test/run-disable.c
@@ -0,0 +1,37 @@
+#define CCAN_PR_LOG_DISABLE 1
+#include <ccan/pr_log/pr_log.h>
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+
+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 0000000..40c2b1a
--- /dev/null
+++ b/ccan/pr_log/test/run.c
@@ -0,0 +1,43 @@
+#include <ccan/pr_log/pr_log.h>
+#include <ccan/pr_log/pr_log.c>
+#include <ccan/tap/tap.h>
+
+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.5.0



More information about the ccan mailing list