src/server/daemon/log.c

Wed, 02 Jan 2013 16:03:50 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 02 Jan 2013 16:03:50 +0100
changeset 45
a24aa388f02f
parent 44
3da1f7b6847f
child 60
feb2f1e115c6
permissions
-rw-r--r--

added access log

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <aio.h>
#include <time.h>

#include "log.h"
#include "../util/strbuf.h"
#include "../util/io.h"
#include "../ucx/map.h"

static int is_initialized = 0;

static int log_file_fd;
static int log_level = 0;

/*
 * if the log file is uninitialized, output is written to the ui_buffer
 */
static sbuf_t *ui_buffer = NULL;

/*
 * access logfile map
 */
static UcxMap    *access_log_files; // map of AccessLog*
static AccessLog *default_access_log;


char *log_date_month[] = {
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
};

char *log_levels[] = {
    "error",
    "warning",
    "info"
};

int init_log_file(LogConfig *cfg) {
    if(is_initialized) {
        return 0;
    }
    
    /* open the log file */
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    log_file_fd = open(cfg->file, O_WRONLY | O_CREAT | O_APPEND, mode);
    if(log_file_fd == -1) {
        return -1;
    }
    
    if(!strcmp(cfg->level, "ERROR")) {
        log_level = LOG_LEVEL_ERROR;
    } else if(!strcmp(cfg->level, "WARNING")) {
        log_level = LOG_LEVEL_WARNING;
    } else if(!strcmp(cfg->level, "INFO")) {
        log_level = LOG_LEVEL_INFO;
    }
    
    if(cfg->log_stdout) {
        // TODO
    }
    if(cfg->log_stderr) {
        // TODO
    }
    
    
    is_initialized = 1;
    
    /* if ui_buffer is not NULL, write it to the log file */
    if(ui_buffer) {
        size_t len = ui_buffer->length;
        size_t r;
        while(len > 0) {
            r = write(log_file_fd, ui_buffer->ptr, ui_buffer->length);
            len -= r;
        }
        
        sbuf_free(ui_buffer);
    }
    
    return 0;
}

void log_uninitialized_writeln(char *str, size_t len) {
    if(ui_buffer == NULL) {
        ui_buffer = sbuf_new(1024);
        if(ui_buffer == NULL) {
            return; /* TODO: critical error, exit */
        }
    }
    
    sstr_t s;
    s.ptr = str;
    s.length = len;
    
    sbuf_append(ui_buffer, s);
    sbuf_put(ui_buffer, '\n');
}

void log_file_writeln(char *str, size_t len) {
    if(!is_initialized) {
        log_uninitialized_writeln(str, len);
    }
    
    struct iovec io[] = {
        { str, len },
        { "\n", 1}
    };
    
    writev(log_file_fd, io, 2); /* TODO: aio */
}

sstr_t log_get_prefix(int level) {
    time_t t = time(NULL);
    
    sstr_t d;
    d.ptr = NULL;
    d.length = 0;
    
    struct tm date;
    localtime_r(&t, &date);
    
    char *buf = malloc(64);
    int len = snprintf(
            buf,
            64,
            "[%02d/%s/%d:%02d:%02d:%02d] %s : ",
            date.tm_mday,
            log_date_month[date.tm_mon],
            1900 + date.tm_year,
            date.tm_hour,
            date.tm_min,
            date.tm_sec,
            log_levels[level]);
    
    if(len > 0) {
        d.ptr = buf;
        d.length = len;
    }
    
    return d;
}


/*
 * log api functions
 */

int log_ereport(int degree, const char *format, ...) { 
    if(degree > log_level) {
        return 0;
    }
    
    sstr_t lmsg;
    lmsg.ptr = NULL;
    va_list ap;
    va_start(ap, format);
    
    /* create log message prefix */
    sstr_t lpre = log_get_prefix(degree); 
    
    /* format message */
    int len = vasprintf(&lmsg.ptr, format, ap);
    lmsg.length = len;
    
    /* create message string */
    sstr_t message;
    message.length = lpre.length + len;
    message.ptr = malloc(message.length + 1);
    
    message = sstrncat(2, message, lpre, lmsg);
    
    /* write message to the log file */ 
    log_file_writeln(message.ptr, message.length);
    
    /* cleanup */
    free(lmsg.ptr);
    free(message.ptr);
    
    return 0;
}


/*
 * access log
 * This source file only manages access log files. IO is performed directly
 * by AddLog safs. 
 */
AccessLog* get_access_log(sstr_t file, sstr_t format) {
    if(!access_log_files) {
        access_log_files = ucx_map_new(4);
    }
    
    if(file.ptr == NULL || file.length == 0) {
        return NULL;
    }
    
    AccessLog *log = ucx_map_sstr_get(access_log_files, file);
    if(log != NULL) {
        // TODO: ref
        return log;
    }
    
    // the log file is not opened
    // check first if we can open it
    sstr_t path = sstrdup(file);
    FILE *out = fopen(path.ptr, "a");
    if(out == NULL) {
        free(path.ptr);
        return NULL;
    }
    
    // create access log object
    log = calloc(1, sizeof(AccessLog));
    log->file = path;
    if(format.ptr != NULL) {
        log->format = sstrdup(format);
    }
    log->log = out;
    // TODO: log->ref = 1;
    
    // add access log to the map
    ucx_map_sstr_put(access_log_files, file, log);
    
    if(!default_access_log) {
        default_access_log = log;
    }
    
    return log;
}

AccessLog* get_default_access_log() {
    // TODO: ref
    return default_access_log;
}

mercurial