Initial import of libucore
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/queue.h>
|
||||
#include "ucore_logging.h"
|
||||
|
||||
|
||||
struct uc_log_destination {
|
||||
SLIST_ENTRY(uc_log_destination) entry;
|
||||
enum UC_LOG_DESTINATION dest_type;
|
||||
int log_level;
|
||||
int log_location;
|
||||
union {
|
||||
//for syslog
|
||||
struct {
|
||||
const char *ident;
|
||||
int facility;
|
||||
} syslog;
|
||||
//for stderr and files
|
||||
struct {
|
||||
FILE* file;
|
||||
char *file_name; //NULL for stderr destinaton
|
||||
} file;
|
||||
} type;
|
||||
};
|
||||
|
||||
struct uc_log_args {
|
||||
struct uc_log_destination *dest;
|
||||
const struct uc_log_module *module;
|
||||
int log_level;
|
||||
const char *file;
|
||||
int line;
|
||||
};
|
||||
|
||||
|
||||
//Global lock for the logging system, as logging could be performed from any thread. All operations must grab
|
||||
//this lock before accessing any the global data.
|
||||
static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static struct uc_log_modules modules;
|
||||
|
||||
static const struct uc_log_module unknown_module = {
|
||||
.id = -1,
|
||||
.short_name = "UNKNOWN",
|
||||
.long_name = "Unknown log module",
|
||||
.log_level = UC_LL_DEBUG,
|
||||
};
|
||||
|
||||
|
||||
static SLIST_HEAD(, uc_log_destination) destinations;
|
||||
|
||||
inline const char *uc_ll_2_str(enum UC_LOG_LEVEL l)
|
||||
{
|
||||
switch(l) {
|
||||
case UC_LL_NONE:
|
||||
return "NONE";
|
||||
case UC_LL_DEBUG:
|
||||
return "DEBUG";
|
||||
case UC_LL_INFO:
|
||||
return "INFO";
|
||||
case UC_LL_WARNING:
|
||||
return "WARNING";
|
||||
case UC_LL_ERROR:
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline int uc_ll_2_syslog(enum UC_LOG_LEVEL l)
|
||||
{
|
||||
switch(l) {
|
||||
case UC_LL_NONE:
|
||||
case UC_LL_DEBUG:
|
||||
return LOG_DEBUG;
|
||||
case UC_LL_INFO:
|
||||
return LOG_INFO;
|
||||
case UC_LL_WARNING:
|
||||
return LOG_WARNING;
|
||||
case UC_LL_ERROR:
|
||||
return LOG_ERR;
|
||||
}
|
||||
|
||||
return LOG_INFO;
|
||||
}
|
||||
|
||||
struct uc_log_destination *uc_log_new_stderr(int log_level, int log_location)
|
||||
{
|
||||
struct uc_log_destination *dest = calloc(1, sizeof *dest);
|
||||
|
||||
if(dest == NULL)
|
||||
return NULL;
|
||||
|
||||
dest->dest_type = UC_LDEST_STDERR;
|
||||
dest->type.file.file = stderr;
|
||||
dest->log_level = log_level;;
|
||||
dest->log_location = log_location;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
struct uc_log_destination *uc_log_new_syslog(const char *ident, int facility, int log_level, int log_location)
|
||||
{
|
||||
struct uc_log_destination *dest = calloc(1, sizeof *dest);
|
||||
|
||||
if(dest == NULL)
|
||||
return NULL;
|
||||
|
||||
openlog(ident, LOG_NDELAY | LOG_PID, facility);
|
||||
|
||||
dest->dest_type = UC_LDEST_SYSLOG;
|
||||
dest->type.syslog.ident = ident;
|
||||
dest->type.syslog.facility = facility;
|
||||
dest->log_level = log_level;;
|
||||
dest->log_location = log_location;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
struct uc_log_destination *uc_log_new_file(const char *filename, int log_level, int log_location)
|
||||
{
|
||||
struct uc_log_destination *dest = calloc(1, sizeof *dest);
|
||||
FILE *f;
|
||||
char *name;
|
||||
|
||||
if(dest == NULL)
|
||||
return NULL;
|
||||
|
||||
name = strdup(filename);
|
||||
if(name == NULL) {
|
||||
free(dest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = fopen(filename, "a");
|
||||
if(f == NULL) {
|
||||
free(name);
|
||||
free(dest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dest->dest_type = UC_LDEST_FILE;
|
||||
dest->type.file.file = f;
|
||||
dest->type.file.file_name = name;
|
||||
dest->log_level = log_level;;
|
||||
dest->log_location = log_location;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void uc_log_add_destination(struct uc_log_destination *dest)
|
||||
{
|
||||
struct uc_log_destination *it;
|
||||
|
||||
pthread_mutex_lock(&log_lock);
|
||||
|
||||
//make sure the destination isn't added twice.
|
||||
SLIST_FOREACH(it, &destinations, entry) {
|
||||
if(dest == it)
|
||||
break;
|
||||
}
|
||||
|
||||
//add if it isn't already in the list
|
||||
if(it == NULL) {
|
||||
SLIST_INSERT_HEAD(&destinations, dest, entry);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&log_lock);
|
||||
}
|
||||
|
||||
void uc_log_remove_destination(struct uc_log_destination *dest)
|
||||
{
|
||||
pthread_mutex_lock(&log_lock);
|
||||
|
||||
SLIST_REMOVE(&destinations, dest, uc_log_destination, entry);
|
||||
|
||||
pthread_mutex_unlock(&log_lock);
|
||||
}
|
||||
|
||||
int uc_log_reopen_files(void)
|
||||
{
|
||||
struct uc_log_destination *dest;
|
||||
int rc = 0;
|
||||
|
||||
pthread_mutex_lock(&log_lock);
|
||||
|
||||
SLIST_FOREACH(dest, &destinations, entry) {
|
||||
|
||||
if(dest->dest_type == UC_LDEST_FILE) {
|
||||
|
||||
if(dest->type.file.file != NULL) {
|
||||
fclose(dest->type.file.file);
|
||||
dest->type.file.file = NULL;
|
||||
}
|
||||
|
||||
if(dest->type.file.file_name != NULL) {
|
||||
dest->type.file.file = fopen(dest->type.file.file_name, "a");
|
||||
|
||||
if(dest->type.file.file == NULL)
|
||||
rc = errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&log_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void uc_log_init(struct uc_log_modules *user_modules)
|
||||
{
|
||||
modules = *user_modules;
|
||||
}
|
||||
|
||||
int uc_log_module_set_loglevel(int module, enum UC_LOG_LEVEL level)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&log_lock);
|
||||
|
||||
for(i = 0; i < modules.cnt; i++) {
|
||||
if(modules.mods[i].id == module) {
|
||||
modules.mods[i].log_level = level;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&log_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void uc_vlog_internal(const struct uc_log_args *args, const char *fmt, va_list ap)
|
||||
{
|
||||
struct uc_log_destination *dest = args->dest;
|
||||
|
||||
if(dest->dest_type != UC_LDEST_SYSLOG && dest->type.file.file != NULL) {
|
||||
char time_buf[48];
|
||||
time_t now;
|
||||
struct tm tmbuf;
|
||||
|
||||
now = time(NULL);
|
||||
localtime_r(&now, &tmbuf);
|
||||
strftime(time_buf, sizeof time_buf, "%d %b %Y %H:%M:%S %Z", &tmbuf);
|
||||
|
||||
if(dest->log_location) {
|
||||
fprintf(dest->type.file.file, "[%s] %s %s:%d [%s] : ", uc_ll_2_str(args->log_level),
|
||||
time_buf, args->file, args->line, args->module->short_name);
|
||||
} else {
|
||||
fprintf(dest->type.file.file, "[%s] %s [%s]: ",
|
||||
uc_ll_2_str(args->log_level), time_buf, args->module->short_name);
|
||||
}
|
||||
|
||||
|
||||
vfprintf(dest->type.file.file, fmt, ap);
|
||||
|
||||
fflush(dest->type.file.file); //strictly not needed for stderr
|
||||
// though stderr could be redirected to a file
|
||||
|
||||
} else if(dest->dest_type == UC_LDEST_SYSLOG) {
|
||||
int pri = uc_ll_2_syslog(args->log_level);
|
||||
|
||||
//go via a buffer as we must do just 1 syslog call
|
||||
char buf[4096];
|
||||
int len = 0;
|
||||
|
||||
if(dest->log_location) {
|
||||
len = snprintf(buf, sizeof buf, "%s:%d ", args->file, args->line);
|
||||
|
||||
}
|
||||
|
||||
len += snprintf(&buf[len], sizeof buf - (size_t)len, "[%s] ", args->module->short_name);
|
||||
|
||||
vsnprintf(&buf[len], sizeof buf - (size_t)len, fmt, ap);
|
||||
|
||||
syslog(pri, "%s", buf);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void uc_logf(int log_level, int module, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct uc_log_destination *dest;
|
||||
const struct uc_log_module *log_module;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
pthread_mutex_lock(&log_lock);
|
||||
|
||||
//Find the module doign the logging, or use the unknown module -
|
||||
//the latter would indicate a bug somewhere in the application as it should
|
||||
//always specify a known module
|
||||
if(module >= 0 && module < modules.cnt)
|
||||
log_module = &modules.mods[module];
|
||||
else
|
||||
log_module = &unknown_module;
|
||||
|
||||
//Check if the module should not be logged
|
||||
if(log_level < log_module->log_level)
|
||||
goto out;
|
||||
|
||||
//do logging for each destination
|
||||
SLIST_FOREACH(dest, &destinations, entry) {
|
||||
va_list apc;
|
||||
|
||||
if(log_level >= dest->log_level) {
|
||||
const struct uc_log_args args = {
|
||||
.dest = dest,
|
||||
.module = log_module,
|
||||
.log_level = log_level,
|
||||
.file = file,
|
||||
.line = line,
|
||||
};
|
||||
|
||||
//log func will mess with the va_list,
|
||||
//so make a copy
|
||||
va_copy(apc, ap);
|
||||
uc_vlog_internal(&args, fmt, apc);
|
||||
va_end(apc);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&log_lock);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user