qblog.h(3) | libqb | qblog.h(3) |
qblog.h - The logging API provides four main parts (basics, filtering, threading & blackbox).
#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>
#include <assert.h>
#include <dlfcn.h>
struct qb_log_callsite
An instance of this structure is created in a special ELF section at every
dynamic debug callsite. union qb_log_ctl2_arg_t
#define LOG_TRACE (LOG_DEBUG + 1)
#define QB_LOG_MAX_LEN 512
#define QB_LOG_STRERROR_MAX_LEN 128
#define QB_ATTR_SECTION __verbose /* conforms to C ident. */
#define QB_ATTR_SECTION_STR
QB_PP_STRINGIFY(QB_ATTR_SECTION)
#define QB_ATTR_SECTION_START QB_PP_JOIN(__start_,
QB_ATTR_SECTION)
#define QB_ATTR_SECTION_STOP QB_PP_JOIN(__stop_,
QB_ATTR_SECTION)
#define QB_ATTR_SECTION_START_STR
QB_PP_STRINGIFY(QB_ATTR_SECTION_START)
#define QB_ATTR_SECTION_STOP_STR
QB_PP_STRINGIFY(QB_ATTR_SECTION_STOP)
#define QB_NONAPI_LOG_INIT_DATA_EXTRA_(name)
#define QB_LOG_INIT_DATA(name)
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 qb_logt 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. #define
QB_LOG_TAG_LIBQB_MSG_BIT 31
#define QB_LOG_TAG_LIBQB_MSG (1U << 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 ''
#define QB_XS ''
#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) })
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)
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
}
struct qb_log_callsite __attribute__ ((aligned(8)))
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. 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.
const char * function
const char * filename
const char * format
uint8_t priority
uint32_t lineno
uint32_t targets
uint32_t tags
struct qb_log_callsite QB_ATTR_SECTION_START []
struct qb_log_callsite QB_ATTR_SECTION_STOP []
enum qb_log_target_slot __attribute__
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.
Basic logging API.
Simplest possible use:
main() {
qb_log_init("simple-log", LOG_DAEMON, LOG_INFO);
// ...
qb_log(LOG_WARNING, "watch out");
// ...
qb_log_fini(); }
Note:
Configuring log targets.
qb_log_ctl(B_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
To enable a target do the following:
qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
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 QB_LOG_TARGET_DYNAMIC_START up to QB_LOG_TARGET_DYNAMIC_END). 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);
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 LOG_DEBUG importance, this can be worked around by proportionally bumping the priorities to be passed to syslog (here, the step is such that LOG_DEBUG gets promoted to 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 (new messages expressly transferred to the storage device as they keep coming, otherwise defaults to QB_FALSE):
qb_log_ctl(mytarget, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
Filtering messages.
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);
Thread safe non-blocking logging.
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(); }
A blackbox for in-field diagnosis.
Note:
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(); }
Tagging messages.
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
This is the main function to generate a log message.
Parameters:
Value:
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 arget'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 arget's build is at fault, preventing reliable logging" && QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } void __attribute__ ((constructor)) name(void);
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 qb_logt 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 _GNU_SOURCE 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.
Value:
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)
This is the function to generate a log message if you want to manually add tags.
Parameters:
Value:
_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)))
Value:
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.
Parameters:
Note:
Enumerator
Enumerator
Enumerator
Enumerator
Enumerator
Read the blackbox for file and print it out.
Write the blackbox to file.
Get or create a callsite at the given position. The result can then be passed into qb_log_real_()
Parameters:
Dump the callsite info to stdout.
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, QB_ATTR_SECTION_START_STR); _stop = dlsym (dl_handle, QB_ATTR_SECTION_STOP_STR); qb_log_callsites_register(_start, _stop);
Main logging control function.
Parameters:
See also:
Return values:
Extension of main logging control function accepting also strings.
Parameters:
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)
See also:
Note:
Close a custom log target and release is resources.
Open a custom log target.
Return values:
Convert string 'auth' to equivalent number 'LOG_AUTH' etc.
Convert number 'LOG_AUTH' to equivalent string 'auth' etc.
Close a log file and release is resources.
Open a log file.
Return values:
This allows you modify the 'tags' and 'targets' callsite fields at runtime.
This extends qb_log_filter_ctl() by been able to provide a high_priority.
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);
} }
Logging system finalization function. It releases any shared memory. Stops the logging thread if running. Flushes the last messages to their destinations.
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.
Note:
This function is to import logs from other code (like libraries) that provide a callback with their logs.
Note:
Parameters:
Init the logging system.
Parameters:
Internal function: use qb_log() or qb_logt()
Set the callback to map the 'tags' bit map to a string.
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.
Retrieve the user data set by either qb_log_custom_open or qb_log_target_user_data_set.
Associate user data with this log target.
Note:
When using threaded logging set the pthread policy and priority.
Return values:
Start the logging pthread.
Generated automatically by Doxygen for libqb from the source code.
Fri Apr 26 2019 | Version 1.0.5 |