diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-03-04 14:40:36 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2013-03-04 14:40:36 +0100 |
commit | 0249b2cb0b0da272a78b4967b9c9f6f02d73cad5 (patch) | |
tree | 6b2c8d325b8c18dda73f7bb6e028d64cd49730c6 /src/shl_log.h | |
parent | f14fde004f67171eaaa20081f475d49089a9ace3 (diff) |
shl: move log.[ch] to shl_log.[ch]
We want to avoid any static files that are shared between multiple
programs but are not part of SHL. These make the build-process just more
complex.
Move log.[ch] to SHL so we have a known context.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Diffstat (limited to 'src/shl_log.h')
-rw-r--r-- | src/shl_log.h | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/src/shl_log.h b/src/shl_log.h new file mode 100644 index 0000000..fe62397 --- /dev/null +++ b/src/shl_log.h @@ -0,0 +1,298 @@ +/* + * Log/Debug Interface + * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com> + * Dedicated to the Public Domain + */ + +/* + * Log/Debug Interface + * This interface provides basic logging to a single file or stderr. By default, + * all log-messages are forwarded to stderr but you can change this to an + * arbitrary file. However, no complex file-rotation/backup functions are + * supported so you should use the default (stderr) and use a proper init-system + * like systemd to do log-rotations. This can also forward stderr messages into + * log-files. + * + * Besides simple log-functions this also provides run-time filters so special + * debug messages can be enabled/disabled. This should be used for + * debugging-only because it may slow-down your application if every message is + * filtered. + * + * Define BUILD_ENABLE_DEBUG before including this header to enable + * debug-messages for this file. + */ + +#ifndef SHL_LOG_H_INCLUDED +#define SHL_LOG_H_INCLUDED + +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> + +/* + * Log Messages and Filters + * A log message consists of: + * - file: the source file where the call was made + * - line: the corresponding line number + * - func: the function name + * - config: special configuration for this message only + * - subs: the subsystem + * - sev: the severity + * - format: format string + * - args: arguments depending on format string + * Depending on this information the log system decides whether the message is + * discarded or logged and what information is included. To allow fine-grained + * configuration you can add log_filter and log_config objects. A log_filter + * object specifies to what messages the log_config object shall apply. If a + * log_filter does not match, then the corresponding log_config is ignored. + * The log_config specifies whether a message is discarded, logged or whether + * other filters shall be searched. + * + * The "config" field of every log-message does not have a corresponding + * log_filter object. Instead, it is assumed that the config object only applies + * to this single message. This allows to specify special behavior for every + * single message but also refer to global filters. + * + * Config Object: + * A log_config object contains a severity-array. Each severity is the index of + * an integer in the array. If the integer is 0, then messages with the given + * severity are discarded, if is is 1, then they are logged. If it is 2, then + * the config object is ignored and global filters will be used. + * + * Filter Object: + * A filter object specifies what messages are affected by the corresponding + * config object. file, func and subs are strings. If they are empty (length=0) + * then they are not used for matching. If line is smaller than 0 then it is + * ignored. Otherwise all given information must match. + * + * log_set_config(config): + * This sets the global config which is used if no filter applies. + * + * log_add_filter(filter, config): + * This adds a new filter to the global filter-list. If the filter matches the + * given config shall apply. This returns a negative error code on failure. + * Otherwise it returns an ID which can be used to remove the filter again. + * An ID is always >= 0. + * + * log_rm_filter(id): + * This removes the filter with ID=id. + * + * log_clean_filters(): + * This removes all filters. This frees all allocated memory by the filters. + * + * + * If you want to set a config option which shall apply to all log-messages in + * a single source-file, then you can add the following line to the head of the + * source-file: + * #define LOG_CONFIG LOG_CONFIG_ALL(options...) + * Where LOG_CONFIG_ALL() can be replaced by LOG_CONFIG_DEBUG, LOG_CONFIG_INFO + * and so on. + * The LOG_CONFIG_*() macros create a log_config object on the stack and are + * used for convenience. You can also provide your own log_config object here. + * This LOG_CONFIG constant is picked up by all log-helpers which are provided + * below. The raw log_submit and log_format() functions are not affected by + * this, though. + */ + +enum log_severity { + LOG_FATAL = 0, + LOG_ALERT = 1, + LOG_CRITICAL = 2, + LOG_ERROR = 3, + LOG_WARNING = 4, + LOG_NOTICE = 5, + LOG_INFO = 6, + LOG_DEBUG = 7, + LOG_SEV_NUM, +}; + +#define LOG_STRMAX 128 + +struct log_filter { + char file[LOG_STRMAX]; + int line; + char func[LOG_STRMAX]; + char subs[LOG_STRMAX]; +}; + +struct log_config { + int sev[LOG_SEV_NUM]; +}; + +#define LOG_CONFIG_ALL(debug, info, notice, warning, error, critical, alert, fatal) \ + (struct log_config){ .sev = { \ + [LOG_DEBUG] = (debug), \ + [LOG_INFO] = (info), \ + [LOG_NOTICE] = (notice), \ + [LOG_WARNING] = (warning), \ + [LOG_ERROR] = (error), \ + [LOG_CRITICAL] = (critical), \ + [LOG_ALERT] = (alert), \ + [LOG_FATAL] = (fatal), \ + } } + +#define LOG_CONFIG_DEBUG(debug) \ + LOG_CONFIG_ALL((debug), 2, 2, 2, 2, 2, 2, 2) +#define LOG_CONFIG_INFO(debug, info) \ + LOG_CONFIG_ALL((debug), (info), 2, 2, 2, 2, 2, 2) +#define LOG_CONFIG_WARNING(debug, info, notice, warning) \ + LOG_CONFIG_ALL((debug), (info), (notice), (warning), 2, 2, 2, 2) + +void log_set_config(const struct log_config *config); +int log_add_filter(const struct log_filter *filter, + const struct log_config *config); +void log_rm_filter(int handle); +void log_clean_filter(); + +/* + * Log-Functions + * These functions pass a log-message to the log-subsystem. Handy helpers are + * provided below. You almost never use these directly. + * + * log_submit: + * Submit the message to the log-subsystem. This is the backend of all other + * loggers. + * + * log_format: + * Same as log_submit but first converts the arguments into a va_list object. + * + * log_llog: + * Same as log_submit but used as connection to llog. It uses the default config + * for every message. + * + * log_set_file(file): + * This opens the file specified by \file and redirects all new messages to this + * file. If \file is NULL, then the default is used which is stderr. + * Messages are appended to the file and no file-locks are used so you cannot + * use a single file for multiple processes. + * No log-file-rotations or other backup/rotation functions are supported. Use a + * proper init system like systemd to do this. + * + * log_print_init(appname): + * This prints a message with build-time/date and appname to the log. You should + * invoke this very early in your program. It is not required, though. However, + * every message is prepended with a time-offset since application-start. This + * offset is measured since the first log-message is sent so you should send + * some log-message at application start. This is a handy-helper to do this. + */ + +__attribute__((format(printf, 7, 0))) +void log_submit(const char *file, + int line, + const char *func, + const struct log_config *config, + const char *subs, + unsigned int sev, + const char *format, + va_list args); + +__attribute__((format(printf, 7, 8))) +void log_format(const char *file, + int line, + const char *func, + const struct log_config *config, + const char *subs, + unsigned int sev, + const char *format, + ...); + +__attribute__((format(printf, 7, 0))) +void log_llog(void *data, + const char *file, + int line, + const char *func, + const char *subs, + unsigned int sev, + const char *format, + va_list args); + +int log_set_file(const char *file); +void log_print_init(const char *appname); + +static inline __attribute__((format(printf, 2, 3))) +void log_dummyf(unsigned int sev, const char *format, ...) +{ +} + +/* + * Default values + * All helpers automatically pick-up the file, line, func, config and subsystem + * parameters for a log-message. file, line and func are generated with + * __FILE__, __LINE__ and __func__ and should almost never be replaced. The + * config argument is by default NULL so global filters apply. You can use the + * #define LOG_CONFIG ... + * method to overwrite this. It is described above in detail. + * The subsystem is by default an empty string. To overwrite this, add this + * line to the top of your source file: + * #define LOG_SUBSYSTEM "mysubsystem" + * Then all following log-messages will use this string as subsystem. + * + * If you want to change one of these, you need to directly use log_submit and + * log_format. If you want the defaults for file, line and func you can use: + * log_format(LOG_DEFAULT_BASE, config, subsys, sev, format, ...); + * If you want the default config, use: + * log_format(LOG_DEFAULT_CONF, subsys, sev, format, ...); + * If you want all default values, use: + * log_format(LOG_DEFAULT, sev, format, ...); + * + * If you want to change a single value, this is the default line that is used + * internally. Adjust it to your needs: + * log_format(__FILE__, __LINE__, __func__, &LOG_CONFIG, LOG_SUBSYSTEM, + * LOG_ERROR, "your format string: %s %d", "some args", 5, ...); + * + * log_printf is the same as log_format(LOG_DEFAULT, sev, format, ...) and is + * the most basic wrapper that you can use. + */ + +#ifndef LOG_CONFIG +extern const struct log_config LOG_CONFIG; +#endif + +#ifndef LOG_SUBSYSTEM +extern const char *LOG_SUBSYSTEM; +#endif + +#define LOG_DEFAULT_BASE __FILE__, __LINE__, __func__ +#define LOG_DEFAULT_CONF LOG_DEFAULT_BASE, &LOG_CONFIG +#define LOG_DEFAULT LOG_DEFAULT_CONF, LOG_SUBSYSTEM + +#define log_printf(sev, format, ...) \ + log_format(LOG_DEFAULT, (sev), (format), ##__VA_ARGS__) + +/* + * Helpers + * The pick-up all the default values and submit the message to the + * log-subsystem. The log_debug() function produces zero-code if + * BUILD_ENABLE_DEBUG is not defined. Therefore, it can be heavily used for + * debugging and will not have any side-effects. + */ + +#ifdef BUILD_ENABLE_DEBUG + #define log_debug(format, ...) \ + log_printf(LOG_DEBUG, (format), ##__VA_ARGS__) +#else + #define log_debug(format, ...) \ + log_dummyf(LOG_DEBUG, (format), ##__VA_ARGS__) +#endif + +#define log_info(format, ...) \ + log_printf(LOG_INFO, (format), ##__VA_ARGS__) +#define log_notice(format, ...) \ + log_printf(LOG_NOTICE, (format), ##__VA_ARGS__) +#define log_warning(format, ...) \ + log_printf(LOG_WARNING, (format), ##__VA_ARGS__) +#define log_error(format, ...) \ + log_printf(LOG_ERROR, (format), ##__VA_ARGS__) +#define log_critical(format, ...) \ + log_printf(LOG_CRITICAL, (format), ##__VA_ARGS__) +#define log_alert(format, ...) \ + log_printf(LOG_ALERT, (format), ##__VA_ARGS__) +#define log_fatal(format, ...) \ + log_printf(LOG_FATAL, (format), ##__VA_ARGS__) + +#define log_dbg log_debug +#define log_warn log_warning +#define log_err log_error +#define log_crit log_critical + +#endif /* SHL_LOG_H_INCLUDED */ |