src/log.c
author Tero Marttila <terom@fixme.fi>
Mon, 04 May 2009 20:55:04 +0300
branchnew-transport
changeset 168 a58ad50911fc
parent 156 6534a4ac957b
child 172 ea4972e51fa3
permissions -rw-r--r--
refactor test.c into tests/*

#include "log.h"
#include "str.h"

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

/**
 * The default log output func
 */
void log_default_func (const char *line, void *arg)
{
    (void) arg;

    // display
    printf("%s\n", line);
}

/**
 * The global log level
 */
static enum log_level _log_level = LOG_LEVEL_DEFAULT;

/**
 * The global log output func
 */
static struct log_output_ctx {
    /** The function itself */
    log_output_func func;

    /** The arg */
    void *arg;
} _log_output_ctx = { log_default_func, NULL };

/**
 * List of log level names
 */
const char *log_level_names[] = {
    "DEBUG",
    "INFO",
    "WARN",
    "ERROR",
    "FATAL",
    NULL
};

#define _LOG_LEVEL_NAME(ll) case LOG_ ## ll: return #ll;
const char *log_level_name (enum log_level level)
{
    switch (level) {
        _LOG_LEVEL_NAME(DEBUG)
        _LOG_LEVEL_NAME(INFO)
        _LOG_LEVEL_NAME(WARN)
        _LOG_LEVEL_NAME(ERROR)
        _LOG_LEVEL_NAME(FATAL)
        default: return "???";
    }
}
#undef _LOG_LEVEL_NAME

void set_log_level (enum log_level level)
{
    // meep meep
    _log_level = level;
}

void log_set_func (log_output_func func, void *arg)
{
    // replace the current one
    _log_output_ctx.func = func ? func : log_default_func;
    _log_output_ctx.arg = arg;
}

/**
 * Format the full output line, and pass it to the log_output_func. The line will be of the form:
 *  "[<level>] <func> : <log_fmt> [ <user_fmt> ]"
 */
static void log_output (enum log_level level, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
{
    char buf[LOG_MSG_MAX], *buf_ptr = buf;
    size_t buf_size = sizeof(buf);

    // filter out?
    if (level < _log_level)
        return;

    // output the header
    buf_ptr += str_advance(NULL, &buf_size, str_append_fmt(buf_ptr, buf_size, "[%5s] %20s : ", log_level_name(level), func));
    
    // output the user data
    buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, user_fmt, user_fmtargs));
    
    // output the suffix
    if (log_fmt) {
        va_list vargs;

        va_start(vargs, log_fmt);
        buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, vargs));
        va_end(vargs);
    }

    // send it to the output func
    _log_output_ctx.func(buf, _log_output_ctx.arg);
}

void log_msg (enum log_level level, const char *func, const char *format, ...)
{
    va_list vargs;
    
    // formatted output: no suffix
    va_start(vargs, format);
    log_output(level, func, format, vargs, NULL);
    va_end(vargs);
}

void _log_err_code (enum log_level level, err_t err, const char *func, const char *format, ...)
{
    va_list vargs;

    // formatted output: suffix error_name()
    va_start(vargs, format);
    log_output(level, func, format, vargs, ": %s", error_name(err));
    va_end(vargs);
}

void _log_err (enum log_level level, const error_t *err, const char *func, const char *format, ...)
{
    va_list vargs;

    // formatted output: suffix error_msg()
    va_start(vargs, format);
    log_output(level, func, format, vargs, ": %s", error_msg(err));
    va_end(vargs);
}

void _log_perr (enum log_level level, const char *func, const char *format, ...)
{
    va_list vargs;

    // formatted output: suffix strerror()
    va_start(vargs, format);
    log_output(level, func, format, vargs, ": %s", strerror(errno));
    va_end(vargs);
}