.TH "qblog.h" 3 "Fri Apr 26 2019" "Version 1.0.5" "libqb" \" -*- nroff -*- .ad l .nh .SH NAME qblog.h \- The logging API provides four main parts (basics, filtering, threading & blackbox)\&. .SH SYNOPSIS .br .PP \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br \fC#include \fP .br .SS "Data Structures" .in +1c .ti -1c .RI "struct \fBqb_log_callsite\fP" .br .RI "An instance of this structure is created in a special ELF section at every dynamic debug callsite\&. " .ti -1c .RI "union \fBqb_log_ctl2_arg_t\fP" .br .in -1c .SS "Macros" .in +1c .ti -1c .RI "#define \fBLOG_TRACE\fP (LOG_DEBUG + 1)" .br .ti -1c .RI "#define \fBQB_LOG_MAX_LEN\fP 512" .br .ti -1c .RI "#define \fBQB_LOG_STRERROR_MAX_LEN\fP 128" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION\fP __verbose /* conforms to C ident\&. */" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION_STR\fP \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION\fP)" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION_START\fP \fBQB_PP_JOIN\fP(__start_, \fBQB_ATTR_SECTION\fP)" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION_STOP\fP \fBQB_PP_JOIN\fP(__stop_, \fBQB_ATTR_SECTION\fP)" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION_START_STR\fP \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION_START\fP)" .br .ti -1c .RI "#define \fBQB_ATTR_SECTION_STOP_STR\fP \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION_STOP\fP)" .br .ti -1c .RI "#define \fBQB_NONAPI_LOG_INIT_DATA_EXTRA_\fP(name)" .br .ti -1c .RI "#define \fBQB_LOG_INIT_DATA\fP(name)" .br .RI "Optional on-demand self-check of 1/ toolchain sanity (prerequisite for the logging subsystem to work properly) and 2/ non-void active use of logging (satisfied with a justifying existence of a logging callsite as defined with a \fCqb_logt\fP invocation) at the target (but see below), which is supposedly assured by it's author(!) as of relying on this very macro [technically, the symbols that happen to be resolved under the respective identifiers do not necessarily originate in the same compilation unit as when it's not the end executable (or by induction, a library positioned earlier in the symbol lookup order) but a shared library, the former takes a precedence unless that site comes short of exercising the logging, making its callsite section empty and, in turn, without such boundary symbols, hence making the resolution continue further in the lookup order -- despite fuzzily targeted attestation, the check remains reasonable]; only effective when link-time ('run-time amortizing') callsite collection is; as a side effect, it can ensure the boundary-denoting symbols for the target collection area are kept alive with some otherwise unkind linkers\&. " .ti -1c .RI "#define \fBQB_LOG_TAG_LIBQB_MSG_BIT\fP 31" .br .ti -1c .RI "#define \fBQB_LOG_TAG_LIBQB_MSG\fP (1U << QB_LOG_TAG_LIBQB_MSG_BIT)" .br .ti -1c .RI "#define \fBqb_logt\fP(\fBpriority\fP, \fBtags\fP, fmt, args\&.\&.\&.)" .br .RI "This is the function to generate a log message if you want to manually add tags\&. " .ti -1c .RI "#define \fBqb_log\fP(\fBpriority\fP, fmt, args\&.\&.\&.) \fBqb_logt\fP(\fBpriority\fP, 0, fmt, ##args)" .br .RI "This is the main function to generate a log message\&. " .ti -1c .RI "#define \fBQB_XC\fP '\\a'" .br .ti -1c .RI "#define \fBQB_XS\fP '\\a'" .br .ti -1c .RI "#define \fBqb_perror\fP(\fBpriority\fP, fmt, args\&.\&.\&.)" .br .RI "This is similar to perror except it goes into the logging system\&. " .ti -1c .RI "#define \fBqb_enter\fP() \fBqb_log\fP(\fBLOG_TRACE\fP, 'ENTERING %s()', __func__)" .br .ti -1c .RI "#define \fBqb_leave\fP() \fBqb_log\fP(\fBLOG_TRACE\fP, 'LEAVING %s()', __func__)" .br .ti -1c .RI "#define \fBQB_LOG_CTL2_I32\fP(a) ((\fBqb_log_ctl2_arg_t\fP) { \&.i32 = (a) })" .br .ti -1c .RI "#define \fBQB_LOG_CTL2_S\fP(a) ((\fBqb_log_ctl2_arg_t\fP) { \&.s = (a) })" .br .in -1c .SS "Typedefs" .in +1c .ti -1c .RI "typedef const char *(* \fBqb_log_tags_stringify_fn\fP) (uint32_t \fBtags\fP)" .br .ti -1c .RI "typedef void(* \fBqb_log_filter_fn\fP) (struct \fBqb_log_callsite\fP *cs)" .br .ti -1c .RI "typedef void(* \fBqb_log_logger_fn\fP) (int32_t t, struct \fBqb_log_callsite\fP *cs, time_t timestamp, const char *msg)" .br .ti -1c .RI "typedef void(* \fBqb_log_vlogger_fn\fP) (int32_t t, struct \fBqb_log_callsite\fP *cs, time_t timestamp, va_list ap)" .br .ti -1c .RI "typedef void(* \fBqb_log_close_fn\fP) (int32_t t)" .br .ti -1c .RI "typedef void(* \fBqb_log_reload_fn\fP) (int32_t t)" .br .in -1c .SS "Enumerations" .in +1c .ti -1c .RI "enum \fBqb_log_target_slot\fP { \fBQB_LOG_TARGET_START\fP, \fBQB_LOG_TARGET_STATIC_START\fP = QB_LOG_TARGET_START, \fBQB_LOG_SYSLOG\fP = QB_LOG_TARGET_STATIC_START, \fBQB_LOG_STDERR\fP, \fBQB_LOG_BLACKBOX\fP, \fBQB_LOG_STDOUT\fP, \fBQB_LOG_TARGET_STATIC_MAX\fP, \fBQB_LOG_TARGET_STATIC_END\fP = QB_LOG_TARGET_STATIC_MAX - 1, \fBQB_LOG_TARGET_DYNAMIC_START\fP = QB_LOG_TARGET_STATIC_MAX, \fBQB_LOG_TARGET_MAX\fP = 32, \fBQB_LOG_TARGET_DYNAMIC_END\fP = QB_LOG_TARGET_MAX - 1, \fBQB_LOG_TARGET_END\fP = QB_LOG_TARGET_DYNAMIC_END }" .br .ti -1c .RI "enum \fBqb_log_target_state\fP { \fBQB_LOG_STATE_UNUSED\fP = 1, \fBQB_LOG_STATE_DISABLED\fP = 2, \fBQB_LOG_STATE_ENABLED\fP = 3 }" .br .ti -1c .RI "enum \fBqb_log_conf\fP { \fBQB_LOG_CONF_ENABLED\fP, \fBQB_LOG_CONF_FACILITY\fP, \fBQB_LOG_CONF_DEBUG\fP, \fBQB_LOG_CONF_SIZE\fP, \fBQB_LOG_CONF_THREADED\fP, \fBQB_LOG_CONF_PRIORITY_BUMP\fP, \fBQB_LOG_CONF_STATE_GET\fP, \fBQB_LOG_CONF_FILE_SYNC\fP, \fBQB_LOG_CONF_EXTENDED\fP, \fBQB_LOG_CONF_IDENT\fP }" .br .ti -1c .RI "enum \fBqb_log_filter_type\fP { \fBQB_LOG_FILTER_FILE\fP, \fBQB_LOG_FILTER_FUNCTION\fP, \fBQB_LOG_FILTER_FORMAT\fP, \fBQB_LOG_FILTER_FILE_REGEX\fP, \fBQB_LOG_FILTER_FUNCTION_REGEX\fP, \fBQB_LOG_FILTER_FORMAT_REGEX\fP }" .br .ti -1c .RI "enum \fBqb_log_filter_conf\fP { \fBQB_LOG_FILTER_ADD\fP, \fBQB_LOG_FILTER_REMOVE\fP, \fBQB_LOG_FILTER_CLEAR_ALL\fP, \fBQB_LOG_TAG_SET\fP, \fBQB_LOG_TAG_CLEAR\fP, \fBQB_LOG_TAG_CLEAR_ALL\fP }" .br .in -1c .SS "Functions" .in +1c .ti -1c .RI "struct \fBqb_log_callsite\fP \fB__attribute__\fP ((aligned(8)))" .br .ti -1c .RI "void \fBqb_log_real_\fP (struct \fBqb_log_callsite\fP *cs,\&.\&.\&.)" .br .RI "Internal function: use \fBqb_log()\fP or \fBqb_logt()\fP " .ti -1c .RI "void \fBqb_log_real_va_\fP (struct \fBqb_log_callsite\fP *cs, va_list ap)" .br .ti -1c .RI "void \fBqb_log_from_external_source\fP (const char *\fBfunction\fP, const char *\fBfilename\fP, const char *\fBformat\fP, uint8_t \fBpriority\fP, uint32_t \fBlineno\fP, uint32_t \fBtags\fP,\&.\&.\&.) \fB__attribute__\fP((\fBformat\fP(printf" .br .RI "This function is to import logs from other code (like libraries) that provide a callback with their logs\&. " .ti -1c .RI "void struct \fBqb_log_callsite\fP * \fBqb_log_callsite_get\fP (const char *\fBfunction\fP, const char *\fBfilename\fP, const char *\fBformat\fP, uint8_t \fBpriority\fP, uint32_t \fBlineno\fP, uint32_t \fBtags\fP)" .br .RI "Get or create a callsite at the given position\&. " .ti -1c .RI "void \fBqb_log_from_external_source_va\fP (const char *\fBfunction\fP, const char *\fBfilename\fP, const char *\fBformat\fP, uint8_t \fBpriority\fP, uint32_t \fBlineno\fP, uint32_t \fBtags\fP, va_list ap) \fB__attribute__\fP((\fBformat\fP(printf" .br .ti -1c .RI "void \fBqb_log_init\fP (const char *name, int32_t facility, uint8_t \fBpriority\fP)" .br .RI "Init the logging system\&. " .ti -1c .RI "void \fBqb_log_fini\fP (void)" .br .RI "Logging system finalization function\&. " .ti -1c .RI "int32_t \fBqb_log_callsites_register\fP (struct \fBqb_log_callsite\fP *_start, struct \fBqb_log_callsite\fP *_stop)" .br .RI "If you are using dynamically loadable modules via dlopen() and you load them after \fBqb_log_init()\fP then after you load the module you will need to do the following to get the filters to work in that module: " .ti -1c .RI "void \fBqb_log_callsites_dump\fP (void)" .br .RI "Dump the callsite info to stdout\&. " .ti -1c .RI "int32_t \fBqb_log_ctl\fP (int32_t target, enum \fBqb_log_conf\fP conf_type, int32_t arg)" .br .RI "Main logging control function\&. " .ti -1c .RI "int32_t \fBqb_log_ctl2\fP (int32_t target, enum \fBqb_log_conf\fP conf_type, \fBqb_log_ctl2_arg_t\fP arg)" .br .RI "Extension of main logging control function accepting also strings\&. " .ti -1c .RI "int32_t \fBqb_log_filter_ctl\fP (int32_t value, enum \fBqb_log_filter_conf\fP c, enum \fBqb_log_filter_type\fP type, const char *text, uint8_t low_priority)" .br .RI "This allows you modify the 'tags' and 'targets' callsite fields at runtime\&. " .ti -1c .RI "int32_t \fBqb_log_filter_ctl2\fP (int32_t value, enum \fBqb_log_filter_conf\fP c, enum \fBqb_log_filter_type\fP type, const char *text, uint8_t high_priority, uint8_t low_priority)" .br .RI "This extends \fBqb_log_filter_ctl()\fP by been able to provide a high_priority\&. " .ti -1c .RI "int32_t \fBqb_log_filter_fn_set\fP (\fBqb_log_filter_fn\fP fn)" .br .RI "Instead of using the \fBqb_log_filter_ctl()\fP functions you can apply the filters manually by defining a callback and setting the targets field using \fBqb_bit_set()\fP and \fBqb_bit_clear()\fP like the following below: " .ti -1c .RI "void \fBqb_log_tags_stringify_fn_set\fP (\fBqb_log_tags_stringify_fn\fP fn)" .br .RI "Set the callback to map the 'tags' bit map to a string\&. " .ti -1c .RI "void \fBqb_log_format_set\fP (int32_t t, const char *\fBformat\fP)" .br .RI "Set the format specifiers\&. " .ti -1c .RI "int32_t \fBqb_log_file_open\fP (const char *\fBfilename\fP)" .br .RI "Open a log file\&. " .ti -1c .RI "void \fBqb_log_file_close\fP (int32_t t)" .br .RI "Close a log file and release is resources\&. " .ti -1c .RI "int32_t \fBqb_log_thread_priority_set\fP (int32_t policy, int32_t \fBpriority\fP)" .br .RI "When using threaded logging set the pthread policy and priority\&. " .ti -1c .RI "int32_t \fBqb_log_thread_start\fP (void)" .br .RI "Start the logging pthread\&. " .ti -1c .RI "ssize_t \fBqb_log_blackbox_write_to_file\fP (const char *\fBfilename\fP)" .br .RI "Write the blackbox to file\&. " .ti -1c .RI "void \fBqb_log_blackbox_print_from_file\fP (const char *\fBfilename\fP)" .br .RI "Read the blackbox for file and print it out\&. " .ti -1c .RI "int32_t \fBqb_log_custom_open\fP (\fBqb_log_logger_fn\fP log_fn, \fBqb_log_close_fn\fP close_fn, \fBqb_log_reload_fn\fP reload_fn, void *user_data)" .br .RI "Open a custom log target\&. " .ti -1c .RI "void \fBqb_log_custom_close\fP (int32_t t)" .br .RI "Close a custom log target and release is resources\&. " .ti -1c .RI "void * \fBqb_log_target_user_data_get\fP (int32_t t)" .br .RI "Retrieve the user data set by either qb_log_custom_open or qb_log_target_user_data_set\&. " .ti -1c .RI "int32_t \fBqb_log_target_user_data_set\fP (int32_t t, void *user_data)" .br .RI "Associate user data with this log target\&. " .ti -1c .RI "void \fBqb_log_target_format\fP (int32_t target, struct \fBqb_log_callsite\fP *cs, time_t timestamp, const char *formatted_message, char *output_buffer)" .br .RI "Format the callsite and timestamp info according to the format\&. " .ti -1c .RI "int32_t \fBqb_log_facility2int\fP (const char *fname)" .br .RI "Convert string 'auth' to equivalent number 'LOG_AUTH' etc\&. " .ti -1c .RI "const char * \fBqb_log_facility2str\fP (int32_t fnum)" .br .RI "Convert number 'LOG_AUTH' to equivalent string 'auth' etc\&. " .in -1c .SS "Variables" .in +1c .ti -1c .RI "const char * \fBfunction\fP" .br .ti -1c .RI "const char * \fBfilename\fP" .br .ti -1c .RI "const char * \fBformat\fP" .br .ti -1c .RI "uint8_t \fBpriority\fP" .br .ti -1c .RI "uint32_t \fBlineno\fP" .br .ti -1c .RI "uint32_t \fBtargets\fP" .br .ti -1c .RI "uint32_t \fBtags\fP" .br .ti -1c .RI "struct \fBqb_log_callsite\fP \fBQB_ATTR_SECTION_START\fP []" .br .ti -1c .RI "struct \fBqb_log_callsite\fP \fBQB_ATTR_SECTION_STOP\fP []" .br .ti -1c .RI "enum \fBqb_log_target_slot\fP \fB__attribute__\fP" .br .in -1c .SH "Detailed Description" .PP 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\&. .PP \fBBasic logging API\&.\fP .RS 4 Call \fBqb_log()\fP to generate a log message\&. Then to write the message somewhere meaningful call \fBqb_log_ctl()\fP to configure the targets\&. .RE .PP Simplest possible use: .PP .nf main() { qb_log_init("simple-log", LOG_DAEMON, LOG_INFO); // \&.\&.\&. qb_log(LOG_WARNING, "watch out"); // \&.\&.\&. qb_log_fini(); } .fi .PP .PP \fBNote:\fP .RS 4 In practice, such a minimalistic approach hardly caters real use cases\&. Following section discusses the customization\&. Moreover when employing the log module is bound to its active use (some log messages are assuredly emitted within the target compilation unit), it's quite vital to instrument the target side with \fC\fBQB_LOG_INIT_DATA()\fP\fP macro placed in the top file scope in exactly one source file (preferably the main one) to be mixed into the resulting compilation unit\&. This is a self-defensive measure for when the linker-assisted collection of callsite data silently fails, which could otherwise go unnoticed, causing troubles down the road, but alas it cannot discern misuse of \fC\fBQB_LOG_INIT_DATA()\fP\fP macro in no-logging context from broken callsite section handling assumptions owing to overboard fancy linker -- situation that the self-check aims to detect in the first place\&. .RE .PP \fBConfiguring log targets\&.\fP .RS 4 A log target can be syslog, stderr, the blackbox, stdout, or a text file\&. By default, only syslog is enabled\&. While this is customary for daemons, it is rarely appropriate for ordinary programs, which should promptly disable that when other targets (read on) are to be utilized: .PP .nf qb_log_ctl(B_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); .fi .PP .RE .PP To enable a target do the following: .PP .nf qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); .fi .PP .PP 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 (falling within inclusive range \fCQB_LOG_TARGET_DYNAMIC_START\fP up to \fCQB_LOG_TARGET_DYNAMIC_END\fP)\&. To do this, use the following code: .PP .nf mytarget = qb_log_file_open("/var/log/mylogfile"); qb_log_ctl(mytarget, QB_LOG_CONF_ENABLED, QB_TRUE); .fi .PP .PP Once your targets are enabled/opened, you can configure them as follows: Configure the size of blackbox: .PP .nf qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10); .fi .PP .PP Make logging to file threaded: .PP .nf qb_log_ctl(mytarget, QB_LOG_CONF_THREADED, QB_TRUE); .fi .PP .PP Sometimes, syslog daemons are (pre)configured to filter messages not exceeding a particular priority\&. When this happens to be the logging target, the designated priority of the message is passed along unchanged, possibly resulting in message loss\&. For messages up to \fCLOG_DEBUG\fP importance, this can be worked around by proportionally bumping the priorities to be passed to syslog (here, the step is such that \fCLOG_DEBUG\fP gets promoted to \fCLOG_INFO\fP): .PP .nf qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_INFO - LOG_DEBUG); .fi .PP .PP To ensure all logs to file targets are fsync'ed (new messages expressly transferred to the storage device as they keep coming, otherwise defaults to \fCQB_FALSE\fP): .PP .nf qb_log_ctl(mytarget, QB_LOG_CONF_FILE_SYNC, QB_TRUE); .fi .PP .PP \fBFiltering messages\&.\fP .RS 4 To have more power over what log messages go to which target you can apply filters to the targets\&. What happens is the desired callsites have the correct bit set\&. Then when the log message is generated it gets sent to the targets based on which bit is set in the callsite's 'target' bitmap\&. Messages can be filtered based on the: .IP "1." 4 filename + priority .IP "2." 4 function name + priority .IP "3." 4 format string + priority .PP .RE .PP So to make all logs from evil_function() go to stderr, do the following: .PP .nf qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "evil_function", LOG_TRACE); .fi .PP .PP So to make all logs from totem* (with a priority <= LOG_INFO) go to stderr, do the following: .PP .nf qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "totem", LOG_INFO); .fi .PP .PP So to make all logs with the substring 'ringbuffer' go to stderr, do the following: .PP .nf qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT, "ringbuffer", LOG_TRACE); .fi .PP .PP \fBThread safe non-blocking logging\&.\fP .RS 4 Logging is only thread safe when threaded logging is in use\&. If you plan on logging from multiple threads, you must initialize libqb's logger thread and use qb_log_filter_ctl to set the QB_LOG_CONF_THREADED flag on all the logging targets in use\&. .RE .PP 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\&. .PP Threaded logging use: .PP .nf 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(); } .fi .PP .PP \fBA blackbox for in-field diagnosis\&.\fP .RS 4 This stores log messages in a ringbuffer so they can be written to file if the program crashes (you will need to catch SIGSEGV)\&. These can then be easily printed out later\&. .RE .PP \fBNote:\fP .RS 4 the blackbox is not enabled by default\&. .RE .PP Blackbox usage: .PP .nf 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(); } .fi .PP .PP \fBTagging messages\&.\fP .RS 4 You can tag messages using the second argument to \fBqb_logt()\fP or by using \fBqb_log_filter_ctl()\fP\&. This can be used to add feature or sub-system information to the logs\&. .RE .PP .PP .nf 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"); } .fi .PP The code above will produce: .PP .nf [libqb] some message [three] info hello [MAIN ] info hello .fi .PP .SH "Macro Definition Documentation" .PP .SS "#define LOG_TRACE (LOG_DEBUG + 1)" .SS "#define QB_ATTR_SECTION __verbose /* conforms to C ident\&. */" .SS "#define QB_ATTR_SECTION_START \fBQB_PP_JOIN\fP(__start_, \fBQB_ATTR_SECTION\fP)" .SS "#define QB_ATTR_SECTION_START_STR \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION_START\fP)" .SS "#define QB_ATTR_SECTION_STOP \fBQB_PP_JOIN\fP(__stop_, \fBQB_ATTR_SECTION\fP)" .SS "#define QB_ATTR_SECTION_STOP_STR \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION_STOP\fP)" .SS "#define QB_ATTR_SECTION_STR \fBQB_PP_STRINGIFY\fP(\fBQB_ATTR_SECTION\fP)" .SS "#define qb_enter() \fBqb_log\fP(\fBLOG_TRACE\fP, 'ENTERING %s()', __func__)" .SS "#define qb_leave() \fBqb_log\fP(\fBLOG_TRACE\fP, 'LEAVING %s()', __func__)" .SS "#define qb_log(\fBpriority\fP, fmt, args\&.\&.\&.) \fBqb_logt\fP(\fBpriority\fP, 0, fmt, ##args)" .PP This is the main function to generate a log message\&. .PP \fBParameters:\fP .RS 4 \fIpriority\fP this takes syslog priorities\&. .br \fIfmt\fP usual printf style format specifiers .br \fIargs\fP usual printf style args .RE .PP .SS "#define QB_LOG_CTL2_I32(a) ((\fBqb_log_ctl2_arg_t\fP) { \&.i32 = (a) })" .SS "#define QB_LOG_CTL2_S(a) ((\fBqb_log_ctl2_arg_t\fP) { \&.s = (a) })" .SS "#define QB_LOG_INIT_DATA(name)" \fBValue:\fP .PP .nf void name(void); \ void name(void) { \ void *work_handle; struct qb_log_callsite *work_s1, *work_s2; \ /* our own (target's) sanity, or possibly that of higher priority \ symbol resolution site (unless target equals end executable) \ or even the lower one if no such predecessor defines these */ \ if ((work_handle = dlopen(NULL, RTLD_LOCAL|RTLD_LAZY)) != NULL) { \ work_s1 = (struct qb_log_callsite *) \ dlsym(work_handle, QB_ATTR_SECTION_START_STR); \ work_s2 = (struct qb_log_callsite *) \ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \ assert("implicit callsite section is observable, otherwise \target's and/or libqb's build is at fault, preventing reliable logging" \ && work_s1 != NULL && work_s2 != NULL); \ dlclose(work_handle); /* perhaps overly eager thing to do */ } \ QB_NONAPI_LOG_INIT_DATA_EXTRA_(name); \ /* finally, original, straightforward check */ \ assert("implicit callsite section is populated, otherwise \target's build is at fault, preventing reliable logging" \ && QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } \ void __attribute__ ((constructor)) name(void); .fi .PP Optional on-demand self-check of 1/ toolchain sanity (prerequisite for the logging subsystem to work properly) and 2/ non-void active use of logging (satisfied with a justifying existence of a logging callsite as defined with a \fCqb_logt\fP invocation) at the target (but see below), which is supposedly assured by it's author(!) as of relying on this very macro [technically, the symbols that happen to be resolved under the respective identifiers do not necessarily originate in the same compilation unit as when it's not the end executable (or by induction, a library positioned earlier in the symbol lookup order) but a shared library, the former takes a precedence unless that site comes short of exercising the logging, making its callsite section empty and, in turn, without such boundary symbols, hence making the resolution continue further in the lookup order -- despite fuzzily targeted attestation, the check remains reasonable]; only effective when link-time ('run-time amortizing') callsite collection is; as a side effect, it can ensure the boundary-denoting symbols for the target collection area are kept alive with some otherwise unkind linkers\&. Applying this macro in the target program/library is strongly recommended whenever the logging as framed by this header file is in use\&. Moreover, it's important to state that using this check while not ensuring \fC_GNU_SOURCE\fP macro definition is present at compile-time means only half of the available sanity checking will be performed, possibly resulting in libqb's own internally logged messages being lost without warning\&. .SS "#define QB_LOG_MAX_LEN 512" .SS "#define QB_LOG_STRERROR_MAX_LEN 128" .SS "#define QB_LOG_TAG_LIBQB_MSG (1U << QB_LOG_TAG_LIBQB_MSG_BIT)" .SS "#define QB_LOG_TAG_LIBQB_MSG_BIT 31" .SS "#define qb_logt(\fBpriority\fP, \fBtags\fP, fmt, args\&.\&.\&.)" \fBValue:\fP .PP .nf do { \ static struct qb_log_callsite descriptor \ __attribute__((section(QB_ATTR_SECTION_STR), aligned(8))) = \ { __func__, __FILE__, fmt, priority, __LINE__, 0, tags }; \ qb_log_real_(&descriptor, ##args); \ } while(0) .fi .PP This is the function to generate a log message if you want to manually add tags\&. .PP \fBParameters:\fP .RS 4 \fIpriority\fP this takes syslog priorities\&. .br \fItags\fP this is a uint32_t that you can use with \fBqb_log_tags_stringify_fn_set()\fP to 'tag' a log message with a feature or sub-system then you can use '%g' in the format specifer to print it out\&. .br \fIfmt\fP usual printf style format specifiers .br \fIargs\fP usual printf style args .RE .PP .SS "#define QB_NONAPI_LOG_INIT_DATA_EXTRA_(name)" \fBValue:\fP .PP .nf _Pragma(QB_PP_STRINGIFY(GCC warning QB_PP_STRINGIFY( \ without "_GNU_SOURCE" defined (directly or not) \ QB_LOG_INIT_DATA cannot check sanity of libqb proper \ nor of the target site originating this check alone))) .fi .SS "#define qb_perror(\fBpriority\fP, fmt, args\&.\&.\&.)" \fBValue:\fP .PP .nf 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) .fi .PP This is similar to perror except it goes into the logging system\&. .PP \fBParameters:\fP .RS 4 \fIpriority\fP this takes syslog priorities\&. .br \fIfmt\fP usual printf style format specifiers .br \fIargs\fP usual printf style args .RE .PP \fBNote:\fP .RS 4 Because \fBqb_perror()\fP adds the system error message and error number onto the end of the given fmt, that information will become extended information if QB_XS is used inside fmt and will not show up in any logs that strip extended information\&. .RE .PP .SS "#define QB_XC '\\a'" .SS "#define QB_XS '\\a'" .SH "Typedef Documentation" .PP .SS "typedef void(* qb_log_close_fn) (int32_t t)" .SS "typedef void(* qb_log_filter_fn) (struct \fBqb_log_callsite\fP *cs)" .SS "typedef void(* qb_log_logger_fn) (int32_t t, struct \fBqb_log_callsite\fP *cs, time_t timestamp, const char *msg)" .SS "typedef void(* qb_log_reload_fn) (int32_t t)" .SS "typedef const char*(* qb_log_tags_stringify_fn) (uint32_t \fBtags\fP)" .SS "typedef void(* qb_log_vlogger_fn) (int32_t t, struct \fBqb_log_callsite\fP *cs, time_t timestamp, va_list ap)" .SH "Enumeration Type Documentation" .PP .SS "enum \fBqb_log_conf\fP" .PP \fBEnumerator\fP .in +1c .TP \fB\fIQB_LOG_CONF_ENABLED \fP\fP .TP \fB\fIQB_LOG_CONF_FACILITY \fP\fP .TP \fB\fIQB_LOG_CONF_DEBUG \fP\fP .TP \fB\fIQB_LOG_CONF_SIZE \fP\fP .TP \fB\fIQB_LOG_CONF_THREADED \fP\fP .TP \fB\fIQB_LOG_CONF_PRIORITY_BUMP \fP\fP .TP \fB\fIQB_LOG_CONF_STATE_GET \fP\fP .TP \fB\fIQB_LOG_CONF_FILE_SYNC \fP\fP .TP \fB\fIQB_LOG_CONF_EXTENDED \fP\fP .TP \fB\fIQB_LOG_CONF_IDENT \fP\fP .SS "enum \fBqb_log_filter_conf\fP" .PP \fBEnumerator\fP .in +1c .TP \fB\fIQB_LOG_FILTER_ADD \fP\fP .TP \fB\fIQB_LOG_FILTER_REMOVE \fP\fP .TP \fB\fIQB_LOG_FILTER_CLEAR_ALL \fP\fP .TP \fB\fIQB_LOG_TAG_SET \fP\fP .TP \fB\fIQB_LOG_TAG_CLEAR \fP\fP .TP \fB\fIQB_LOG_TAG_CLEAR_ALL \fP\fP .SS "enum \fBqb_log_filter_type\fP" .PP \fBEnumerator\fP .in +1c .TP \fB\fIQB_LOG_FILTER_FILE \fP\fP .TP \fB\fIQB_LOG_FILTER_FUNCTION \fP\fP .TP \fB\fIQB_LOG_FILTER_FORMAT \fP\fP .TP \fB\fIQB_LOG_FILTER_FILE_REGEX \fP\fP .TP \fB\fIQB_LOG_FILTER_FUNCTION_REGEX \fP\fP .TP \fB\fIQB_LOG_FILTER_FORMAT_REGEX \fP\fP .SS "enum \fBqb_log_target_slot\fP" .PP \fBEnumerator\fP .in +1c .TP \fB\fIQB_LOG_TARGET_START \fP\fP .TP \fB\fIQB_LOG_TARGET_STATIC_START \fP\fP .TP \fB\fIQB_LOG_SYSLOG \fP\fP .TP \fB\fIQB_LOG_STDERR \fP\fP .TP \fB\fIQB_LOG_BLACKBOX \fP\fP .TP \fB\fIQB_LOG_STDOUT \fP\fP .TP \fB\fIQB_LOG_TARGET_STATIC_MAX \fP\fP .TP \fB\fIQB_LOG_TARGET_STATIC_END \fP\fP .TP \fB\fIQB_LOG_TARGET_DYNAMIC_START \fP\fP .TP \fB\fIQB_LOG_TARGET_MAX \fP\fP .TP \fB\fIQB_LOG_TARGET_DYNAMIC_END \fP\fP .TP \fB\fIQB_LOG_TARGET_END \fP\fP .SS "enum \fBqb_log_target_state\fP" .PP \fBEnumerator\fP .in +1c .TP \fB\fIQB_LOG_STATE_UNUSED \fP\fP .TP \fB\fIQB_LOG_STATE_DISABLED \fP\fP .TP \fB\fIQB_LOG_STATE_ENABLED \fP\fP .SH "Function Documentation" .PP .SS "struct \fBqb_log_callsite\fP __attribute__ ((aligned(8)))" .SS "void qb_log_blackbox_print_from_file (const char * filename)" .PP Read the blackbox for file and print it out\&. .SS "ssize_t qb_log_blackbox_write_to_file (const char * filename)" .PP Write the blackbox to file\&. .SS "void struct \fBqb_log_callsite\fP* qb_log_callsite_get (const char * function, const char * filename, const char * format, uint8_t priority, uint32_t lineno, uint32_t tags)" .PP Get or create a callsite at the given position\&. The result can then be passed into \fBqb_log_real_()\fP .PP \fBParameters:\fP .RS 4 \fIfunction\fP originating function name .br \fIfilename\fP originating filename .br \fIformat\fP format string .br \fIpriority\fP this takes syslog priorities\&. .br \fIlineno\fP file line number .br \fItags\fP the tag .RE .PP .SS "void qb_log_callsites_dump (void)" .PP Dump the callsite info to stdout\&. .SS "int32_t qb_log_callsites_register (struct \fBqb_log_callsite\fP * _start, struct \fBqb_log_callsite\fP * _stop)" .PP If you are using dynamically loadable modules via dlopen() and you load them after \fBqb_log_init()\fP then after you load the module you will need to do the following to get the filters to work in that module: .PP .nf _start = dlsym (dl_handle, QB_ATTR_SECTION_START_STR); _stop = dlsym (dl_handle, QB_ATTR_SECTION_STOP_STR); qb_log_callsites_register(_start, _stop); .fi .PP .SS "int32_t qb_log_ctl (int32_t target, enum \fBqb_log_conf\fP conf_type, int32_t arg)" .PP Main logging control function\&. .PP \fBParameters:\fP .RS 4 \fItarget\fP QB_LOG_SYSLOG, QB_LOG_STDERR or result from \fBqb_log_file_open()\fP .br \fIconf_type\fP configuration directive ('what to configure') that accepts \fCint32_t\fP argument determining the new value unless ignored for particular directive altogether (incompatible directives: QB_LOG_CONF_IDENT) .br \fIarg\fP the new value for a state-changing configuration directive, ignored otherwise .RE .PP \fBSee also:\fP .RS 4 \fBqb_log_conf\fP .RE .PP \fBReturn values:\fP .RS 4 \fI-errno\fP on error .br \fI0\fP on success .br \fIqb_log_target_state\fP for QB_LOG_CONF_STATE_GET .RE .PP .SS "int32_t qb_log_ctl2 (int32_t target, enum \fBqb_log_conf\fP conf_type, \fBqb_log_ctl2_arg_t\fP arg)" .PP Extension of main logging control function accepting also strings\&. .PP \fBParameters:\fP .RS 4 \fItarget\fP QB_LOG_SYSLOG, QB_LOG_STDERR or result from \fBqb_log_file_open()\fP .br \fIconf_type\fP configuration directive ('what to configure') that accepts either \fCint32_t\fP or a null-terminated string argument determining the new value unless ignored for particular directive (compatible directives: those valid for qb_log_ctl .IP "\(bu" 2 QB_LOG_CONF_IDENT) .PP .br \fIarg\fP 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) .RE .PP \fBSee also:\fP .RS 4 \fBqb_log_ctl\fP .RE .PP \fBNote:\fP .RS 4 You can use \fBQB_LOG_CTL2_I32\fP and \fBQB_LOG_CTL2_S\fP macros for a convenient on-the-fly construction of the object to be passed as an \fCarg\fP argument\&. .RE .PP .SS "void qb_log_custom_close (int32_t t)" .PP Close a custom log target and release is resources\&. .SS "int32_t qb_log_custom_open (\fBqb_log_logger_fn\fP log_fn, \fBqb_log_close_fn\fP close_fn, \fBqb_log_reload_fn\fP reload_fn, void * user_data)" .PP Open a custom log target\&. .PP \fBReturn values:\fP .RS 4 \fI-errno\fP on error .br \fIvalue\fP in inclusive range QB_LOG_TARGET_DYNAMIC_START to QB_LOG_TARGET_DYNAMIC_END (to be passed into other qb_log_* functions) .RE .PP .SS "int32_t qb_log_facility2int (const char * fname)" .PP Convert string 'auth' to equivalent number 'LOG_AUTH' etc\&. .SS "const char* qb_log_facility2str (int32_t fnum)" .PP Convert number 'LOG_AUTH' to equivalent string 'auth' etc\&. .SS "void qb_log_file_close (int32_t t)" .PP Close a log file and release is resources\&. .SS "int32_t qb_log_file_open (const char * filename)" .PP Open a log file\&. .PP \fBReturn values:\fP .RS 4 \fI-errno\fP on error .br \fIvalue\fP in inclusive range QB_LOG_TARGET_DYNAMIC_START to QB_LOG_TARGET_DYNAMIC_END (to be passed into other qb_log_* functions) .RE .PP .SS "int32_t qb_log_filter_ctl (int32_t value, enum \fBqb_log_filter_conf\fP c, enum \fBqb_log_filter_type\fP type, const char * text, uint8_t low_priority)" .PP This allows you modify the 'tags' and 'targets' callsite fields at runtime\&. .SS "int32_t qb_log_filter_ctl2 (int32_t value, enum \fBqb_log_filter_conf\fP c, enum \fBqb_log_filter_type\fP type, const char * text, uint8_t high_priority, uint8_t low_priority)" .PP This extends \fBqb_log_filter_ctl()\fP by been able to provide a high_priority\&. .SS "int32_t qb_log_filter_fn_set (\fBqb_log_filter_fn\fP fn)" .PP Instead of using the \fBqb_log_filter_ctl()\fP functions you can apply the filters manually by defining a callback and setting the targets field using \fBqb_bit_set()\fP and \fBqb_bit_clear()\fP like the following below: .PP .nf 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); } } .fi .PP .SS "void qb_log_fini (void)" .PP Logging system finalization function\&. It releases any shared memory\&. Stops the logging thread if running\&. Flushes the last messages to their destinations\&. .SS "void qb_log_format_set (int32_t t, const char * format)" .PP 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 .PP Any number between % and character specify field length to pad or chop\&. .PP \fBNote:\fP .RS 4 Some of the fields are immediately evaluated and remembered for performance reasons, so when there's an objective for log messages to carry PIDs (not in the default setup) and, moreover, precisely, this function needs to be reinvoked upon \fCfork\fP (\fCclone\fP) in the respective children\&. When already linking to \fClibpthread\fP, \fCpthread_atfork\fP callback registration could be useful\&. .RE .PP .SS "void qb_log_from_external_source (const char * function, const char * filename, const char * format, uint8_t priority, uint32_t lineno, uint32_t tags, \&.\&.\&.)" .PP This function is to import logs from other code (like libraries) that provide a callback with their logs\&. .PP \fBNote:\fP .RS 4 the performance of this will not impress you, as the filtering is done on each log message, not beforehand\&. So try doing basic pre-filtering\&. .RE .PP \fBParameters:\fP .RS 4 \fIfunction\fP originating function name .br \fIfilename\fP originating filename .br \fIformat\fP format string .br \fIpriority\fP this takes syslog priorities\&. .br \fIlineno\fP file line number .br \fItags\fP this is a uint32_t that you can use with \fBqb_log_tags_stringify_fn_set()\fP to 'tag' a log message with a feature or sub-system then you can use '%g' in the format specifer to print it out\&. .RE .PP .SS "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)" .SS "void qb_log_init (const char * name, int32_t facility, uint8_t priority)" .PP Init the logging system\&. .PP \fBParameters:\fP .RS 4 \fIname\fP will be passed into openlog() .br \fIfacility\fP default for all new targets\&. .br \fIpriority\fP a basic filter with this priority will be added\&. .RE .PP .SS "void qb_log_real_ (struct \fBqb_log_callsite\fP * cs, \&.\&.\&.)" .PP Internal function: use \fBqb_log()\fP or \fBqb_logt()\fP .SS "void qb_log_real_va_ (struct \fBqb_log_callsite\fP * cs, va_list ap)" .SS "void qb_log_tags_stringify_fn_set (\fBqb_log_tags_stringify_fn\fP fn)" .PP Set the callback to map the 'tags' bit map to a string\&. .SS "void qb_log_target_format (int32_t target, struct \fBqb_log_callsite\fP * cs, time_t timestamp, const char * formatted_message, char * output_buffer)" .PP Format the callsite and timestamp info according to the format\&. set using \fBqb_log_format_set()\fP It is intended to be used from your custom logger function\&. .SS "void* qb_log_target_user_data_get (int32_t t)" .PP Retrieve the user data set by either qb_log_custom_open or qb_log_target_user_data_set\&. .SS "int32_t qb_log_target_user_data_set (int32_t t, void * user_data)" .PP Associate user data with this log target\&. .PP \fBNote:\fP .RS 4 only use this with custom targets .RE .PP .SS "int32_t qb_log_thread_priority_set (int32_t policy, int32_t priority)" .PP When using threaded logging set the pthread policy and priority\&. .PP \fBReturn values:\fP .RS 4 \fI-errno\fP on error .br \fI0\fP success .RE .PP .SS "int32_t qb_log_thread_start (void)" .PP Start the logging pthread\&. .SH "Variable Documentation" .PP .SS "enum \fBqb_log_target_slot\fP __attribute__" .SS "const char* filename" .SS "const char* format" .SS "const char* function" .SS "uint32_t lineno" .SS "uint8_t priority" .SS "struct \fBqb_log_callsite\fP QB_ATTR_SECTION_START[]" .SS "struct \fBqb_log_callsite\fP QB_ATTR_SECTION_STOP[]" .SS "uint32_t tags" .SS "uint32_t targets" .SH "Author" .PP Generated automatically by Doxygen for libqb from the source code\&.