log.c

来自「mms client」· C语言 代码 · 共 370 行

C
370
字号
/* * log.c - implement logging functions */#include <limits.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <time.h>#include <stdarg.h>#include <string.h>#if HAVE_SYSLOG_H    #include <syslog.h>#else/* * If we don't have syslog.h, then we'll use the following dummy definitions * to avoid writing #if HAVE_SYSLOG_H everywhere. */enum {    LOG_PID, LOG_DAEMON, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_ALERT};static void openlog(const char *ident, int option, int facility) {}static void syslog(int translog, const char *buf) {}#endif#include "gwlib.h"/* * List of currently open log files. */#define MAX_LOGFILES 8static struct {    FILE *file;    int minimum_output_level;    char filename[FILENAME_MAX + 1]; /* to allow re-open */} logfiles[MAX_LOGFILES];static int num_logfiles = 0;/* * List of places that should be logged at debug-level. */#define MAX_LOGGABLE_PLACES (10*1000)static char *loggable_places[MAX_LOGGABLE_PLACES];static int num_places = 0;/* * Syslog support. */static int sysloglevel;static int dosyslog = 0;/* * Make sure stderr is included in the list. */static void add_stderr(void) {    int i;    for (i = 0; i < num_logfiles; ++i)        if (logfiles[i].file == stderr)            return;    logfiles[num_logfiles].file = stderr;    logfiles[num_logfiles].minimum_output_level = GW_DEBUG;    ++num_logfiles;}void log_set_output_level(enum output_level level) {    int i;    add_stderr();    for (i = 0; i < num_logfiles; ++i) {        if (logfiles[i].file == stderr) {            logfiles[i].minimum_output_level = level;            break;        }    }}void log_set_syslog(const char *ident, int syslog_level) {    if (ident == NULL)        dosyslog = 0;    else {        dosyslog = 1;        sysloglevel = syslog_level;        openlog(ident, LOG_PID, LOG_DAEMON);        debug("gwlib.log", 0, "Syslog logging enabled.");    }}void log_reopen(void) {    int i;    for (i = 0; i < num_logfiles; ++i) {        if (logfiles[i].file != stderr) {            fclose(logfiles[i].file);            logfiles[i].file = fopen(logfiles[i].filename, "a");            if (logfiles[i].file == NULL) {                error(errno, "Couldn't re-open logfile `%s'.",                      logfiles[i].filename);            }        }    }       }void log_close_all(void) {    while (num_logfiles > 0) {        --num_logfiles;        if (logfiles[num_logfiles].file != stderr)            fclose(logfiles[num_logfiles].file);        logfiles[num_logfiles].file = NULL;    }}void log_open(char *filename, int level) {    FILE *f;    add_stderr();    if (num_logfiles == MAX_LOGFILES) {        error(0, "Too many log files already open, not adding `%s'",               filename);        return;    }    if (strlen(filename) > FILENAME_MAX) {        error(0, "Log filename too long: `%s'.", filename);        return;    }    f = fopen(filename, "a");    if (f == NULL) {        error(errno, "Couldn't open logfile `%s'.", filename);        return;    }    logfiles[num_logfiles].file = f;    logfiles[num_logfiles].minimum_output_level = level;    strcpy(logfiles[num_logfiles].filename, filename);    ++num_logfiles;    info(0, "Added logfile `%s' with level `%d'.", filename, level);}#define FORMAT_SIZE (1024)static void format(char *buf, int level, const char *place, int e,                    const char *fmt) {    static char *tab[] = {        "DEBUG: ",        "INFO: ",        "WARNING: ",        "ERROR: ",        "PANIC: ",        "LOG: "    };    static int tab_size = sizeof(tab) / sizeof(tab[0]);    time_t t;    struct tm tm;    char *p, prefix[1024];    p = prefix;    time(&t);#if LOG_TIMESTAMP_LOCALTIME    tm = gw_localtime(t);#else    tm = gw_gmtime(t);#endif    sprintf(p, "%04d-%02d-%02d %02d:%02d:%02d ",            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,            tm.tm_hour, tm.tm_min, tm.tm_sec);    p = strchr(p, '\0');    sprintf(p, "[%ld] ", gwthread_self());    p = strchr(p, '\0');    if (level < 0 || level >= tab_size)        sprintf(p, "UNKNOWN: ");    else        sprintf(p, "%s", tab[level]);    p = strchr(p, '\0');    if (place != NULL && *place != '\0')        sprintf(p, "%s: ", place);    if (strlen(prefix) + strlen(fmt) > FORMAT_SIZE / 2) {        sprintf(buf, "%s <OUTPUT message too long>\n", prefix);        return;    }    if (e == 0)        sprintf(buf, "%s%s\n", prefix, fmt);    else        sprintf(buf, "%s%s\n%sSystem error %d: %s\n",                prefix, fmt, prefix, e, strerror(e));}static void output(FILE *f, char *buf, va_list args) {    vfprintf(f, buf, args);    fflush(f);}static void kannel_syslog(char *format, va_list args, int level) {    char buf[4096]; /* Trying to syslog more than 4K could be bad */    int translog;    if (level >= sysloglevel && dosyslog) {        if (args == NULL) {            strncpy(buf, format, sizeof(buf));            buf[sizeof(buf) - 1] = '\0';        } else {            vsnprintf(buf, sizeof(buf), format, args);            /* XXX vsnprint not 100% portable */        }        switch (level) {            case GW_DEBUG:                translog = LOG_DEBUG;                break;            case GW_INFO:                translog = LOG_INFO;                break;            case GW_WARNING:                translog = LOG_WARNING;                break;            case GW_ERROR:                translog = LOG_ERR;                break;            case GW_PANIC:                translog = LOG_ALERT;                break;            default:                translog = LOG_INFO;                break;        }        syslog(translog, buf);    }}/* * Almost all of the message printing functions are identical, except for * the output level they use. This macro contains the identical parts of * the functions so that the code needs to exist only once. It's a bit * more awkward to edit, but that can't be helped. The "do {} while (0)" * construct is a gimmick to be more like a function call in all syntactic * situation. */#define FUNCTION_GUTS(level, place) \	do { \	    int i; \	    char buf[FORMAT_SIZE]; \	    va_list args; \	    \	    add_stderr(); \	    format(buf, level, place, e, fmt); \	    for (i = 0; i < num_logfiles; ++i) { \		if (level >= logfiles[i].minimum_output_level) { \		    va_start(args, fmt); \		    output(logfiles[i].file, buf, args); \		    va_end(args); \		} \	    } \	    if (dosyslog) { \		va_start(args, fmt); \		kannel_syslog(buf,args,level); \		va_end(args); \	    } \	} while (0)void gw_panic(int e, const char *fmt, ...) {    FUNCTION_GUTS(GW_PANIC, "");    exit(EXIT_FAILURE);}void error(int e, const char *fmt, ...) {    FUNCTION_GUTS(GW_ERROR, "");}void warning(int e, const char *fmt, ...) {    FUNCTION_GUTS(GW_WARNING, "");}void info(int e, const char *fmt, ...) {    FUNCTION_GUTS(GW_INFO, "");}static int place_matches(const char *place, const char *pat) {    size_t len;    len = strlen(pat);    if (pat[len-1] == '*')        return(strncasecmp(place, pat, len - 1) == 0);    return(strcasecmp(place, pat) == 0);}static int place_should_be_logged(const char *place) {    int i;    if (num_places == 0)        return 1;    for (i = 0; i < num_places; ++i) {        if (*loggable_places[i] != '-' &&             place_matches(place, loggable_places[i]))            return 1;    }    return 0;}static int place_is_not_logged(const char *place) {    int i;    if (num_places == 0)        return 0;    for (i = 0; i < num_places; ++i) {        if (*loggable_places[i] == '-' &&            place_matches(place, loggable_places[i]+1))            return 1;    }    return 0;}void debug(const char *place, int e, const char *fmt, ...) {    if (place_should_be_logged(place) && place_is_not_logged(place) == 0) {        FUNCTION_GUTS(GW_DEBUG, place);        /*         * Note: giving `place' to FUNCTION_GUTS makes log lines             * too long and hard to follow. We'll rely on an external             * list of what places are used instead of reading them             * from the log file.         */    }}void log_set_debug_places(const char *places) {    char *p;    p = strtok(gw_strdup(places), " ,");    num_places = 0;    while (p != NULL && num_places < MAX_LOGGABLE_PLACES) {        loggable_places[num_places++] = p;        p = strtok(NULL, " ,");    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?