Add logging infrastructure.

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2011-03-25 23:34:12 +11:00
parent ea704d5821
commit e910f8bc18
9 changed files with 413 additions and 3 deletions

View File

@ -26,7 +26,7 @@ inc_dir = $(top_srcdir)/include/qb
dependant_headers = $(wildcard $(inc_dir)/qb*.h)
dist_man_MANS = man3/qbipcc.h.3 man3/qbipcs.h.3 man3/qbatomic.h.3 \
man3/qbhdb.h.3 man3/qbipc_common.h.3 man3/qblist.h.3 \
man3/qbloop.h.3 man3/qbutil.h.3 man3/qbarray.h.3
man3/qbloop.h.3 man3/qbutil.h.3 man3/qbarray.h.3 man3/qblog.h
$(dist_man_MANS): man.dox $(dependant_headers)

View File

@ -51,6 +51,12 @@
* @see qbloop.h
*/
/**
* @page qb_log_overview Logging
* @copydoc qblog.h
* @see qblog.h
*/
/**
* @page qb_ipc_overview IPC Overview
*

View File

@ -47,6 +47,16 @@ extern "C" {
#define QB_FALSE 0
#define QB_TRUE (!QB_FALSE)
/*
* bit manipulation
*/
#define qb_bit_value(bit) (1 << (bit))
#define qb_bit_set(barray, bit) (barray |= qb_bit_value(bit))
#define qb_bit_clear(barray, bit) (barray &= ~(qb_bit_value(bit)))
#define qb_bit_is_set(barray, bit) (barray & qb_bit_value(bit))
#define qb_bit_is_clear(barray, bit) (!(barray & qb_bit_value(bit)))
/*
* handy time based converters.
*/

113
include/qb/qblog.h Normal file
View File

@ -0,0 +1,113 @@
/*
* 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
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
/**
* @file qblog.h
* This is a the logging API.
*/
/*
* 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 tags;
} __attribute__((aligned(8)));
typedef struct qb_log_filter qb_log_filter_t;
typedef void (*qb_log_logger_fn)(struct qb_log_callsite *cs,
const char *msg);
/* will be assigned by ld linker magic */
extern struct qb_log_callsite __start___verbose[];
extern struct qb_log_callsite __stop___verbose[];
/**
* Internal function use qb_log()
*/
void qb_log_real_(struct qb_log_callsite *cs,
int32_t error_number, ...);
/**
* 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...) do { \
static struct qb_log_callsite descriptor \
__attribute__((section("__verbose"), aligned(8))) = \
{ __func__, __FILE__, fmt, priority, __LINE__, 0 }; \
qb_log_real_(&descriptor, 0, ##args); \
} while(0)
/**
* Create a filter to tag the callsites.
*/
qb_log_filter_t* qb_log_filter_create(void);
/**
* destroy a filter.
*/
void qb_log_filter_destroy(struct qb_log_filter* flt);
/**
* Set the priority on the filter
*/
int32_t qb_log_filter_priority_set(qb_log_filter_t* flt, uint8_t priority);
/**
* add a chunk of code to a filter.
*/
void qb_log_filter_file_add(struct qb_log_filter* flt,
const char* filename,
int32_t start, int32_t end);
/**
* tag the callsites defined by the filter.
*/
void qb_log_tag(struct qb_log_filter* flt, int32_t is_set, int32_t tag_bit);
/**
* set your log handling function.
*/
void qb_log_handler_set(qb_log_logger_fn logger_fn);
#endif /* QB_LOG_H_DEFINED */

View File

@ -40,7 +40,8 @@ libqb_la_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
libqb_la_LDFLAGS = -version-info 4:1:4
source_to_lint = util.c hdb.c ringbuffer.c ringbuffer_helper.c array.c \
loop.c loop_poll.c loop_job.c \
ipcc.c ipcs.c ipc_posix_mq.c ipc_sysv_mq.c ipc_shm.c ipc_us.c
ipcc.c ipcs.c ipc_posix_mq.c ipc_sysv_mq.c ipc_shm.c ipc_us.c \
log.c
libqb_la_SOURCES = $(source_to_lint)
if USE_TIMERFD
else

