Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/qb/qblog.h b/include/qb/qblog.h
index 3d7bf82..caa93a7 100644
--- a/include/qb/qblog.h
+++ b/include/qb/qblog.h
@@ -1,601 +1,602 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QB_LOG_H_DEFINED
#define QB_LOG_H_DEFINED
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
#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>
#ifdef S_SPLINT_S
#undef QB_HAVE_ATTRIBUTE_SECTION
#endif /* S_SPLINT_S */
/**
* @file qblog.h
* 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.
*
* @par Basic logging API.
* Call qb_log() to generate a log message. Then to write the message
* somewhere meaningful call qb_log_ctl() to configure the targets.
*
* Simplist possible use:
* @code
* main() {
* qb_log_init("simple-log", LOG_DAEMON, LOG_INFO);
* // ...
* qb_log(LOG_WARNING, "watch out");
* // ...
* qb_log_fini();
* }
* @endcode
*
* @par Configuring log targets.
* A log target can by syslog, stderr, the blackbox or a text file.
* By default only syslog is enabled.
*
* To enable a target do the following
* @code
* qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
* @endcode
*
* syslog, stderr and the blackbox are static (they don't need
* to be created, just enabled or disabled. However you can open multiple
* logfiles (32 - QB_LOG_BLACKBOX). To do this use the following code.
* @code
* mytarget = qb_log_file_open("/var/log/mylogfile");
* qb_log_ctl(mytarget, QB_LOG_CONF_ENABLED, QB_TRUE);
* @endcode
*
* Once your targets are enabled/opened you can configure them as follows:
* Configure the size of blackbox
* @code
* qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10);
* @endcode
*
* Make logging to file threaded:
* @code
* qb_log_ctl(mytarget, QB_LOG_CONF_THREADED, QB_TRUE);
* @endcode
*
* To workaround your syslog daemon filtering all messages > LOG_INFO
* @code
* qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP,
* LOG_INFO - LOG_DEBUG);
* @endcode
*
* @par Filtering messages.
* 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:
* -# filename + priority
* -# function name + priority
* -# format string + priority
*
* So to make all logs from evil_fnunction() go to stderr do the following:
* @code
* qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
* QB_LOG_FILTER_FUNCTION, "evil_fnunction", LOG_TRACE);
* @endcode
*
* So to make all logs from totem* (with a priority <= LOG_INFO) go to stderr do the following:
* @code
* qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
* QB_LOG_FILTER_FILE, "totem", LOG_INFO);
* @endcode
*
* So to make all logs with the substring "ringbuffer" go to stderr do the following:
* @code
* qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
* QB_LOG_FILTER_FORMAT, "ringbuffer", LOG_TRACE);
* @endcode
*
* @par Threaded logging.
* To achieve non-blocking logging you can use threaded logging. So any
* calls to write() or syslog() will not hold up your program.
*
* Threaded logging use:
* @code
* 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();
* }
* @endcode
*
* @par A blackbox for in-field diagnosis.
* 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.
*
* @note the blackbox is not enabled by default.
*
* Blackbox usage:
* @code
*
* 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();
* }
* @endcode
*
* @par Tagging messages.
* You can tag messages using the second argument to qb_logt() or
* by using qb_log_filter_ctl().
* This can be used to add feature or sub-system information to the logs.
*
* @code
* 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");
* }
* @endcode
* The code above will produce:
* @code
* [libqb] some message
* [three] info hello
* [MAIN ] info hello
* @endcode
*
* @example simplelog.c
*/
#undef LOG_TRACE
#define LOG_TRACE (LOG_DEBUG + 1)
#define QB_LOG_MAX_LEN 512
#define QB_LOG_STRERROR_MAX_LEN 128
typedef const char *(*qb_log_tags_stringify_fn)(uint32_t tags);
/**
* 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.
*/
struct qb_log_callsite {
const char *function;
const char *filename;
const char *format;
uint8_t priority;
uint32_t lineno;
uint32_t targets;
uint32_t tags;
} __attribute__((aligned(8)));
typedef void (*qb_log_filter_fn)(struct qb_log_callsite * cs);
/* will be assigned by ld linker magic */
#ifdef QB_HAVE_ATTRIBUTE_SECTION
extern struct qb_log_callsite __start___verbose[];
extern struct qb_log_callsite __stop___verbose[];
#define QB_LOG_INIT_DATA(name) \
void name(void); \
void name(void) { if (__start___verbose != __stop___verbose) {assert(1);} } \
void __attribute__ ((constructor)) name(void);
#else
#define QB_LOG_INIT_DATA(name)
#endif
/**
* Internal function: use qb_log() or qb_logt()
*/
void qb_log_real_(struct qb_log_callsite *cs, ...);
void qb_log_real_va_(struct qb_log_callsite *cs, va_list ap);
#define QB_LOG_TAG_LIBQB_MSG_BIT 31
#define QB_LOG_TAG_LIBQB_MSG (1 << QB_LOG_TAG_LIBQB_MSG_BIT)
/**
* This function is to import logs from other code (like libraries)
* that provide a callback with their logs.
*
* @note the performance of this will not impress you, as
* the filtering is done on each log message, not
* before hand. So try doing basic pre-filtering.
*
* @param function originating function name
* @param filename originating filename
* @param format format string
* @param priority this takes syslog priorities.
* @param lineno file line number
* @param tags This MUST have QB_LOG_EXTERNAL_TAG or'ed in
* so that it gets free'ed.
*/
void qb_log_from_external_source(const char *function,
const char *filename,
const char *format,
uint8_t priority,
uint32_t lineno,
uint32_t tags,
...);
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);
/**
* This is the function to generate a log message if you want to
* manually add tags.
*
* @param priority this takes syslog priorities.
* @param 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.
* @param fmt usual printf style format specifiers
* @param args usual printf style args
*/
#ifdef QB_HAVE_ATTRIBUTE_SECTION
#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)
#else
#define qb_logt(priority, tags, fmt, args...) do { \
qb_log_from_external_source(__func__, __FILE__, fmt, priority, \
__LINE__, tags, ##args); \
} while(0)
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
/**
* This is the main function to generate a log message.
*
* @param priority this takes syslog priorities.
* @param fmt usual printf style format specifiers
* @param args usual printf style args
*/
#define qb_log(priority, fmt, args...) qb_logt(priority, 0, fmt, ##args)
/**
* This is similar to perror except it goes into the logging system.
*
* @param priority this takes syslog priorities.
* @param fmt usual printf style format specifiers
* @param args usual printf style args
*/
#ifndef S_SPLINT_S
#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)
#else
#define qb_perror
#endif
#define qb_enter() qb_log(LOG_TRACE, "ENTERING %s()", __func__)
#define qb_leave() qb_log(LOG_TRACE, "LEAVING %s()", __func__)
#define QB_LOG_SYSLOG 0
#define QB_LOG_STDERR 1
#define QB_LOG_BLACKBOX 2
#define QB_LOG_TARGET_MAX 32
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,
};
enum qb_log_filter_type {
QB_LOG_FILTER_FILE,
QB_LOG_FILTER_FUNCTION,
QB_LOG_FILTER_FORMAT,
};
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,
};
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);
/**
* Init the logging system.
*
* @param name will be passed into openlog()
* @param facility default for all new targets.
* @param priority a basic filter with this priority will be added.
*/
void qb_log_init(const char *name,
int32_t facility,
uint8_t priority);
/**
* Logging system finalization function.
*
* It releases any shared memory.
* Stops the logging thread if running.
* Flushes the last message to their destinations.
*/
void qb_log_fini(void);
/**
* 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.
* @code
* _start = dlsym (dl_handle, "__start___verbose");
* _stop = dlsym (dl_handle, "__stop___verbose");
* qb_log_callsites_register(_start, _stop);
* @endcode
*/
int32_t qb_log_callsites_register(struct qb_log_callsite *_start, struct qb_log_callsite *_stop);
/**
* Dump the callsite info to stdout.
*/
void qb_log_callsites_dump(void);
/**
* Main logging control function.
*
* @param target QB_LOG_SYSLOG, QB_LOG_STDERR or result from qb_log_file_open()
* @param conf_type what to configure
* @param arg the new value
* @see qb_log_conf
*
* @retval -errno on error
* @retval 0 on success
* @retval qb_log_target_state for QB_LOG_CONF_STATE_GET
*/
int32_t qb_log_ctl(int32_t target, enum qb_log_conf conf_type, int32_t arg);
/**
* This allows you modify the 'tags' and 'targets' callsite fields at runtime.
*/
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 extends qb_log_filter_ctl() by been able to provide a high_priority.
*/
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);
/**
* 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.
* @code
* 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);
* }
* }
* @endcode
*/
int32_t qb_log_filter_fn_set(qb_log_filter_fn fn);
/**
* Set the callback to map the 'tags' bit map to a string.
*/
void qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn);
/**
* 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_format_set(int32_t t, const char* format);
/**
* Open a log file.
*
* @retval -errno on error
* @retval 3 to 31 (to be passed into other qb_log_* functions)
*/
int32_t qb_log_file_open(const char *filename);
/**
* Close a log file and release is resources.
*/
void qb_log_file_close(int32_t t);
/**
* When using threaded logging set the pthread policy and priority.
*
* @retval -errno on error
* @retval 0 success
*/
int32_t qb_log_thread_priority_set(int32_t policy, int32_t priority);
/**
* Start the logging pthread.
*/
int32_t qb_log_thread_start(void);
/**
* Write the blackbox to file.
*/
ssize_t qb_log_blackbox_write_to_file(const char *filename);
/**
* Read the blackbox for file and print it out.
*/
void qb_log_blackbox_print_from_file(const char* filename);
/**
* Open a custom log target.
*
* @retval -errno on error
* @retval 3 to 31 (to be passed into other qb_log_* functions)
*/
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);
/**
* Close a custom log target and release is resources.
*/
void qb_log_custom_close(int32_t t);
/**
* Retrieve the user data set by either qb_log_custom_open or
* qb_log_target_user_data_set.
*/
void *qb_log_target_user_data_get(int32_t t);
/**
* Associate user data with this log target
* @note only use this with custom targets
*/
int32_t qb_log_target_user_data_set(int32_t t, void *user_data);
/**
* 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_format(int32_t target,
struct qb_log_callsite *cs,
time_t timestamp,
const char* formatted_message,
char *output_buffer);
/**
* Convert string "auth" to equivalent number "LOG_AUTH" etc.
*/
int32_t qb_log_facility2int(const char *fname);
/**
* Convert number "LOG_AUTH" to equivalent string "auth" etc.
*/
const char * qb_log_facility2str(int32_t fnum);
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif /* __cplusplus */
/* *INDENT-ON* */
#endif /* QB_LOG_H_DEFINED */
diff --git a/lib/log.c b/lib/log.c
index 0785b21..4bcd3f5 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -1,991 +1,983 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <ctype.h>
#ifdef HAVE_LINK_H
#include <link.h>
#endif /* HAVE_LINK_H */
#include <stdarg.h>
#include <pthread.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif /* HAVE_DLFCN_H */
#include <stdarg.h>
#include <qb/qbdefs.h>
#include <qb/qblist.h>
#include <qb/qblog.h>
#include <qb/qbutil.h>
#include <qb/qbarray.h>
#include "log_int.h"
#include "util_int.h"
static struct qb_log_target conf[QB_LOG_TARGET_MAX];
static uint32_t conf_active_max = 0;
static int32_t in_logger = QB_FALSE;
static int32_t logger_inited = QB_FALSE;
static pthread_rwlock_t _listlock;
static qb_log_filter_fn _custom_filter_fn = NULL;
static QB_LIST_DECLARE(tags_head);
static QB_LIST_DECLARE(callsite_sections);
struct callsite_section {
struct qb_log_callsite *start;
struct qb_log_callsite *stop;
struct qb_list_head list;
};
static int32_t _log_target_enable(struct qb_log_target *t);
static void _log_target_disable(struct qb_log_target *t);
static void _log_filter_apply(struct callsite_section *sect,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority, uint8_t low_priority);
static void _log_filter_apply_to_cs(struct qb_log_callsite *cs,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority, uint8_t low_priority);
/* deprecated method of getting internal log messages */
static qb_util_log_fn_t old_internal_log_fn = NULL;
void
qb_util_set_log_function(qb_util_log_fn_t fn)
{
old_internal_log_fn = fn;
}
static int32_t
_cs_matches_filter_(struct qb_log_callsite *cs,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority,
uint8_t low_priority)
{
int32_t match = QB_FALSE;
if (cs->priority > low_priority ||
cs->priority < high_priority) {
return QB_FALSE;
}
if (strcmp(text, "*") == 0) {
return QB_TRUE;
}
if (type == QB_LOG_FILTER_FILE ||
type == QB_LOG_FILTER_FUNCTION) {
char token[500];
const char *offset = NULL;
const char *next = text;
do {
offset = next;
next = strchrnul(offset, ',');
snprintf(token, 499, "%.*s", (int)(next - offset), offset);
if (type == QB_LOG_FILTER_FILE) {
match = (strstr(cs->filename, token) != NULL);
} else {
match = (strstr(cs->function, token) != NULL);
}
if (!match && next[0] != 0) {
next++;
}
} while (match == QB_FALSE && next != NULL && next[0] != 0);
} else if (type == QB_LOG_FILTER_FORMAT) {
if (strstr(cs->format, text)) {
match = QB_TRUE;
}
}
return match;
}
void
qb_log_real_va_(struct qb_log_callsite *cs, va_list ap)
{
int32_t found_threaded;
struct qb_log_target *t;
struct timeval tv;
int32_t pos;
int len;
int32_t formatted = QB_FALSE;
char buf[QB_LOG_MAX_LEN];
char *str = buf;
va_list ap_copy;
if (in_logger) {
return;
}
in_logger = QB_TRUE;
if (old_internal_log_fn) {
if (qb_bit_is_set(cs->tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
if (!formatted) {
va_copy(ap_copy, ap);
len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
va_end(ap_copy);
if (str[len - 1] == '\n') str[len - 1] = '\0';
formatted = QB_TRUE;
}
old_internal_log_fn(cs->filename, cs->lineno,
cs->priority, str);
}
}
gettimeofday(&tv, NULL);
/*
* 1 if we can find a threaded target that needs this log then post it
* 2 foreach non-threaded target call it's logger function
*/
found_threaded = QB_FALSE;
for (pos = 0; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
if (t->threaded) {
if (!found_threaded
&& qb_bit_is_set(cs->targets, t->pos)) {
found_threaded = QB_TRUE;
if (!formatted) {
va_copy(ap_copy, ap);
len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
va_end(ap_copy);
if (str[len - 1] == '\n') str[len - 1] = '\0';
formatted = QB_TRUE;
}
}
} else {
if (qb_bit_is_set(cs->targets, t->pos)) {
if (t->vlogger) {
va_copy(ap_copy, ap);
t->vlogger(t->pos, cs, tv.tv_sec, ap_copy);
va_end(ap_copy);
} else if (t->logger) {
if (!formatted) {
va_copy(ap_copy, ap);
len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
va_end(ap_copy);
if (str[len - 1] == '\n') str[len - 1] = '\0';
formatted = QB_TRUE;
}
t->logger(t->pos, cs, tv.tv_sec, str);
}
}
}
}
if (found_threaded) {
qb_log_thread_log_post(cs, tv.tv_sec, str);
}
in_logger = QB_FALSE;
}
void
qb_log_real_(struct qb_log_callsite *cs, ...)
{
va_list ap;
va_start(ap, cs);
qb_log_real_va_(cs, ap);
va_end(ap);
}
void
qb_log_thread_log_write(struct qb_log_callsite *cs,
time_t timestamp, const char *buffer)
{
struct qb_log_target *t;
int32_t pos;
for (pos = 0; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
if (t->threaded) {
continue;
}
if (qb_bit_is_set(cs->targets, t->pos)) {
t->logger(t->pos, cs, timestamp, buffer);
}
}
}
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)
{
struct qb_log_target *t;
struct qb_log_filter *flt;
struct qb_log_callsite *cs;
int32_t new_dcs = QB_FALSE;
struct qb_list_head *f_item;
int32_t pos;
if (!logger_inited) {
return;
}
cs = qb_log_dcs_get(&new_dcs, function, filename,
format, priority, lineno, tags);
if (cs == NULL) {
return;
}
if (new_dcs) {
pthread_rwlock_rdlock(&_listlock);
for (pos = 0; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
for (f_item = t->filter_head.next; f_item != &t->filter_head; f_item = f_item->next) {
flt = qb_list_entry(f_item, struct qb_log_filter, list);
_log_filter_apply_to_cs(cs, t->pos, flt->conf, flt->type,
flt->text, flt->high_priority,
flt->low_priority);
}
}
if (tags == 0) {
for (f_item = tags_head.next; f_item != &tags_head; f_item = f_item->next) {
flt = qb_list_entry(f_item, struct qb_log_filter, list);
_log_filter_apply_to_cs(cs, flt->new_value, flt->conf, flt->type,
flt->text, flt->high_priority,
flt->low_priority);
}
} else {
cs->tags = tags;
}
if (_custom_filter_fn) {
_custom_filter_fn(cs);
}
pthread_rwlock_unlock(&_listlock);
}
qb_log_real_va_(cs, 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, ...)
{
va_list ap;
va_start(ap, tags);
qb_log_from_external_source_va(function, filename, format, priority,
lineno, tags, ap);
va_end(ap);
}
int32_t
qb_log_callsites_register(struct qb_log_callsite *_start,
struct qb_log_callsite *_stop)
{
struct callsite_section *sect;
struct qb_log_callsite *cs;
struct qb_log_target *t;
struct qb_log_filter *flt;
int32_t pos;
if (_start == NULL || _stop == NULL) {
return -EINVAL;
}
pthread_rwlock_rdlock(&_listlock);
qb_list_for_each_entry(sect, &callsite_sections, list) {
if (sect->start == _start || sect->stop == _stop) {
pthread_rwlock_unlock(&_listlock);
return -EEXIST;
}
}
pthread_rwlock_unlock(&_listlock);
sect = calloc(1, sizeof(struct callsite_section));
if (sect == NULL) {
return -ENOMEM;
}
sect->start = _start;
sect->stop = _stop;
qb_list_init(&sect->list);
pthread_rwlock_wrlock(&_listlock);
qb_list_add(&sect->list, &callsite_sections);
/*
* Now apply the filters on these new callsites
*/
for (pos = 0; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
qb_list_for_each_entry(flt, &t->filter_head, list) {
_log_filter_apply(sect, t->pos, flt->conf,
flt->type, flt->text,
flt->high_priority, flt->low_priority);
}
}
qb_list_for_each_entry(flt, &tags_head, list) {
_log_filter_apply(sect, flt->new_value, flt->conf,
flt->type, flt->text,
flt->high_priority, flt->low_priority);
}
pthread_rwlock_unlock(&_listlock);
if (_custom_filter_fn) {
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno == 0) {
break;
}
_custom_filter_fn(cs);
}
}
return 0;
}
void
qb_log_callsites_dump(void)
{
struct callsite_section *sect;
struct qb_log_callsite *cs;
int32_t l;
pthread_rwlock_rdlock(&_listlock);
l = qb_list_length(&callsite_sections);
printf("Callsite Database [%d]\n", l);
printf("---------------------\n");
qb_list_for_each_entry(sect, &callsite_sections, list) {
printf(" start %p - stop %p\n", sect->start, sect->stop);
printf("filename lineno targets tags\n");
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno == 0) {
break;
}
printf("%12s %6d %16d %16d\n", cs->filename, cs->lineno,
cs->targets, cs->tags);
}
}
pthread_rwlock_unlock(&_listlock);
}
static int32_t
_log_filter_exists(struct qb_list_head *list_head,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority,
uint8_t low_priority,
uint32_t new_value)
{
struct qb_log_filter *flt;
qb_list_for_each_entry(flt, list_head, list) {
if (flt->type == type &&
flt->high_priority == high_priority &&
flt->low_priority == low_priority &&
flt->new_value == new_value &&
strcmp(flt->text, text) == 0) {
return QB_TRUE;
}
}
return QB_FALSE;
}
static int32_t
_log_filter_store(uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority,
uint8_t low_priority)
{
struct qb_log_filter *flt;
struct qb_list_head *iter;
struct qb_list_head *next;
struct qb_list_head *list_head;
switch (c) {
case QB_LOG_FILTER_ADD:
case QB_LOG_FILTER_REMOVE:
case QB_LOG_FILTER_CLEAR_ALL:
list_head = &conf[t].filter_head;
break;
case QB_LOG_TAG_SET:
case QB_LOG_TAG_CLEAR:
case QB_LOG_TAG_CLEAR_ALL:
list_head = &tags_head;
break;
default:
return -ENOSYS;
}
if (c == QB_LOG_FILTER_ADD || c == QB_LOG_TAG_SET) {
if (text == NULL) {
return -EINVAL;
}
if (_log_filter_exists(list_head, type, text,
high_priority, low_priority, t)) {
return -EEXIST;
}
flt = calloc(1, sizeof(struct qb_log_filter));
if (flt == NULL) {
return -ENOMEM;
}
qb_list_init(&flt->list);
flt->conf = c;
flt->type = type;
flt->text = strdup(text);
if (flt->text == NULL) {
free(flt);
return -ENOMEM;
}
flt->high_priority = high_priority;
flt->low_priority = low_priority;
flt->new_value = t;
qb_list_add_tail(&flt->list, list_head);
} else if (c == QB_LOG_FILTER_REMOVE || c == QB_LOG_TAG_CLEAR) {
qb_list_for_each_safe(iter, next, list_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
if (flt->type == type &&
flt->low_priority <= low_priority &&
flt->high_priority >= high_priority &&
(strcmp(flt->text, text) == 0 ||
strcmp("*", text) == 0)) {
qb_list_del(iter);
free(flt->text);
free(flt);
return 0;
}
}
} else if (c == QB_LOG_FILTER_CLEAR_ALL || c == QB_LOG_TAG_CLEAR_ALL) {
qb_list_for_each_safe(iter, next, list_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
qb_list_del(iter);
free(flt->text);
free(flt);
}
}
return 0;
}
static void
_log_filter_apply(struct callsite_section *sect,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority, uint8_t low_priority)
{
struct qb_log_callsite *cs;
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno == 0) {
break;
}
_log_filter_apply_to_cs(cs, t, c, type, text,
high_priority, low_priority);
}
}
/* #define _QB_FILTER_DEBUGGING_ 1 */
static void
_log_filter_apply_to_cs(struct qb_log_callsite *cs,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority, uint8_t low_priority)
{
if (c == QB_LOG_FILTER_CLEAR_ALL) {
qb_bit_clear(cs->targets, t);
return;
} else if (c == QB_LOG_TAG_CLEAR_ALL) {
cs->tags = 0;
return;
}
if (_cs_matches_filter_(cs, type, text, high_priority, low_priority)) {
#ifdef _QB_FILTER_DEBUGGING_
uint32_t old_targets = cs->targets;
uint32_t old_tags = cs->tags;
#endif /* _QB_FILTER_DEBUGGING_ */
if (c == QB_LOG_FILTER_ADD) {
qb_bit_set(cs->targets, t);
} else if (c == QB_LOG_FILTER_REMOVE) {
qb_bit_clear(cs->targets, t);
} else if (c == QB_LOG_TAG_SET) {
cs->tags = t;
} else if (c == QB_LOG_TAG_CLEAR) {
cs->tags = 0;
}
#ifdef _QB_FILTER_DEBUGGING_
if (old_targets != cs->targets) {
printf("targets: %s:%u value(%d) %d -> %d\n",
cs->filename, cs->lineno, t,
old_targets, cs->targets);
}
if (old_tags != cs->tags) {
printf("tags: %s:%u value(%d) %d -> %d\n",
cs->filename, cs->lineno, t, old_tags, cs->tags);
}
#endif /* _QB_FILTER_DEBUGGING_ */
}
}
int32_t
qb_log_filter_ctl2(int32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type, const char * text,
uint8_t high_priority, uint8_t low_priority)
{
struct callsite_section *sect;
int32_t rc;
if (!logger_inited) {
return -EINVAL;
}
if (c == QB_LOG_FILTER_ADD ||
c == QB_LOG_FILTER_CLEAR_ALL ||
c == QB_LOG_FILTER_REMOVE) {
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
}
if (text == NULL ||
low_priority < high_priority ||
type > QB_LOG_FILTER_FORMAT ||
c > QB_LOG_TAG_CLEAR_ALL) {
return -EINVAL;
}
pthread_rwlock_rdlock(&_listlock);
rc = _log_filter_store(t, c, type, text, high_priority, low_priority);
if (rc < 0) {
pthread_rwlock_unlock(&_listlock);
return rc;
}
qb_list_for_each_entry(sect, &callsite_sections, list) {
_log_filter_apply(sect, t, c, type, text, high_priority, low_priority);
}
pthread_rwlock_unlock(&_listlock);
return 0;
}
int32_t
qb_log_filter_fn_set(qb_log_filter_fn fn)
{
struct callsite_section *sect;
struct qb_log_callsite *cs;
if (!logger_inited) {
return -EINVAL;
}
_custom_filter_fn = fn;
if (_custom_filter_fn == NULL) {
return 0;
}
qb_list_for_each_entry(sect, &callsite_sections, list) {
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno == 0) {
break;
}
_custom_filter_fn(cs);
}
}
return 0;
}
int32_t
qb_log_filter_ctl(int32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text, uint8_t priority)
{
return qb_log_filter_ctl2(t, c, type, text, LOG_EMERG, priority);
}
#ifdef QB_HAVE_ATTRIBUTE_SECTION
static int32_t
_log_so_walk_callback(struct dl_phdr_info *info, size_t size, void *data)
{
if (strlen(info->dlpi_name) > 0) {
void *handle;
void *start;
void *stop;
const char *error;
handle = dlopen(info->dlpi_name, RTLD_LAZY);
error = dlerror();
if (!handle || error) {
qb_log(LOG_ERR, "%s", error);
if (handle) {
dlclose(handle);
}
return 0;
}
start = dlsym(handle, "__start___verbose");
error = dlerror();
if (error) {
goto done;
}
stop = dlsym(handle, "__stop___verbose");
error = dlerror();
if (error) {
goto done;
} else {
qb_log_callsites_register(start, stop);
}
done:
dlclose(handle);
}
return 0;
}
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
static void
_log_target_state_set(struct qb_log_target *t, enum qb_log_target_state s)
{
int32_t i;
int32_t a_set = QB_FALSE;
int32_t u_set = QB_FALSE;
t->state = s;
for (i = 31; i >= 0; i--) {
if (!a_set && conf[i].state == QB_LOG_STATE_ENABLED) {
a_set = QB_TRUE;
conf_active_max = i;
}
if (!u_set && conf[i].state != QB_LOG_STATE_UNUSED) {
u_set = QB_TRUE;
}
}
}
void
qb_log_init(const char *name, int32_t facility, uint8_t priority)
{
int32_t i;
i = pthread_rwlock_init(&_listlock, NULL);
assert(i == 0);
for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
conf[i].pos = i;
conf[i].debug = QB_FALSE;
conf[i].state = QB_LOG_STATE_UNUSED;
(void)strlcpy(conf[i].name, name, PATH_MAX);
conf[i].facility = facility;
qb_list_init(&conf[i].filter_head);
qb_log_format_set(i, NULL);
}
qb_log_dcs_init();
#ifdef QB_HAVE_ATTRIBUTE_SECTION
qb_log_callsites_register(__start___verbose, __stop___verbose);
dl_iterate_phdr(_log_so_walk_callback, NULL);
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
conf[QB_LOG_STDERR].state = QB_LOG_STATE_DISABLED;
conf[QB_LOG_BLACKBOX].state = QB_LOG_STATE_DISABLED;
logger_inited = QB_TRUE;
(void)qb_log_syslog_open(&conf[QB_LOG_SYSLOG]);
_log_target_state_set(&conf[QB_LOG_SYSLOG], QB_LOG_STATE_ENABLED);
(void)qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", priority);
}
void
qb_log_fini(void)
{
struct qb_log_target *t;
struct qb_log_filter *flt;
struct callsite_section *s;
struct qb_list_head *iter;
struct qb_list_head *iter2;
struct qb_list_head *next;
struct qb_list_head *next2;
int32_t pos;
if (!logger_inited) {
return;
}
logger_inited = QB_FALSE;
qb_log_thread_stop();
pthread_rwlock_destroy(&_listlock);
for (pos = 0; pos <= conf_active_max; pos++) {
t = &conf[pos];
_log_target_disable(t);
free(conf[pos].format);
qb_list_for_each_safe(iter2, next2, &t->filter_head) {
flt = qb_list_entry(iter2, struct qb_log_filter, list);
qb_list_del(iter2);
free(flt->text);
free(flt);
}
}
qb_log_dcs_fini();
qb_list_for_each_safe(iter, next, &callsite_sections) {
s = qb_list_entry(iter, struct callsite_section, list);
qb_list_del(iter);
free(s);
}
qb_list_for_each_safe(iter, next, &tags_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
qb_list_del(iter);
free(flt->text);
free(flt);
}
}
struct qb_log_target *
qb_log_target_alloc(void)
{
int32_t i;
for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
if (conf[i].state == QB_LOG_STATE_UNUSED) {
_log_target_state_set(&conf[i], QB_LOG_STATE_DISABLED);
return &conf[i];
}
}
return NULL;
}
void
qb_log_target_free(struct qb_log_target *t)
{
(void)qb_log_filter_ctl(t->pos, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, NULL, 0);
t->debug = QB_FALSE;
t->filename[0] = '\0';
qb_log_format_set(t->pos, NULL);
_log_target_state_set(t, QB_LOG_STATE_UNUSED);
}
struct qb_log_target *
qb_log_target_get(int32_t pos)
{
return &conf[pos];
}
void *
qb_log_target_user_data_get(int32_t t)
{
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
errno = -EBADF;
return NULL;
}
return conf[t].instance;
}
int32_t
qb_log_target_user_data_set(int32_t t, void *user_data)
{
if (!logger_inited) {
return -EINVAL;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
conf[t].instance = user_data;
return 0;
}
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)
{
struct qb_log_target *t;
t = qb_log_target_alloc();
if (t == NULL) {
return -errno;
}
t->instance = user_data;
snprintf(t->filename, PATH_MAX, "custom-%d", t->pos);
t->logger = log_fn;
t->vlogger = NULL;
t->reload = reload_fn;
t->close = close_fn;
return t->pos;
}
void
qb_log_custom_close(int32_t t)
{
struct qb_log_target *target;
if (!logger_inited) {
return;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return;
}
target = qb_log_target_get(t);
if (target->close) {
target->close(t);
}
qb_log_target_free(target);
}
static int32_t
_log_target_enable(struct qb_log_target *t)
{
int32_t rc = 0;
if (t->state == QB_LOG_STATE_ENABLED) {
return 0;
}
if (t->pos == QB_LOG_STDERR) {
rc = qb_log_stderr_open(t);
} else if (t->pos == QB_LOG_SYSLOG) {
rc = qb_log_syslog_open(t);
} else if (t->pos == QB_LOG_BLACKBOX) {
rc = qb_log_blackbox_open(t);
}
if (rc == 0) {
_log_target_state_set(t, QB_LOG_STATE_ENABLED);
}
return rc;
}
static void
_log_target_disable(struct qb_log_target *t)
{
if (t->state != QB_LOG_STATE_ENABLED) {
return;
}
_log_target_state_set(t, QB_LOG_STATE_DISABLED);
if (t->close) {
in_logger = QB_TRUE;
t->close(t->pos);
in_logger = QB_FALSE;
}
}
int32_t
qb_log_ctl(int32_t t, enum qb_log_conf c, int32_t arg)
{
int32_t rc = 0;
int32_t need_reload = QB_FALSE;
if (!logger_inited) {
return -EINVAL;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
switch (c) {
case QB_LOG_CONF_ENABLED:
if (arg) {
rc = _log_target_enable(&conf[t]);
} else {
_log_target_disable(&conf[t]);
}
break;
case QB_LOG_CONF_STATE_GET:
rc = conf[t].state;
break;
case QB_LOG_CONF_FACILITY:
conf[t].facility = arg;
if (t == QB_LOG_SYSLOG) {
need_reload = QB_TRUE;
}
break;
case QB_LOG_CONF_PRIORITY_BUMP:
conf[t].priority_bump = arg;
break;
case QB_LOG_CONF_SIZE:
if (t == QB_LOG_BLACKBOX) {
if (arg <= 0) {
return -EINVAL;
}
conf[t].size = arg;
need_reload = QB_TRUE;
} else {
return -ENOSYS;
}
break;
case QB_LOG_CONF_THREADED:
conf[t].threaded = arg;
break;
default:
rc = -EINVAL;
}
if (rc == 0 && need_reload && conf[t].reload) {
conf[t].reload(t);
}
return rc;
}
void
qb_log_format_set(int32_t t, const char *format)
{
- char* ptr = NULL;
- size_t len = 0;
-
- if (format) {
- ptr = strstr(format, "%N");
- len = strlen(format);
- }
+ char modified_format[256];
free(conf[t].format);
- if (ptr) {
- len += strlen(conf[t].name);
- conf[t].format = calloc(len + 1, sizeof(char));
- strncpy(conf[t].format, format, ptr - format);
- (void)strlcat(conf[t].format, conf[t].name, len);
- ptr += 2;
- (void)strlcat(conf[t].format, ptr, len);
+ if (format) {
+ printf("B %s\n", format);
+ qb_log_target_format_static(t, format, modified_format);
+ printf("A %s\n", modified_format);
+ conf[t].format = strdup(modified_format);
} else {
- conf[t].format = strdup(format ? format : "[%p] %b");
+ conf[t].format = strdup("[%p] %b");
}
assert(conf[t].format != NULL);
}
diff --git a/lib/log_format.c b/lib/log_format.c
index 70eaedb..6071a30 100644
--- a/lib/log_format.c
+++ b/lib/log_format.c
@@ -1,618 +1,702 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <ctype.h>
#include <qb/qbdefs.h>
#include "log_int.h"
static qb_log_tags_stringify_fn _user_tags_stringify_fn;
/*
* syslog prioritynames, facility names to value mapping
* Some C libraries build this in to their headers, but it is non-portable
* so logsys supplies its own version.
*/
struct syslog_names {
const char *c_name;
int32_t c_val;
};
static struct syslog_names prioritynames[] = {
{"emerg", LOG_EMERG},
{"alert", LOG_ALERT},
{"crit", LOG_CRIT},
{"error", LOG_ERR},
{"warning", LOG_WARNING},
{"notice", LOG_NOTICE},
{"info", LOG_INFO},
{"debug", LOG_DEBUG},
{"trace", LOG_TRACE},
{NULL, -1}
};
struct syslog_names facilitynames[] = {
{"auth", LOG_AUTH},
{"authpriv", LOG_AUTHPRIV},
{"cron", LOG_CRON},
{"daemon", LOG_DAEMON},
{"ftp", LOG_FTP},
{"kern", LOG_KERN},
{"lpr", LOG_LPR},
{"mail", LOG_MAIL},
{"news", LOG_NEWS},
{"syslog", LOG_SYSLOG},
{"user", LOG_USER},
{"uucp", LOG_UUCP},
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7},
{NULL, -1}
};
static const char log_month_name[][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
int32_t
qb_log_facility2int(const char *fname)
{
int32_t i;
if (fname == NULL) {
return -EINVAL;
}
for (i = 0; facilitynames[i].c_name != NULL; i++) {
if (strcmp(fname, facilitynames[i].c_name) == 0) {
return facilitynames[i].c_val;
}
}
return -EINVAL;
}
/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
const char *
qb_log_facility2str(int32_t fnum)
{
int32_t i;
for (i = 0; facilitynames[i].c_name != NULL; i++) {
if (facilitynames[i].c_val == fnum) {
return facilitynames[i].c_name;
}
}
return NULL;
}
void
qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn)
{
_user_tags_stringify_fn = fn;
}
static int
_strcpy_cutoff(char *dest, const char *src, size_t cutoff, int ralign,
size_t buf_len)
{
size_t len = strlen(src);
if (buf_len <= 1) {
if (buf_len == 0)
dest[0] = 0;
return 0;
}
if (cutoff == 0) {
cutoff = len;
}
cutoff = QB_MIN(cutoff, buf_len - 1);
len = QB_MIN(len, cutoff);
if (ralign) {
memset(dest, ' ', cutoff - len);
memcpy(dest + cutoff - len, src, len);
} else {
memcpy(dest, src, len);
memset(dest + len, ' ', cutoff - len);
}
dest[cutoff] = '\0';
return cutoff;
}
+/*
+ * This function will do static formatting (for things that don't
+ * change on each log message).
+ *
+ * %P PID
+ * %N name passed into qb_log_init
+ * %H hostname
+ *
+ * any number between % and character specify field length to pad or chop
+ */
+void
+qb_log_target_format_static(int32_t target, const char * format,
+ char *output_buffer)
+{
+ char tmp_buf[255];
+ unsigned int format_buffer_idx = 0;
+ unsigned int output_buffer_idx = 0;
+ size_t cutoff;
+ uint32_t len;
+ int ralign;
+ int c;
+ struct qb_log_target *t = qb_log_target_get(target);
+
+ if (format == NULL) {
+ return;
+ }
+
+ while ((c = format[format_buffer_idx])) {
+ cutoff = 0;
+ ralign = 0;
+ if (c != '%') {
+ output_buffer[output_buffer_idx++] = c;
+ format_buffer_idx++;
+ } else {
+ const char *p;
+ unsigned int percent_buffer_idx = format_buffer_idx;
+
+ format_buffer_idx += 1;
+ if (format[format_buffer_idx] == '-') {
+ ralign = 1;
+ format_buffer_idx += 1;
+ }
+
+ if (isdigit(format[format_buffer_idx])) {
+ cutoff = atoi(&format[format_buffer_idx]);
+ }
+ while (isdigit(format[format_buffer_idx])) {
+ format_buffer_idx += 1;
+ }
+
+ switch (format[format_buffer_idx]) {
+ case 'P':
+ snprintf(tmp_buf, 30, "%d", getpid());
+ p = tmp_buf;
+ break;
+
+ case 'N':
+ p = t->name;
+ break;
+
+ case 'H':
+ if (gethostname(tmp_buf, 255) == 0) {
+ tmp_buf[254] = '\0';
+ } else {
+ (void)strlcpy(tmp_buf, "localhost", 255);
+ }
+ p = tmp_buf;
+ break;
+
+ default:
+ p = &format[percent_buffer_idx];
+ cutoff = (format_buffer_idx - percent_buffer_idx + 1);
+ ralign = 0;
+ break;
+ }
+ len = _strcpy_cutoff(output_buffer + output_buffer_idx,
+ p, cutoff, ralign,
+ (QB_LOG_MAX_LEN -
+ output_buffer_idx));
+ output_buffer_idx += len;
+ format_buffer_idx += 1;
+ }
+ if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
+ break;
+ }
+ }
+
+ output_buffer[output_buffer_idx] = '\0';
+}
+
/*
* %n FUNCTION NAME
* %f FILENAME
* %l FILELINE
* %p PRIORITY
* %t TIMESTAMP
* %b BUFFER
* %g SUBSYSTEM
*
* any number between % and character specify field length to pad or chop
*/
void
qb_log_target_format(int32_t target,
struct qb_log_callsite *cs,
time_t current_time,
const char *formatted_message, char *output_buffer)
{
- char char_time[128];
+ char tmp_buf[128];
struct tm tm_res;
- char line_no[30];
unsigned int format_buffer_idx = 0;
unsigned int output_buffer_idx = 0;
size_t cutoff;
uint32_t len;
int ralign;
int c;
struct qb_log_target *t = qb_log_target_get(target);
if (t->format == NULL) {
return;
}
while ((c = t->format[format_buffer_idx])) {
cutoff = 0;
ralign = 0;
if (c != '%') {
output_buffer[output_buffer_idx++] = c;
format_buffer_idx++;
} else {
const char *p;
format_buffer_idx += 1;
if (t->format[format_buffer_idx] == '-') {
ralign = 1;
format_buffer_idx += 1;
}
if (isdigit(t->format[format_buffer_idx])) {
cutoff = atoi(&t->format[format_buffer_idx]);
}
while (isdigit(t->format[format_buffer_idx])) {
format_buffer_idx += 1;
}
switch (t->format[format_buffer_idx]) {
case 'g':
if (_user_tags_stringify_fn) {
p = _user_tags_stringify_fn(cs->tags);
} else {
p = "";
}
break;
case 'n':
p = cs->function;
break;
case 'f':
#ifdef BUILDING_IN_PLACE
p = cs->filename;
#else
p = strrchr(cs->filename, '/');
if (p == NULL) {
p = cs->filename;
} else {
p++; /* move past the "/" */
}
#endif /* BUILDING_IN_PLACE */
break;
case 'l':
- snprintf(line_no, 30, "%d", cs->lineno);
- p = line_no;
+ snprintf(tmp_buf, 30, "%d", cs->lineno);
+ p = tmp_buf;
break;
case 't':
(void)localtime_r(&current_time, &tm_res);
- snprintf(char_time, TIME_STRING_SIZE,
+ snprintf(tmp_buf, TIME_STRING_SIZE,
"%s %02d %02d:%02d:%02d",
log_month_name[tm_res.tm_mon],
tm_res.tm_mday, tm_res.tm_hour,
tm_res.tm_min, tm_res.tm_sec);
- p = char_time;
+ p = tmp_buf;
break;
case 'b':
p = formatted_message;
break;
case 'p':
if (cs->priority > LOG_TRACE) {
p = prioritynames[LOG_TRACE].c_name;
} else {
p = prioritynames[cs->priority].c_name;
}
break;
- case 'P':
- snprintf(line_no, 30, "%d", getpid());
- p = line_no;
- break;
-
default:
p = "";
break;
}
len = _strcpy_cutoff(output_buffer + output_buffer_idx,
p, cutoff, ralign,
(QB_LOG_MAX_LEN -
output_buffer_idx));
output_buffer_idx += len;
format_buffer_idx += 1;
}
if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
break;
}
}
if (output_buffer[output_buffer_idx - 1] == '\n') {
output_buffer[output_buffer_idx - 1] = '\0';
} else {
output_buffer[output_buffer_idx] = '\0';
}
}
size_t
qb_vsprintf_serialize(char *serialize, const char *fmt, va_list ap)
{
char *format;
char *p;
uint32_t location = 0;
int type_long = 0;
int type_longlong = 0;
p = stpcpy(serialize, fmt);
location = p - serialize + 1;
format = (char *)fmt;
for (;;) {
type_long = 0;
type_longlong = 0;
p = strchrnul((const char *)format, '%');
if (*p == '\0') {
break;
}
format = p + 1;
reprocess:
switch (format[0]) {
case '#': /* alternate form conversion, ignore */
case '-': /* left adjust, ignore */
case ' ': /* a space, ignore */
case '+': /* a sign should be used, ignore */
case '\'': /* group in thousands, ignore */
case 'I': /* glibc-ism locale alternative, ignore */
case '.': /* precision, ignore */
case '0': /* field width, ignore */
case '1': /* field width, ignore */
case '2': /* field width, ignore */
case '3': /* field width, ignore */
case '4': /* field width, ignore */
case '5': /* field width, ignore */
case '6': /* field width, ignore */
case '7': /* field width, ignore */
case '8': /* field width, ignore */
case '9': /* field width, ignore */
format++;
goto reprocess;
case '*': /* variable field width, save */ {
int arg_int = va_arg(ap, int);
memcpy(&serialize[location], &arg_int, sizeof (int));
location += sizeof(int);
format++;
goto reprocess;
}
case 'l':
format++;
type_long = 1;
if (*format == 'l') {
type_long = 0;
type_longlong = 1;
format++;
}
goto reprocess;
case 'd': /* int argument */
case 'i': /* int argument */
case 'o': /* unsigned int argument */
case 'u':
case 'x':
case 'X':
if (type_long) {
long int arg_int;
arg_int = va_arg(ap, long int);
memcpy(&serialize[location], &arg_int,
sizeof(long int));
location += sizeof(long int);
format++;
break;
} else if (type_longlong) {
long long int arg_int;
arg_int = va_arg(ap, long long int);
memcpy(&serialize[location], &arg_int,
sizeof(long long int));
location += sizeof(long long int);
format++;
break;
} else {
int arg_int;
arg_int = va_arg(ap, int);
memcpy(&serialize[location], &arg_int,
sizeof(int));
location += sizeof(int);
format++;
break;
}
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
{
double arg_double;
arg_double = va_arg(ap, double);
memcpy (&serialize[location], &arg_double, sizeof (double));
location += sizeof(double);
format++;
break;
}
case 'c':
{
int arg_int;
unsigned char arg_char;
arg_int = va_arg(ap, unsigned int);
arg_char = (unsigned char)arg_int;
memcpy (&serialize[location], &arg_char, sizeof (unsigned char));
location += sizeof(unsigned char);
break;
}
case 's':
{
char *arg_string;
arg_string = va_arg(ap, char *);
p = stpcpy(&serialize[location], arg_string);
location += p - &serialize[location] + 1;
break;
}
case 'p':
{
void *arg_pointer;
arg_pointer = va_arg(ap, void *);
memcpy (&serialize[location], &arg_pointer, sizeof (void *));
location += sizeof (arg_pointer);
break;
}
case '%':
serialize[location++] = '%';
break;
}
}
return (location);
}
#define MINI_FORMAT_STR_LEN 20
size_t
qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf)
{
char *p;
char *format;
char fmt[MINI_FORMAT_STR_LEN];
int fmt_pos;
uint32_t location = 0;
uint32_t data_pos = strlen(buf) + 1;
int type_long = 0;
int type_longlong = 0;
int len;
format = (char *)buf;
for (;;) {
type_long = 0;
type_longlong = 0;
p = strchrnul((const char *)format, '%');
if (*p == '\0') {
p = stpcpy(&string[location], format);
location += p - &string[location] + 1;
break;
}
/* copy from current to the next % */
len = p - format;
strncpy(&string[location], format, len);
location += len;
format = p;
/* start building up the format for snprintf */
fmt_pos = 0;
fmt[fmt_pos++] = *format;
format++;
reprocess:
switch (format[0]) {
case '#': /* alternate form conversion, ignore */
case '-': /* left adjust, ignore */
case ' ': /* a space, ignore */
case '+': /* a sign should be used, ignore */
case '\'': /* group in thousands, ignore */
case 'I': /* glibc-ism locale alternative, ignore */
case '.': /* precision, ignore */
case '0': /* field width, ignore */
case '1': /* field width, ignore */
case '2': /* field width, ignore */
case '3': /* field width, ignore */
case '4': /* field width, ignore */
case '5': /* field width, ignore */
case '6': /* field width, ignore */
case '7': /* field width, ignore */
case '8': /* field width, ignore */
case '9': /* field width, ignore */
fmt[fmt_pos++] = *format;
format++;
goto reprocess;
case '*': {
int arg_int;
memcpy(&arg_int, &buf[data_pos], sizeof(int));
data_pos += sizeof(int);
fmt_pos += snprintf(&fmt[fmt_pos],
MINI_FORMAT_STR_LEN - fmt_pos,
"%d", arg_int);
format++;
goto reprocess;
}
case 'l':
fmt[fmt_pos++] = *format;
format++;
type_long = 1;
if (*format == 'l') {
type_long = 0;
type_longlong = 1;
}
goto reprocess;
case 'd': /* int argument */
case 'i': /* int argument */
case 'o': /* unsigned int argument */
case 'u':
case 'x':
case 'X':
if (type_long) {
long int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(long int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(long int);
format++;
break;
} else if (type_longlong) {
long long int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(long long int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(long long int);
format++;
break;
} else {
int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(int);
format++;
break;
}
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
{
double arg_double;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_double, &buf[data_pos], sizeof(double));
location += snprintf(&string[location],
str_len - location,
fmt, arg_double);
data_pos += sizeof(double);
format++;
break;
}
case 'c':
{
unsigned char *arg_char;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
arg_char = (unsigned char*)&buf[data_pos];
location += snprintf(&string[location],
str_len - location,
fmt, *arg_char);
data_pos += sizeof(unsigned char);
format++;
break;
}
case 's':
{
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
len = snprintf(&string[location],
str_len - location,
fmt, &buf[data_pos]);
location += len;
data_pos += len + 1;
format++;
break;
}
case 'p':
{
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
location = snprintf(&string[location],
str_len - location,
fmt, &buf[data_pos]);
data_pos += sizeof(void*);
format++;
break;
}
case '%':
string[location++] = '%';
format++;
break;
}
}
return location;
}
diff --git a/lib/log_int.h b/lib/log_int.h
index a1755a9..88f4b0c 100644
--- a/lib/log_int.h
+++ b/lib/log_int.h
@@ -1,100 +1,102 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _QB_LOG_INT_H_
#define _QB_LOG_INT_H_
#include <qb/qblist.h>
#include <qb/qblog.h>
#include <qb/qbrb.h>
struct qb_log_target;
struct qb_log_target {
uint32_t pos;
enum qb_log_target_state state;
char name[PATH_MAX];
char filename[PATH_MAX];
struct qb_list_head filter_head;
int32_t facility;
int32_t priority_bump;
int32_t debug;
size_t size;
char *format;
int32_t threaded;
void *instance;
qb_log_reload_fn reload;
qb_log_close_fn close;
qb_log_logger_fn logger;
qb_log_vlogger_fn vlogger;
};
struct qb_log_filter {
enum qb_log_filter_conf conf;
enum qb_log_filter_type type;
char *text;
uint8_t high_priority;
uint8_t low_priority;
uint32_t new_value;
struct qb_list_head list;
};
struct qb_log_record {
struct qb_log_callsite *cs;
time_t timestamp;
char *buffer;
struct qb_list_head list;
};
#define TIME_STRING_SIZE 64
struct qb_log_target * qb_log_target_alloc(void);
void qb_log_target_free(struct qb_log_target *t);
struct qb_log_target * qb_log_target_get(int32_t pos);
int32_t qb_log_syslog_open(struct qb_log_target *t);
int32_t qb_log_stderr_open(struct qb_log_target *t);
int32_t qb_log_blackbox_open(struct qb_log_target *t);
void qb_log_thread_stop(void);
void qb_log_thread_log_post(struct qb_log_callsite *cs,
time_t current_time,
const char *buffer);
void qb_log_thread_log_write(struct qb_log_callsite *cs,
time_t current_time,
const char *buffer);
void qb_log_dcs_init(void);
void qb_log_dcs_fini(void);
struct qb_log_callsite *qb_log_dcs_get(int32_t *newly_created,
const char *function,
const char *filename,
const char *format,
uint8_t priority,
uint32_t lineno,
uint32_t tags);
size_t qb_vsprintf_serialize(char *serialize, const char *fmt, va_list ap);
size_t qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf);
+void qb_log_target_format_static(int32_t target, const char * format, char *output_buffer);
+
#endif /* _QB_LOG_INT_H_ */
diff --git a/tests/check_log.c b/tests/check_log.c
index 318de9a..4b06e67 100644
--- a/tests/check_log.c
+++ b/tests/check_log.c
@@ -1,602 +1,618 @@
/*
* Copyright (c) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <pthread.h>
#include <check.h>
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qblog.h>
extern size_t qb_vsprintf_serialize(char *serialize, const char *fmt, va_list ap);
extern size_t qb_vsnprintf_deserialize(char *string, size_t strlen, const char *buf);
static void
format_this(char *out, const char *fmt, ...)
{
char buf[QB_LOG_MAX_LEN];
va_list ap;
va_start(ap, fmt);
qb_vsprintf_serialize(buf, fmt, ap);
qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf);
va_end(ap);
}
START_TEST(test_va_serialize)
{
char buf[QB_LOG_MAX_LEN];
format_this(buf, "one line");
ck_assert_str_eq(buf, "one line");
format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
ck_assert_str_eq(buf, "s1:Yes, s2:Never");
format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
ck_assert_str_eq(buf, "s1:Yes, s2:Never");
format_this(buf, "d1:%d, d2:%5i, d3:%04i", 23, 37, 84);
ck_assert_str_eq(buf, "d1:23, d2: 37, d3:0084");
format_this(buf, "f1:%.5f, f2:%.2f", 23.34109, 23.34109);
ck_assert_str_eq(buf, "f1:23.34109, f2:23.34");
format_this(buf, ":%s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%15s:", "Hello, world!");
ck_assert_str_eq(buf, ": Hello, world!:");
format_this(buf, ":%.10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, wor:");
format_this(buf, ":%-10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%-15s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world! :");
format_this(buf, ":%.15s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%15.10s:", "Hello, world!");
ck_assert_str_eq(buf, ": Hello, wor:");
format_this(buf, ":%-15.10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, wor :");
format_this(buf, ":%*d:", 8, 96);
ck_assert_str_eq(buf, ": 96:");
}
END_TEST
START_TEST(test_log_stupid_inputs)
{
int32_t rc;
/* shouldn't crash with out an init() */
qb_log_fini();
/* not init'ed */
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 2000);
ck_assert_int_eq(rc, -EINVAL);
qb_log(LOG_INFO, "not init'd");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "also not init'd");
qb_log_init("test", LOG_USER, LOG_DEBUG);
/* non-opened log file */
rc = qb_log_filter_ctl(21, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EBADF);
rc = qb_log_ctl(21, QB_LOG_CONF_PRIORITY_BUMP, -1);
ck_assert_int_eq(rc, -EBADF);
/* target < 0 or >= 32 */
rc = qb_log_filter_ctl(41, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EBADF);
rc = qb_log_ctl(-1, QB_LOG_CONF_PRIORITY_BUMP, -1);
ck_assert_int_eq(rc, -EBADF);
/* crap values to filter_ctl() */
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, NULL, LOG_INFO);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, 56,
QB_LOG_FILTER_FILE, "boja", LOG_INFO);
ck_assert_int_eq(rc, -EINVAL);
/* crap values to ctl() */
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, -2000);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_BLACKBOX, 67, 2000);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_SIZE, 2000);
ck_assert_int_eq(rc, -ENOSYS);
}
END_TEST
static char test_buf[QB_LOG_MAX_LEN];
static int32_t test_priority;
static int32_t num_msgs;
/*
* to test that we get what we expect.
*/
static void
_test_logger(int32_t t,
struct qb_log_callsite *cs,
time_t timestamp, const char *msg)
{
test_buf[0] = '\0';
qb_log_target_format(t, cs, timestamp, msg, test_buf);
test_priority = cs->priority;
num_msgs++;
}
static void log_also(void)
{
qb_log(LOG_INFO, "yes please");
}
static void log_and_this_too(void)
{
qb_log(LOG_INFO, "this too please");
}
static void log_it_please(void)
{
qb_enter();
qb_log(LOG_TRACE, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_DEBUG, "A:%d B:%d C:%d", 1, 2, 3);
errno = EEXIST;
qb_perror(LOG_WARNING, "bogus error");
errno = 0;
qb_log(LOG_INFO, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_NOTICE, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_WARNING, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_ERR, "A:%d B:%d C:%d", 1, 2, 3);
qb_leave();
}
static int32_t _cust_t = -1;
static void
m_filter(struct qb_log_callsite *cs)
{
if ((cs->priority >= LOG_ALERT &&
cs->priority <= LOG_INFO) ||
cs->tags > 0) {
qb_bit_set(cs->targets, _cust_t);
} else {
qb_bit_clear(cs->targets, _cust_t);
}
}
START_TEST(test_log_filter_fn)
{
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
_cust_t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
_ck_assert_int(_cust_t, >, QB_LOG_BLACKBOX);
rc = qb_log_ctl(_cust_t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
/*
* test the custom filter function.
* make sure qb_log, and qb_log_from_external_source are filtered.
*/
qb_log_filter_fn_set(m_filter);
num_msgs = 0;
qb_log(LOG_NOTICE, "qb_log_filter_fn_set good");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "qb_log_filter_fn_set good");
qb_log(LOG_TRACE, "qb_log_filter_fn_set bad");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
__LINE__, 44, "qb_log_filter_fn_set woot");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
__LINE__, 0, "qb_log_filter_fn_set bad");
ck_assert_int_eq(num_msgs, 3);
}
END_TEST
START_TEST(test_log_basic)
{
int32_t t;
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FORMAT, "Angus", LOG_WARNING);
ck_assert_int_eq(rc, 0);
qb_log_format_set(t, "%b");
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
memset(test_buf, 0, sizeof(test_buf));
test_priority = 0;
num_msgs = 0;
/*
* test filtering by format
*/
qb_log(LOG_INFO, "Hello Angus, how are you?");
qb_log(LOG_WARNING, "Hello Steven, how are you?");
qb_log(LOG_ERR, "Hello Andrew, how are you?");
qb_log(LOG_ERR, "Hello Angus, how are you?");
qb_log(LOG_EMERG, "Hello Anna, how are you?");
ck_assert_int_eq(test_priority, LOG_ERR);
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "Hello Angus, how are you?");
/*
* test filtering by function
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
num_msgs = 0;
qb_log(LOG_ERR, "try if you: log_it_please()");
log_it_please();
ck_assert_int_eq(num_msgs, 3);
qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE,
QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION, __func__, LOG_DEBUG);
num_msgs = 0;
log_it_please();
ck_assert_int_eq(num_msgs, 0);
qb_log(LOG_DEBUG, "try if you: log_it_please()");
ck_assert_int_eq(num_msgs, 1);
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION,
"log_also,log_and_this_too",
LOG_DEBUG);
num_msgs = 0;
log_also();
log_and_this_too();
ck_assert_int_eq(num_msgs, 2);
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, __FILE__, LOG_DEBUG);
/*
* make sure we can pass in a null filename or function name.
*/
qb_log_from_external_source(__func__, NULL, "%s", LOG_INFO,
__LINE__, 0, "null filename");
qb_log_from_external_source(NULL, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "null function");
/* check same file/lineno logs with different formats work
*/
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__, "%s bla", LOG_INFO,
56, 0, "filename/lineno");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "filename/lineno bla");
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
56, 0, "same filename/lineno");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "same filename/lineno");
/* check filtering works on same file/lineno but different
* log level.
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, __FILE__, LOG_INFO);
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__,
"same filename/lineno, this level %d",
LOG_INFO, 56, 0, LOG_INFO);
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "same filename/lineno, this level 6");
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__,
"same filename/lineno, this level %d",
LOG_DEBUG, 56, 0, LOG_DEBUG);
ck_assert_int_eq(num_msgs, 0);
}
END_TEST
static const char *_test_tags_stringify(uint32_t tags)
{
if (tags == 1) {
return "ONE";
} else if (tags == 8) {
return "ATE";
} else {
return "ANY";
}
}
START_TEST(test_log_format)
{
int32_t t;
+ char cmp_str[256];
+ char host_str[256];
qb_log_init("test", LOG_USER, LOG_DEBUG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_format_set(t, "%p %f %b");
qb_log(LOG_DEBUG, "Angus");
ck_assert_str_eq(test_buf, "debug check_log.c Angus");
qb_log(LOG_INFO, "Angus");
ck_assert_str_eq(test_buf, "info check_log.c Angus");
qb_log(LOG_NOTICE, "Angus");
ck_assert_str_eq(test_buf, "notice check_log.c Angus");
qb_log(LOG_WARNING, "Angus");
ck_assert_str_eq(test_buf, "warning check_log.c Angus");
qb_log(LOG_ERR, "Angus");
ck_assert_str_eq(test_buf, "error check_log.c Angus");
qb_log(LOG_CRIT, "Angus");
ck_assert_str_eq(test_buf, "crit check_log.c Angus");
qb_log(LOG_ALERT, "Angus");
ck_assert_str_eq(test_buf, "alert check_log.c Angus");
qb_log(LOG_EMERG, "Angus");
ck_assert_str_eq(test_buf, "emerg check_log.c Angus");
qb_log_tags_stringify_fn_set(_test_tags_stringify);
qb_log_format_set(t, "%g %b");
qb_logt(LOG_INFO, 0, "Angus");
ck_assert_str_eq(test_buf, "ANY Angus");
qb_logt(LOG_INFO, 1, "Angus");
ck_assert_str_eq(test_buf, "ONE Angus");
qb_logt(LOG_INFO, 5, "Angus");
ck_assert_str_eq(test_buf, "ANY Angus");
qb_logt(LOG_INFO, 8, "Angus");
ck_assert_str_eq(test_buf, "ATE Angus");
qb_log_format_set(t, "%-15f %b");
qb_log(LOG_WARNING, "Andrew");
ck_assert_str_eq(test_buf, " check_log.c Andrew");
+
+ qb_log_tags_stringify_fn_set(NULL);
+
+ gethostname(host_str, 256);
+
+ qb_log_format_set(t, "%P %H %N %b");
+ qb_log(LOG_INFO, "Angus");
+ snprintf(cmp_str, 256, "%d %s test Angus", getpid(), host_str);
+ ck_assert_str_eq(test_buf, cmp_str);
+
+ qb_log_format_set(t, "%3N %H %P %b");
+ qb_log(LOG_INFO, "Angus");
+ snprintf(cmp_str, 256, "tes %s %d Angus", host_str, getpid());
+ ck_assert_str_eq(test_buf, cmp_str);
}
END_TEST
START_TEST(test_log_enable)
{
int32_t t;
int32_t state;
qb_log_init("test", LOG_USER, LOG_DEBUG);
state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_ENABLED);
state = qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_format_set(t, "%b");
qb_log(LOG_DEBUG, "Hello");
ck_assert_str_eq(test_buf, "Hello");
num_msgs = 0;
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_FALSE);
qb_log(LOG_DEBUG, "Goodbye");
ck_assert_int_eq(num_msgs, 0);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log(LOG_DEBUG, "Hello again");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "Hello again");
}
END_TEST
#define ITERATIONS 100000
static void *thr_send_logs_2(void *ctx)
{
int32_t i;
printf("%s\n", __func__);
for (i = 0; i < ITERATIONS; i++) {
qb_log(LOG_INFO, "bla bla");
qb_log(LOG_INFO, "blue blue");
qb_log(LOG_INFO, "bra bra");
qb_log(LOG_INFO, "bro bro");
qb_log(LOG_INFO, "brown brown");
qb_log(LOG_INFO, "booo booo");
qb_log(LOG_INFO, "bogus bogus");
qb_log(LOG_INFO, "bungu bungu");
}
return (NULL);
}
static void *thr_send_logs_1(void *ctx)
{
int32_t i;
printf("%s\n", __func__);
for (i = 0; i < ITERATIONS; i++) {
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "foo soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fungus soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fruity soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "free soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "frot soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fresh soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fattening soup");
}
return (NULL);
}
#define THREADS 4
START_TEST(test_log_threads)
{
pthread_t threads[THREADS];
pthread_attr_t thread_attr[THREADS];
int32_t i;
int32_t rc;
int32_t lf;
void *retval;
qb_log_init("test", LOG_USER, LOG_DEBUG);
lf = qb_log_file_open("threads.log");
rc = qb_log_filter_ctl(lf, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE,
__FILE__, LOG_DEBUG);
ck_assert_int_eq(rc, 0);
qb_log_format_set(lf, "[%p] [%l] %b");
rc = qb_log_ctl(lf, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
ck_assert_int_eq(rc, 0);
for (i = 0; i < THREADS/2; i++) {
pthread_attr_init(&thread_attr[i]);
pthread_attr_setdetachstate(&thread_attr[i],
PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &thread_attr[i],
thr_send_logs_1, NULL);
}
for (i = THREADS/2; i < THREADS; i++) {
pthread_attr_init(&thread_attr[i]);
pthread_attr_setdetachstate(&thread_attr[i],
PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &thread_attr[i],
thr_send_logs_2, NULL);
}
for (i = 0; i < THREADS; i++) {
pthread_join(threads[i], &retval);
}
}
END_TEST
START_TEST(test_log_long_msg)
{
qb_log_init("test", LOG_USER, LOG_DEBUG);
qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_format_set(QB_LOG_SYSLOG, "%b");
qb_log(LOG_ERR, "QMF Agent Initialized: broker=localhost:49000 interval=5 storeFile=.cloudpolicyengine-data-cpe name=cloudpolicyengine.org:cpe:04eabb39-89cf-47dd-9d14-7e77d864be07 QMF Agent Initialized: broker=localhost:49000 interval=5 storeFile=.cloudpolicyengine-data-cpe name=cloudpolicyengine.org:cpe:04eabb39-89cf-47dd-9d14-7e77d864be07 QMF Agent Initialized: broker=localhost:49000 interval=5 storeFile=.cloudpolicyengine-data-cpe name=cloudpolicyengine.org:cpe:04eabb39-89cf-47dd-9d14-7e77d864be07");
}
END_TEST
static Suite *log_suite(void)
{
TCase *tc;
Suite *s = suite_create("logging");
tc = tcase_create("va_serialize");
tcase_add_test(tc, test_va_serialize);
suite_add_tcase(s, tc);
tc = tcase_create("limits");
tcase_add_test(tc, test_log_stupid_inputs);
suite_add_tcase(s, tc);
tc = tcase_create("basic");
tcase_add_test(tc, test_log_basic);
suite_add_tcase(s, tc);
tc = tcase_create("format");
tcase_add_test(tc, test_log_format);
suite_add_tcase(s, tc);
tc = tcase_create("enable");
tcase_add_test(tc, test_log_enable);
suite_add_tcase(s, tc);
tc = tcase_create("threads");
tcase_add_test(tc, test_log_threads);
tcase_set_timeout(tc, 360);
suite_add_tcase(s, tc);
tc = tcase_create("long_msg");
tcase_add_test(tc, test_log_long_msg);
suite_add_tcase(s, tc);
tc = tcase_create("filter_ft");
tcase_add_test(tc, test_log_filter_fn);
suite_add_tcase(s, tc);
return s;
}
int32_t
main(void)
{
int32_t number_failed;
Suite *s = log_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_VERBOSE);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Feb 26, 1:33 PM (21 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1465389
Default Alt Text
(77 KB)

Event Timeline