From dd0f1ae393291c9b635b7bdb9d50dc3153affb0c Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Fri, 19 Nov 2010 11:12:22 +0100 Subject: [PATCH] log.c: Added basic logging support. --- src/log.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/log.h | 30 ++++++++++ test/Makefile | 5 +- test/t_log.c | 28 +++++++++ 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 test/t_log.c diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..bfc5262 --- /dev/null +++ b/src/log.c @@ -0,0 +1,160 @@ +/* log.c + * + * Copyright (C) 2010 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include "strbuf.h" +#include "log.h" + +static FILE *logfd = NULL; +static char *file = NULL; +static char *logname = NULL; + +static struct { + const char *name; + unsigned mask; +} levels[] = { + { "INFO", LOG_INFO }, + { "WARNING", LOG_WARN }, + { "CRITICAL", LOG_CRIT }, + { "DEBUG", LOG_DEBUG }, + { NULL, 0 } +}; + +static int mask = 0; + +/* check (and change) if we need to roll logging file */ +static void checklog() { + + if (file) { + time_t t; + char name[15]; + + time(&t); + strftime(name, sizeof(name), "%Y-%m-%d.log", localtime(&t)); + + /* update the name if we have changed date */ + if (strncmp(logname, name, sizeof(name))) { + memcpy(logname, name, sizeof(name)); + + if (logfd && logfd != stderr) + fclose(logfd); + logfd = fopen(file, "a"); + } + } + + if (!logfd) + logfd = stderr; +} + +/* writes the information about a logg message (timestamp and level) */ +static void writeinfo(unsigned level) { + + time_t tnow; + char buf[512], *ptr; + + time(&tnow); + ptr = buf + strftime(buf, sizeof(buf), "[%H:%M:%S] ", localtime(&tnow)); + ptr += sprintf(ptr, "%-10s ", loglvltostr(level)); + + fputs(buf, logfd); +} + +static int validmask(unsigned x, char unique) { + + /* check if only one bit is set */ + if (unique && (x & (x - 1)) != 0) + return 0; + + /* make sure we don't have any unused bits */ + return (x & ~LOG_ALL) == 0; +} + +void init_log(unsigned level, const char *path) { + + if (!validmask(level, 0)) + die("init_log: unknown level: %x\n", level); + + mask = level; + + if (path) { + strbuf_t sb = STRBUF_INIT; + size_t nameoffset; + + strbuf_append_str(&sb, path); + strbuf_term(&sb, '/'); + nameoffset = sb.len; + + strbuf_append_repeat(&sb, 0, 14); + + if (file) + xfree(file); + + file = strbuf_release(&sb); + logname = file + nameoffset; + } else { + logname = file = NULL; + } +} + +const char* loglvltostr(unsigned level) { + + int i; + + for(i=0; levels[i].name; i++) { + + if (level & levels[i].mask) + return levels[i].name; + } + return "UNKNOWN"; +} + +void logmsg(unsigned level, const char *fmt, ...) { + + va_list vl; + FILE *fd; + + if (!validmask(level, 1)) + die("log: invalid level: %x\n", level); + + if (level & ~mask) + return; + + checklog(); + writeinfo(level); + va_start(vl, fmt); + vfprintf(logfd, fmt, vl); + va_end(vl); + fputc('\n', logfd); + fflush(logfd); +} + +void logerrno(unsigned level, const char *prefix, int err) { + + char *str = strerror(err); + + if (str && level & mask) { + + if (!validmask(level, 1)) + die("logerrno: invalid level: %x\n", level); + + checklog(); + writeinfo(level); + if (prefix && *prefix) + fprintf(logfd, "%s: ", prefix); + fputs(str, logfd); + fputc('\n', logfd); + fflush(logfd); + } +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..d49b9c1 --- /dev/null +++ b/src/log.h @@ -0,0 +1,30 @@ +/* log.h + * + * Copyright (C) 2010 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#ifndef __LOG_H +#define __LOG_H + +#include +#include + +#define LOG_INFO (1<<0) +#define LOG_WARN (1<<1) +#define LOG_CRIT (1<<2) +#define LOG_DEBUG (1<<3) +#define LOG_ALL (LOG_INFO | LOG_WARN | LOG_CRIT | LOG_DEBUG) + +void init_log(unsigned level, const char *path); + +const char* loglvltostr(unsigned level); + +void logmsg(unsigned level, const char *fmt, ...); + +void logerrno(unsigned level, const char *prefix, int err); + +#endif /* __LOG_H */ diff --git a/test/Makefile b/test/Makefile index 9655d4c..34e7a67 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,6 @@ # Test makefile CC=gcc -CFLAGS=-g -D__DEBUG__ +CFLAGS=-g -D__DEBUG__ LDFLAGS=-L/usr/lib64/mysql -lmysqlclient all : raw_inotify strbuf path rbtree inotify fscrawl queue @@ -57,5 +57,8 @@ fscrawl : queue : $(CC) $(CFLAGS) ../src/queue.c t_queue.c -o test_queue +log : + $(CC) $(CFLAGS) ../src/die.c ../src/strbuf.c ../src/xalloc.c ../src/log.c t_log.c -o test_log + clean : rm -f test_* diff --git a/test/t_log.c b/test/t_log.c new file mode 100644 index 0000000..caa77ed --- /dev/null +++ b/test/t_log.c @@ -0,0 +1,28 @@ + +#include +#include +#include "../src/log.h" + +int main() { + + init_log(LOG_INFO | LOG_WARN, NULL); + + logmsg(LOG_INFO, "this is stderr"); + + logmsg(LOG_CRIT, "Should not show"); + logerrno(LOG_CRIT, NULL, ENOENT); + + init_log(LOG_INFO | LOG_WARN | LOG_CRIT, "./logs/"); + + logmsg(LOG_INFO, "some info"); + logmsg(LOG_WARN, "invalid type '%i'", 3); + + logerrno(LOG_CRIT, "malloc", ENOMEM); + logerrno(LOG_CRIT, NULL, ENOENT); + + logmsg(LOG_DEBUG, "Should not show"); + + logmsg(LOG_INFO | LOG_CRIT, "Should not work, can only log to one priority"); + + return 0; +}