159
lib/log.c Normal file
View File

@ -0,0 +1,159 @@
/*
* 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 <config.h>
#include "os_base.h"
#include <ctype.h>
#include <stdarg.h>
#include <qb/qbdefs.h>
#include <qb/qblist.h>
#include <qb/qblog.h>
struct qb_log_filter_file {
char *filename;
int32_t start;
int32_t end;
struct qb_list_head list;
};
struct qb_log_filter {
uint8_t priority;
struct qb_list_head files_head;
};
struct qb_log_destination {
qb_log_logger_fn logger;
};
#define COMBINE_BUFFER_SIZE 256
static struct qb_log_destination destination;
void qb_log_real_(struct qb_log_callsite *cs,
int32_t error_number, ...)
{
va_list ap;
char buf[COMBINE_BUFFER_SIZE];
size_t len;
static int32_t in_logger = 0;
if (destination.logger == NULL) {
return;
}
if (in_logger) {
return;
}
in_logger = 1;
va_start(ap, error_number);
len = vsnprintf(buf, COMBINE_BUFFER_SIZE, cs->format, ap);
va_end(ap);
if (buf[len - 1] == '\n') {
buf[len - 1] = '\0';
len -= 1;
}
destination.logger(cs, buf);
in_logger = 0;
}
qb_log_filter_t* qb_log_filter_create(void)
{
struct qb_log_filter *flt = calloc(1, sizeof(struct qb_log_filter));
qb_list_init(&flt->files_head);
flt->priority = LOG_INFO;
return flt;
}
void qb_log_filter_destroy(struct qb_log_filter* flt)
{
struct qb_log_filter_file *ff;
struct qb_list_head *item;
struct qb_list_head *next;
qb_list_for_each_safe(item, next, &flt->files_head) {
ff = qb_list_entry(item, struct qb_log_filter_file, list);
qb_list_del(item);
free(ff);
}
free(flt);
}
int32_t qb_log_filter_priority_set(struct qb_log_filter* flt, uint8_t priority)
{
flt->priority = priority;
return 0;
}
void qb_log_filter_file_add(struct qb_log_filter* flt,
const char* filename,
int32_t start, int32_t end)
{
struct qb_log_filter_file *ff = calloc(1, sizeof(struct qb_log_filter_file));
qb_list_init(&ff->list);
ff->filename = (char*)filename;
ff->start = start;
ff->end = end;
qb_list_add(&ff->list, &flt->files_head);
}
void qb_log_tag(struct qb_log_filter* flt, int32_t is_set, int32_t tag_bit)
{
struct qb_log_callsite *cs;
struct qb_log_filter_file *ff;
int32_t match;
for (cs = __start___verbose; cs < __stop___verbose; cs++) {
if (cs->priority > flt->priority)
continue;
match = QB_FALSE;
qb_list_for_each_entry(ff, &flt->files_head, list) {
if (ff->start <= cs->lineno &&
ff->end >= cs->lineno &&
strcmp(ff->filename, cs->filename) == 0) {
match = QB_TRUE;
}
}
if (match || qb_list_empty(&flt->files_head)) {
if (is_set) {
qb_bit_set(cs->tags, tag_bit);
} else {
qb_bit_clear(cs->tags, tag_bit);
}
#if 0
printf("matched: %-12s %20s:%u fmt:<%d>%s\n",
cs->function, cs->filename, cs->lineno,
cs->priority, cs->format);
#endif
}
}
}
void qb_log_handler_set(qb_log_logger_fn logger_fn)
{
destination.logger = NULL;
qb_log(LOG_DEBUG, " ");
destination.logger = logger_fn;
}

1
tests/.gitignore vendored
View File

@ -8,3 +8,4 @@ bms
rbreader
rbwriter
loop
simple-log

View File

@ -18,7 +18,7 @@
# along with libqb. If not, see <http://www.gnu.org/licenses/>.
#
noinst_PROGRAMS = bmc bmcpt bms rbwriter rbreader loop
noinst_PROGRAMS = bmc bmcpt bms rbwriter rbreader loop simple-log
bmc_SOURCES = bmc.c $(top_builddir)/include/qb/qbipcc.h
bmc_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
@ -44,6 +44,10 @@ loop_SOURCES = loop.c $(top_builddir)/include/qb/qbloop.h
loop_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
loop_LDADD = -lrt $(top_builddir)/lib/libqb.la
simple_log_SOURCES = simple-log.c $(top_builddir)/include/qb/qblog.h
simple_log_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
simple_log_LDADD = -lrt $(top_builddir)/lib/libqb.la
if HAVE_CHECK
EXTRA_DIST = check_resources.sh

116
tests/simple-log.c Normal file
View File

@ -0,0 +1,116 @@
/*
* 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 <signal.h>
#include <syslog.h>
#include <qb/qbdefs.h>
#include <qb/qblog.h>
static void func_one(void) {
qb_log(LOG_DEBUG, "hello");
qb_log(LOG_CRIT, "hello");
qb_log(LOG_ERR, "hello");
qb_log(LOG_INFO, "hello");
}
static void func_two(void) {
qb_log(LOG_DEBUG, "arrrg!");
qb_log(LOG_CRIT, "arrrg!");
qb_log(LOG_ERR, "arrrg!");
qb_log(LOG_INFO, "arrrg!");
}
static void show_usage(const char *name)
{
printf("usage: \n");
printf("%s <options>\n", name);
printf("\n");
printf(" options:\n");
printf("\n");
printf(" -v verbose\n");
printf(" -h show this help text\n");
printf("\n");
}
#define MY_STDOUT_TAG 1
#define MY_LOG_TAG 2
static void my_log_writer(struct qb_log_callsite *cs, const char *msg)
{
if (qb_bit_is_set(cs->tags, MY_STDOUT_TAG)) {
fprintf(stdout, "[%s:%d] <%d> %s\n", cs->filename, cs->lineno, cs->priority, msg);
}
if (qb_bit_is_set(cs->tags, MY_LOG_TAG)) {
syslog(cs->priority, "%s", msg);
}
}
int32_t main(int32_t argc, char *argv[])
{
const char *options = "vh";
int32_t opt;
uint8_t priority = LOG_NOTICE;
qb_log_filter_t* ls1;
qb_log_filter_t* ls2;
while ((opt = getopt(argc, argv, options)) != -1) {
switch (opt) {
case 'v':
priority++;
break;
case 'h':
default:
show_usage(argv[0]);
exit(0);
break;
}
}
qb_log_handler_set(my_log_writer);
/* stdout logging */
ls1 = qb_log_filter_create();
qb_log_filter_priority_set(ls1, priority);
qb_log_filter_file_add(ls1, __FILE__, 1, 33); // line 1 to 33
qb_log_filter_file_add(ls1, __FILE__, 75, INT32_MAX); // line 75 to EOF
qb_log_tag(ls1, QB_TRUE, MY_STDOUT_TAG);
/* syslog */
ls2 = qb_log_filter_create();
qb_log_filter_priority_set(ls2, LOG_WARNING);
qb_log_tag(ls2, QB_TRUE, MY_LOG_TAG);
qb_log(LOG_DEBUG, "hello");
qb_log(LOG_INFO, "hello");
qb_log(LOG_NOTICE, "hello");
func_one();
func_two();
qb_log_tag(ls2, QB_FALSE, MY_LOG_TAG);
qb_log(LOG_WARNING, "no syslog");
qb_log(LOG_ERR, "no syslog");
qb_log_filter_destroy(ls1);
qb_log_filter_destroy(ls2);
return 0;
}