3 * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
4 * Dedicated to the Public Domain
8 * Log/Debug API Implementation
9 * We provide thread-safety so we need a global lock. Function which
10 * are prefixed with log__* need the lock to be held. All other functions must
11 * be called without the lock held.
22 #include "shl_githead.h"
28 * We need a global locking mechanism. Use pthread here.
31 static pthread_mutex_t log__mutex = PTHREAD_MUTEX_INITIALIZER;
33 static inline void log_lock()
35 pthread_mutex_lock(&log__mutex);
38 static inline void log_unlock()
40 pthread_mutex_unlock(&log__mutex);
45 * We print seconds and microseconds since application start for each
49 static struct timeval log__ftime;
51 static void log__time(long long *sec, long long *usec)
55 if (log__ftime.tv_sec == 0 && log__ftime.tv_usec == 0) {
56 gettimeofday(&log__ftime, NULL);
60 gettimeofday(&t, NULL);
61 *sec = t.tv_sec - log__ftime.tv_sec;
62 *usec = (long long)t.tv_usec - (long long)log__ftime.tv_usec;
65 *usec = 1000000 + *usec;
72 * Several logging-parameters may be omitted by applications. To provide sane
73 * default values we provide constants here.
75 * LOG_SUBSYSTEM: By default no subsystem is specified
79 const struct log_config LOG_CONFIG = {
92 const char *LOG_SUBSYSTEM = NULL;
96 * By default DEBUG and INFO messages are disabled. If LOG_ENABLE_DEBUG is not
97 * defined, then all log_debug() statements compile to zero-code and they cannot
98 * be enabled on runtime.
99 * To enable DEBUG or INFO messages at runtime, you can either specify that they
100 * should be enabled globally, per file or specify a custom filter. Other
101 * messages than DEBUG and INFO cannot be configured. However, additional
102 * configuration options may be added later.
104 * Use log_set_config() to enable debug/info messages globally. If you
105 * enable a global message type, then all other filters are skipped. If you
106 * disable a global message type then fine-grained filters can take effect.
108 * To enable DEBUG/INFO messages for a specific source-file, you can add
109 * this line to the top of the source file:
110 * #define LOG_CONFIG LOG_STATIC_CONFIG(true, true)
111 * So info and debug messages are enabled for this file on compile-time. First
112 * parameter of LOG_STATIC_CONFIG is for debug, second one for info.
114 * Or you can add new configurations on runtime. Runtime configurations take a
115 * filter parameter and a config parameter. The filter specifies what messages
116 * are affected and the config parameter specifies what action is performed.
119 static struct log_config log__gconfig = {
133 struct log_dynconf *next;
135 struct log_filter filter;
136 struct log_config config;
139 static struct log_dynconf *log__dconfig = NULL;
141 void log_set_config(const struct log_config *config)
147 log__gconfig = *config;
151 int log_add_filter(const struct log_filter *filter,
152 const struct log_config *config)
154 struct log_dynconf *dconf;
157 if (!filter || !config)
160 dconf = malloc(sizeof(*dconf));
164 memset(dconf, 0, sizeof(*dconf));
165 memcpy(&dconf->filter, filter, sizeof(*filter));
166 memcpy(&dconf->config, config, sizeof(*config));
170 dconf->handle = log__dconfig->handle + 1;
171 dconf->next = log__dconfig;
172 log__dconfig = dconf;
179 void log_rm_filter(int handle)
181 struct log_dynconf *dconf, *i;
187 if (log__dconfig->handle == handle) {
188 dconf = log__dconfig;
189 log__dconfig = dconf->next;
190 } else for (i = log__dconfig; i->next; i = i->next) {
192 if (dconf->handle == handle) {
193 i->next = dconf->next;
203 void log_clean_filters()
205 struct log_dynconf *dconf;
208 while ((dconf = log__dconfig)) {
209 log__dconfig = dconf->next;
215 static bool log__matches(const struct log_filter *filter,
222 if (!file || strncmp(filter->file, file, LOG_STRMAX))
225 if (filter->line >= 0 && filter->line != line)
228 if (!func || strncmp(filter->func, func, LOG_STRMAX))
232 if (!subs || strncmp(filter->subs, subs, LOG_STRMAX))
238 static bool log__omit(const char *file,
241 const struct log_config *config,
243 enum log_severity sev)
246 struct log_dynconf *dconf;
248 if (sev >= LOG_SEV_NUM)
252 val = config->sev[sev];
259 for (dconf = log__dconfig; dconf; dconf = dconf->next) {
260 if (log__matches(&dconf->filter, file, line, func, subs)) {
261 val = dconf->config.sev[sev];
269 val = log__gconfig.sev[sev];
279 * Forward declaration so we can use the locked-versions in other functions
280 * here. Be careful to avoid deadlocks, though.
281 * Also set default log-subsystem to "log" for all logging inside this API.
284 static void log__submit(const char *file,
287 const struct log_config *config,
293 static void log__format(const char *file,
296 const struct log_config *config,
302 #define LOG_SUBSYSTEM "log"
306 * By default logging is done to stderr. However, you can set a file which is
307 * used instead of stderr for logging. We do not provide complex log-rotation or
308 * management functions, you can add them yourself or use a proper init-system
309 * like systemd which does this for you.
310 * We cannot set this to "stderr" as stderr might not be a compile-time
311 * constant. Therefore, NULL means stderr.
314 static FILE *log__file = NULL;
316 int log_set_file(const char *file)
321 f = fopen(file, "a");
323 log_err("cannot change log-file to %s (%d): %s",
324 file, errno, strerror(errno));
335 if (log__file != f) {
336 log__format(LOG_DEFAULT, LOG_NOTICE,
337 "set log-file to %s", file);
354 * The log__submit function writes the message into the current log-target. It
355 * must be called with log__mutex locked.
356 * log__format does the same but first converts the argument list into a
358 * By default the current time elapsed since the first message was logged is
359 * prepended to the message. file, line and func information are appended to the
360 * message if sev == LOG_DEBUG.
361 * The subsystem, if not NULL, is prepended as "SUBS: " to the message and a
362 * newline is always appended by default. Multiline-messages are not allowed and
363 * do not make sense here.
366 static const char *log__sev2str[] = {
367 [LOG_DEBUG] = "DEBUG",
369 [LOG_NOTICE] = "NOTICE",
370 [LOG_WARNING] = "WARNING",
371 [LOG_ERROR] = "ERROR",
372 [LOG_CRITICAL] = "CRITICAL",
373 [LOG_ALERT] = "ALERT",
374 [LOG_FATAL] = "FATAL",
377 static void log__submit(const char *file,
380 const struct log_config *config,
386 const char *prefix = NULL;
390 if (log__omit(file, line, func, config, subs, sev))
398 log__time(&sec, &usec);
400 if (sev < LOG_SEV_NUM)
401 prefix = log__sev2str[sev];
405 fprintf(out, "[%.4lld.%.6lld] %s: %s: ",
406 sec, usec, prefix, subs);
408 fprintf(out, "[%.4lld.%.6lld] %s: ",
412 fprintf(out, "[%.4lld.%.6lld] %s: ", sec, usec, subs);
414 fprintf(out, "[%.4lld.%.6lld] ", sec, usec);
417 vfprintf(out, format, args);
419 if (sev == LOG_DEBUG) {
426 fprintf(out, " (%s() in %s:%d)\n", func, file, line);
432 static void log__format(const char *file,
435 const struct log_config *config,
443 va_start(list, format);
444 log__submit(file, line, func, config, subs, sev, format, list);
449 void log_submit(const char *file,
452 const struct log_config *config,
458 int saved_errno = errno;
461 log__submit(file, line, func, config, subs, sev, format, args);
468 void log_format(const char *file,
471 const struct log_config *config,
478 int saved_errno = errno;
480 va_start(list, format);
482 log__submit(file, line, func, config, subs, sev, format, list);
490 void log_llog(void *data,
499 log_submit(file, line, func, NULL, subs, sev, format, args);
502 void log_print_init(const char *appname)
505 appname = "<unknown>";
506 log_format(LOG_DEFAULT_CONF, NULL, LOG_NOTICE,
507 "%s Revision %s %s %s", appname,
508 shl_git_head, __DATE__, __TIME__);