The logging API provides four main parts (basics, filtering, threading & blackbox). More...
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <qb/qbutil.h>
#include <qb/qbconfig.h>
Data Structures | |
struct | qb_log_callsite |
An instance of this structure is created in a special ELF section at every dynamic debug callsite. More... | |
union | qb_log_ctl2_arg_t |
Defines | |
#define | LOG_TRACE (LOG_DEBUG + 1) |
#define | QB_LOG_MAX_LEN 512 |
#define | QB_LOG_STRERROR_MAX_LEN 128 |
#define | QB_LOG_INIT_DATA(name) |
#define | QB_LOG_TAG_LIBQB_MSG_BIT 31 |
#define | QB_LOG_TAG_LIBQB_MSG (1 << QB_LOG_TAG_LIBQB_MSG_BIT) |
#define | qb_logt(priority, tags, fmt, args...) |
This is the function to generate a log message if you want to manually add tags. | |
#define | qb_log(priority, fmt, args...) qb_logt(priority, 0, fmt, ##args) |
This is the main function to generate a log message. | |
#define | QB_XC '\a' |
#define | QB_XS "\a" |
#define | qb_perror(priority, fmt, args...) |
This is similar to perror except it goes into the logging system. | |
#define | qb_enter() qb_log(LOG_TRACE, "ENTERING %s()", __func__) |
#define | qb_leave() qb_log(LOG_TRACE, "LEAVING %s()", __func__) |
#define | QB_LOG_CTL2_I32(a) ((qb_log_ctl2_arg_t) { .i32 = (a) }) |
#define | QB_LOG_CTL2_S(a) ((qb_log_ctl2_arg_t) { .s = (a) }) |
Typedefs | |
typedef const char *(* | qb_log_tags_stringify_fn )(uint32_t tags) |
typedef void(* | qb_log_filter_fn )(struct qb_log_callsite *cs) |
typedef void(* | qb_log_logger_fn )(int32_t t, struct qb_log_callsite *cs, time_t timestamp, const char *msg) |
typedef void(* | qb_log_vlogger_fn )(int32_t t, struct qb_log_callsite *cs, time_t timestamp, va_list ap) |
typedef void(* | qb_log_close_fn )(int32_t t) |
typedef void(* | qb_log_reload_fn )(int32_t t) |
Enumerations | |
enum | qb_log_target_slot { QB_LOG_TARGET_START, QB_LOG_TARGET_STATIC_START = QB_LOG_TARGET_START, QB_LOG_SYSLOG = QB_LOG_TARGET_STATIC_START, QB_LOG_STDERR, QB_LOG_BLACKBOX, QB_LOG_STDOUT, QB_LOG_TARGET_STATIC_MAX, QB_LOG_TARGET_STATIC_END = QB_LOG_TARGET_STATIC_MAX - 1, QB_LOG_TARGET_DYNAMIC_START = QB_LOG_TARGET_STATIC_MAX, QB_LOG_TARGET_MAX = 32, QB_LOG_TARGET_DYNAMIC_END = QB_LOG_TARGET_MAX - 1, QB_LOG_TARGET_END = QB_LOG_TARGET_DYNAMIC_END } |
enum | qb_log_target_state { QB_LOG_STATE_UNUSED = 1, QB_LOG_STATE_DISABLED = 2, QB_LOG_STATE_ENABLED = 3 } |
enum | qb_log_conf { QB_LOG_CONF_ENABLED, QB_LOG_CONF_FACILITY, QB_LOG_CONF_DEBUG, QB_LOG_CONF_SIZE, QB_LOG_CONF_THREADED, QB_LOG_CONF_PRIORITY_BUMP, QB_LOG_CONF_STATE_GET, QB_LOG_CONF_FILE_SYNC, QB_LOG_CONF_EXTENDED, QB_LOG_CONF_IDENT } |
enum | qb_log_filter_type { QB_LOG_FILTER_FILE, QB_LOG_FILTER_FUNCTION, QB_LOG_FILTER_FORMAT, QB_LOG_FILTER_FILE_REGEX, QB_LOG_FILTER_FUNCTION_REGEX, QB_LOG_FILTER_FORMAT_REGEX } |
enum | qb_log_filter_conf { QB_LOG_FILTER_ADD, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_TAG_SET, QB_LOG_TAG_CLEAR, QB_LOG_TAG_CLEAR_ALL } |
Functions | |
void | qb_log_real_ (struct qb_log_callsite *cs,...) |
Internal function: use qb_log() or qb_logt(). | |
void | qb_log_real_va_ (struct qb_log_callsite *cs, va_list ap) |
void | qb_log_from_external_source (const char *function, const char *filename, const char *format, uint8_t priority, uint32_t lineno, uint32_t tags,...) __attribute__((format(printf |
This function is to import logs from other code (like libraries) that provide a callback with their logs. | |
void struct qb_log_callsite * | qb_log_callsite_get (const char *function, const char *filename, const char *format, uint8_t priority, uint32_t lineno, uint32_t tags) |
Get or create a callsite at the given position. | |
void | qb_log_from_external_source_va (const char *function, const char *filename, const char *format, uint8_t priority, uint32_t lineno, uint32_t tags, va_list ap) __attribute__((format(printf |
void | qb_log_init (const char *name, int32_t facility, uint8_t priority) |
Init the logging system. | |
void | qb_log_fini (void) |
Logging system finalization function. | |
int32_t | qb_log_callsites_register (struct qb_log_callsite *_start, struct qb_log_callsite *_stop) |
If you are using dynamically loadable modules via dlopen() and you load them after qb_log_init() then after you load the module you will need to do the following to get the filters to work in that module:. | |
void | qb_log_callsites_dump (void) |
Dump the callsite info to stdout. | |
int32_t | qb_log_ctl (int32_t target, enum qb_log_conf conf_type, int32_t arg) |
Main logging control function. | |
int32_t | qb_log_ctl2 (int32_t target, enum qb_log_conf conf_type, qb_log_ctl2_arg_t arg) |
Extension of main logging control function accepting also strings. | |
int32_t | qb_log_filter_ctl (int32_t value, enum qb_log_filter_conf c, enum qb_log_filter_type type, const char *text, uint8_t low_priority) |
This allows you modify the 'tags' and 'targets' callsite fields at runtime. | |
int32_t | qb_log_filter_ctl2 (int32_t value, enum qb_log_filter_conf c, enum qb_log_filter_type type, const char *text, uint8_t high_priority, uint8_t low_priority) |
This extends qb_log_filter_ctl() by been able to provide a high_priority. | |
int32_t | qb_log_filter_fn_set (qb_log_filter_fn fn) |
Instead of using the qb_log_filter_ctl() functions you can apply the filters manually by defining a callback and setting the targets field using qb_bit_set() and qb_bit_clear() like the following below:. | |
void | qb_log_tags_stringify_fn_set (qb_log_tags_stringify_fn fn) |
Set the callback to map the 'tags' bit map to a string. | |
void | qb_log_format_set (int32_t t, const char *format) |
Set the format specifiers. | |
int32_t | qb_log_file_open (const char *filename) |
Open a log file. | |
void | qb_log_file_close (int32_t t) |
Close a log file and release is resources. | |
int32_t | qb_log_thread_priority_set (int32_t policy, int32_t priority) |
When using threaded logging set the pthread policy and priority. | |
int32_t | qb_log_thread_start (void) |
Start the logging pthread. | |
ssize_t | qb_log_blackbox_write_to_file (const char *filename) |
Write the blackbox to file. | |
void | qb_log_blackbox_print_from_file (const char *filename) |
Read the blackbox for file and print it out. | |
int32_t | qb_log_custom_open (qb_log_logger_fn log_fn, qb_log_close_fn close_fn, qb_log_reload_fn reload_fn, void *user_data) |
Open a custom log target. | |
void | qb_log_custom_close (int32_t t) |
Close a custom log target and release is resources. | |
void * | qb_log_target_user_data_get (int32_t t) |
Retrieve the user data set by either qb_log_custom_open or qb_log_target_user_data_set. | |
int32_t | qb_log_target_user_data_set (int32_t t, void *user_data) |
Associate user data with this log target. | |
void | qb_log_target_format (int32_t target, struct qb_log_callsite *cs, time_t timestamp, const char *formatted_message, char *output_buffer) |
format the callsite and timestamp info according to the format set using qb_log_format_set() It is intended to be used from your custom logger function. | |
int32_t | qb_log_facility2int (const char *fname) |
Convert string "auth" to equivalent number "LOG_AUTH" etc. | |
const char * | qb_log_facility2str (int32_t fnum) |
Convert number "LOG_AUTH" to equivalent string "auth" etc. | |
Variables | |
struct qb_log_callsite | aligned |
An instance of this structure is created in a special ELF section at every dynamic debug callsite. | |
struct qb_log_callsite | __start___verbose [] |
struct qb_log_callsite | __stop___verbose [] |
The logging API provides four main parts (basics, filtering, threading & blackbox).
The idea behind this logging system is not to be prescriptive but to provide a set of tools to help the developer achieve what they want quickly and easily.
Simplest possible use:
main() { qb_log_init("simple-log", LOG_DAEMON, LOG_INFO); // ... qb_log(LOG_WARNING, "watch out"); // ... qb_log_fini(); }
To enable a target do the following:
syslog, stderr, the blackbox, and stdout are static (they don't need to be created, just enabled or disabled). However you can open multiple logfiles (QB_LOG_TARGET_MAX - QB_LOG_TARGET_STATIC_MAX). To do this, use the following code:
mytarget = qb_log_file_open("/var/log/mylogfile"); qb_log_ctl(mytarget, QB_LOG_CONF_ENABLED, QB_TRUE);
Once your targets are enabled/opened you can configure them as follows: Configure the size of blackbox
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10);
Make logging to file threaded:
qb_log_ctl(mytarget, QB_LOG_CONF_THREADED, QB_TRUE);
To workaround your syslog daemon filtering all messages > LOG_INFO
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_INFO - LOG_DEBUG);
To ensure all logs to file targets are fsync'ed (default QB_FALSE)
qb_log_ctl(mytarget, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
So to make all logs from evil_function() go to stderr, do the following:
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "evil_function", LOG_TRACE);
So to make all logs from totem* (with a priority <= LOG_INFO) go to stderr, do the following:
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "totem", LOG_INFO);
So to make all logs with the substring "ringbuffer" go to stderr, do the following:
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT, "ringbuffer", LOG_TRACE);
To achieve non-blocking logging, so that any calls to write() or syslog() will not hold up your program, you can use threaded logging as well.
Threaded logging use:
main() { qb_log_init("simple-log", LOG_DAEMON, LOG_INFO); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, QB_TRUE); // ... daemonize(); // call this after you fork() qb_log_thread_start(); // ... qb_log(LOG_WARNING, "watch out"); // ... qb_log_fini(); }
Blackbox usage:
static void sigsegv_handler(int sig) { (void)signal (SIGSEGV, SIG_DFL); qb_log_blackbox_write_to_file("simple-log.fdata"); qb_log_fini(); raise(SIGSEGV); } main() { signal(SIGSEGV, sigsegv_handler); qb_log_init("simple-log", LOG_DAEMON, LOG_INFO); qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_DEBUG); qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10); qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); // ... qb_log(LOG_WARNING, "watch out"); // ... qb_log_fini(); }
const char* my_tags_stringify(uint32_t tags) { if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT) { return "libqb"; } else if (tags == 3) { return "three"; } else { return "MAIN"; } } main() { // ... qb_log_tags_stringify_fn_set(my_tags_stringify); qb_log_format_set(QB_LOG_STDERR, "[%5g] %p %b"); // ... qb_logt(LOG_INFO, 3, "hello"); qb_logt(LOG_INFO, 0, "hello"); }
The code above will produce:
[libqb] some message [three] info hello [MAIN ] info hello
#define LOG_TRACE (LOG_DEBUG + 1) |
#define qb_enter | ( | ) | qb_log(LOG_TRACE, "ENTERING %s()", __func__) |
#define qb_leave | ( | ) | qb_log(LOG_TRACE, "LEAVING %s()", __func__) |
#define qb_log | ( | priority, | |||
fmt, | |||||
args... | ) | qb_logt(priority, 0, fmt, ##args) |
This is the main function to generate a log message.
priority | this takes syslog priorities. | |
fmt | usual printf style format specifiers | |
args | usual printf style args |
#define QB_LOG_CTL2_I32 | ( | a | ) | ((qb_log_ctl2_arg_t) { .i32 = (a) }) |
#define QB_LOG_CTL2_S | ( | a | ) | ((qb_log_ctl2_arg_t) { .s = (a) }) |
#define QB_LOG_INIT_DATA | ( | name | ) |
void name(void); \ void name(void) { if (__start___verbose != __stop___verbose) {assert(1);} } \ void __attribute__ ((constructor)) name(void);
#define QB_LOG_MAX_LEN 512 |
#define QB_LOG_STRERROR_MAX_LEN 128 |
#define QB_LOG_TAG_LIBQB_MSG (1 << QB_LOG_TAG_LIBQB_MSG_BIT) |
#define QB_LOG_TAG_LIBQB_MSG_BIT 31 |
#define qb_logt | ( | priority, | |||
tags, | |||||
fmt, | |||||
args... | ) |
do { \ static struct qb_log_callsite descriptor \ __attribute__((section("__verbose"), aligned(8))) = \ { __func__, __FILE__, fmt, priority, __LINE__, 0, tags }; \ qb_log_real_(&descriptor, ##args); \ } while(0)
This is the function to generate a log message if you want to manually add tags.
priority | this takes syslog priorities. | |
tags | this is a uint32_t that you can use with qb_log_tags_stringify_fn_set() to "tag" a log message with a feature or sub-system then you can use "%g" in the format specifer to print it out. | |
fmt | usual printf style format specifiers | |
args | usual printf style args |
#define qb_perror | ( | priority, | |||
fmt, | |||||
args... | ) |
do { \ char _perr_buf_[QB_LOG_STRERROR_MAX_LEN]; \ const char *_perr_str_ = qb_strerror_r(errno, _perr_buf_, sizeof(_perr_buf_)); \ qb_logt(priority, 0, fmt ": %s (%d)", ##args, _perr_str_, errno); \ } while(0)
This is similar to perror except it goes into the logging system.
priority | this takes syslog priorities. | |
fmt | usual printf style format specifiers | |
args | usual printf style args |
#define QB_XC '\a' |
#define QB_XS "\a" |
typedef void(* qb_log_close_fn)(int32_t t) |
typedef void(* qb_log_filter_fn)(struct qb_log_callsite *cs) |
typedef void(* qb_log_logger_fn)(int32_t t, struct qb_log_callsite *cs, time_t timestamp, const char *msg) |
typedef void(* qb_log_reload_fn)(int32_t t) |
typedef const char*(* qb_log_tags_stringify_fn)(uint32_t tags) |
typedef void(* qb_log_vlogger_fn)(int32_t t, struct qb_log_callsite *cs, time_t timestamp, va_list ap) |
enum qb_log_conf |
enum qb_log_filter_conf |
enum qb_log_filter_type |
enum qb_log_target_slot |
enum qb_log_target_state |
void qb_log_blackbox_print_from_file | ( | const char * | filename | ) |
Read the blackbox for file and print it out.
ssize_t qb_log_blackbox_write_to_file | ( | const char * | filename | ) |
Write the blackbox to file.
void struct qb_log_callsite* qb_log_callsite_get | ( | const char * | function, | |
const char * | filename, | |||
const char * | format, | |||
uint8_t | priority, | |||
uint32_t | lineno, | |||
uint32_t | tags | |||
) | [read] |
Get or create a callsite at the given position.
The result can then be passed into qb_log_real_()
function | originating function name | |
filename | originating filename | |
format | format string | |
priority | this takes syslog priorities. | |
lineno | file line number | |
tags | the tag |
void qb_log_callsites_dump | ( | void | ) |
Dump the callsite info to stdout.
int32_t qb_log_callsites_register | ( | struct qb_log_callsite * | _start, | |
struct qb_log_callsite * | _stop | |||
) |
If you are using dynamically loadable modules via dlopen() and you load them after qb_log_init() then after you load the module you will need to do the following to get the filters to work in that module:.
_start = dlsym (dl_handle, "__start___verbose"); _stop = dlsym (dl_handle, "__stop___verbose"); qb_log_callsites_register(_start, _stop);
int32_t qb_log_ctl | ( | int32_t | target, | |
enum qb_log_conf | conf_type, | |||
int32_t | arg | |||
) |
Main logging control function.
target | QB_LOG_SYSLOG, QB_LOG_STDERR or result from qb_log_file_open() | |
conf_type | configuration directive ("what to configure") that accepts int32_t argument determining the new value unless ignored for particular directive altogether (incompatible directives: QB_LOG_CONF_IDENT) | |
arg | the new value for a state-changing configuration directive, ignored otherwise |
-errno | on error | |
0 | on success | |
qb_log_target_state | for QB_LOG_CONF_STATE_GET |
int32_t qb_log_ctl2 | ( | int32_t | target, | |
enum qb_log_conf | conf_type, | |||
qb_log_ctl2_arg_t | arg | |||
) |
Extension of main logging control function accepting also strings.
target | QB_LOG_SYSLOG, QB_LOG_STDERR or result from qb_log_file_open() | |
conf_type | configuration directive ("what to configure") that accepts either int32_t or a null-terminated string argument determining the new value unless ignored for particular directive (compatible directives: those valid for qb_log_ctl + QB_LOG_CONF_IDENT) | |
arg | the new value for a state-changing configuration directive, ignored otherwise; for QB_LOG_CONF_IDENT, 's' member as new identifier to openlog(), for all qb_log_ctl-compatible ones, 'i32' member is assumed (although a preferred way is to use that original function directly as it allows for more type safety) |
QB_LOG_CTL2_I32
and QB_LOG_CTL2_S
macros for a convenient on-the-fly construction of the object to be passed as an arg
argument. void qb_log_custom_close | ( | int32_t | t | ) |
Close a custom log target and release is resources.
int32_t qb_log_custom_open | ( | qb_log_logger_fn | log_fn, | |
qb_log_close_fn | close_fn, | |||
qb_log_reload_fn | reload_fn, | |||
void * | user_data | |||
) |
Open a custom log target.
-errno | on error | |
value | in inclusive range QB_LOG_TARGET_DYNAMIC_START to QB_LOG_TARGET_DYNAMIC_END (to be passed into other qb_log_* functions) |
int32_t qb_log_facility2int | ( | const char * | fname | ) |
Convert string "auth" to equivalent number "LOG_AUTH" etc.
const char* qb_log_facility2str | ( | int32_t | fnum | ) |
Convert number "LOG_AUTH" to equivalent string "auth" etc.
void qb_log_file_close | ( | int32_t | t | ) |
Close a log file and release is resources.
int32_t qb_log_file_open | ( | const char * | filename | ) |
Open a log file.
-errno | on error | |
value | in inclusive range QB_LOG_TARGET_DYNAMIC_START to QB_LOG_TARGET_DYNAMIC_END (to be passed into other qb_log_* functions) |
int32_t qb_log_filter_ctl | ( | int32_t | value, | |
enum qb_log_filter_conf | c, | |||
enum qb_log_filter_type | type, | |||
const char * | text, | |||
uint8_t | low_priority | |||
) |
This allows you modify the 'tags' and 'targets' callsite fields at runtime.
int32_t qb_log_filter_ctl2 | ( | int32_t | value, | |
enum qb_log_filter_conf | c, | |||
enum qb_log_filter_type | type, | |||
const char * | text, | |||
uint8_t | high_priority, | |||
uint8_t | low_priority | |||
) |
This extends qb_log_filter_ctl() by been able to provide a high_priority.
int32_t qb_log_filter_fn_set | ( | qb_log_filter_fn | fn | ) |
Instead of using the qb_log_filter_ctl() functions you can apply the filters manually by defining a callback and setting the targets field using qb_bit_set() and qb_bit_clear() like the following below:.
static void m_filter(struct qb_log_callsite *cs) { if ((cs->priority >= LOG_ALERT && cs->priority <= LOG_DEBUG) && strcmp(cs->filename, "my_c_file.c") == 0) { qb_bit_set(cs->targets, QB_LOG_SYSLOG); } else { qb_bit_clear(cs->targets, QB_LOG_SYSLOG); } }
void qb_log_fini | ( | void | ) |
Logging system finalization function.
It releases any shared memory. Stops the logging thread if running. Flushes the last messages to their destinations.
void qb_log_format_set | ( | int32_t | t, | |
const char * | format | |||
) |
Set the format specifiers.
n FUNCTION NAME f FILENAME l FILELINE p PRIORITY t TIMESTAMP b BUFFER g TAGS N name (passed into qb_log_init) P PID H hostname
any number between % and character specify field length to pad or chop
void qb_log_from_external_source | ( | const char * | function, | |
const char * | filename, | |||
const char * | format, | |||
uint8_t | priority, | |||
uint32_t | lineno, | |||
uint32_t | tags, | |||
... | ||||
) |
This function is to import logs from other code (like libraries) that provide a callback with their logs.
function | originating function name | |
filename | originating filename | |
format | format string | |
priority | this takes syslog priorities. | |
lineno | file line number | |
tags | this is a uint32_t that you can use with qb_log_tags_stringify_fn_set() to "tag" a log message with a feature or sub-system then you can use "%g" in the format specifer to print it out. |
void qb_log_from_external_source_va | ( | const char * | function, | |
const char * | filename, | |||
const char * | format, | |||
uint8_t | priority, | |||
uint32_t | lineno, | |||
uint32_t | tags, | |||
va_list | ap | |||
) |
void qb_log_init | ( | const char * | name, | |
int32_t | facility, | |||
uint8_t | priority | |||
) |
Init the logging system.
name | will be passed into openlog() | |
facility | default for all new targets. | |
priority | a basic filter with this priority will be added. |
void qb_log_real_ | ( | struct qb_log_callsite * | cs, | |
... | ||||
) |
void qb_log_real_va_ | ( | struct qb_log_callsite * | cs, | |
va_list | ap | |||
) |
void qb_log_tags_stringify_fn_set | ( | qb_log_tags_stringify_fn | fn | ) |
Set the callback to map the 'tags' bit map to a string.
void qb_log_target_format | ( | int32_t | target, | |
struct qb_log_callsite * | cs, | |||
time_t | timestamp, | |||
const char * | formatted_message, | |||
char * | output_buffer | |||
) |
format the callsite and timestamp info according to the format set using qb_log_format_set() It is intended to be used from your custom logger function.
void* qb_log_target_user_data_get | ( | int32_t | t | ) |
Retrieve the user data set by either qb_log_custom_open or qb_log_target_user_data_set.
int32_t qb_log_target_user_data_set | ( | int32_t | t, | |
void * | user_data | |||
) |
Associate user data with this log target.
int32_t qb_log_thread_priority_set | ( | int32_t | policy, | |
int32_t | priority | |||
) |
When using threaded logging set the pthread policy and priority.
-errno | on error | |
0 | success |
int32_t qb_log_thread_start | ( | void | ) |
Start the logging pthread.
struct qb_log_callsite __start___verbose[] |
struct qb_log_callsite __stop___verbose[] |
struct qb_log_callsite aligned |
An instance of this structure is created in a special ELF section at every dynamic debug callsite.
At runtime, the special section is treated as an array of these.