Mercurial > irccd
changeset 936:6866d0d0e360
irccd: add log.h
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 10 Jan 2021 18:10:52 +0100 |
parents | b0451fc0a17d |
children | ffe985308567 |
files | .hgignore Makefile irccd/irccd irccd/log.c irccd/log.h tests/test-log.c |
diffstat | 6 files changed, 416 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Sun Jan 10 17:29:30 2021 +0100 +++ b/.hgignore Sun Jan 10 18:10:52 2021 +0100 @@ -17,12 +17,16 @@ ^extern/libcompat/tryinclude$ ^extern/libcompat/trylib$ +# executables. +irccd/irccd + # temporary files. \.a$ \.o$ \.d$ # tests. +^tests/test-log$ ^tests/test-subst$ ^tests/test-util$
--- a/Makefile Sun Jan 10 17:29:30 2021 +0100 +++ b/Makefile Sun Jan 10 18:10:52 2021 +0100 @@ -25,12 +25,14 @@ IRCCD= irccd/irccd IRCCD_SRCS= extern/libduktape/duktape.c \ + irccd/log.c \ irccd/subst.c \ irccd/util.c IRCCD_OBJS= ${IRCCD_SRCS:.c=.o} IRCCD_DEPS= ${IRCCD_SRCS:.c=.d} -TESTS= tests/test-util.c \ +TESTS= tests/test-log.c \ + tests/test-util.c \ tests/test-subst.c TESTS_OBJS= ${TESTS:.c=}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/irccd/log.c Sun Jan 10 18:10:52 2021 +0100 @@ -0,0 +1,194 @@ +/* + * log.c -- loggers + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include "log.h" + +enum level { + LEVEL_INFO, + LEVEL_WARN, + LEVEL_DEBUG +}; + +static FILE *out, *err; +static bool verbosity; + +static void +handler_files(enum level level, const char *fmt, va_list ap) +{ + switch (level) { + case LEVEL_WARN: + vfprintf(err, fmt, ap); + putc('\n', err); + fflush(err); + break; + default: + vfprintf(out, fmt, ap); + putc('\n', out); + fflush(out); + break; + } +} + +static void +finalizer_files(void) +{ + if (out) + fclose(out); + if (err) + fclose(err); +} + +static void +handler_syslog(enum level level, const char *fmt, va_list ap) +{ + static const int table[] = { + [LEVEL_INFO] = LOG_INFO, + [LEVEL_WARN] = LOG_WARNING, + [LEVEL_DEBUG] = LOG_DEBUG + }; + + /* TODO: add compatibility shim for vsyslog. */ + vsyslog(table[level], fmt, ap); +} + +static void +finalizer_syslog(void) +{ + closelog(); +} + +static void (*handler)(enum level, const char *, va_list); +static void (*finalizer)(void); + +void +irc_log_to_syslog(void) +{ + irc_log_finish(); + + openlog("irccd", 0, LOG_DAEMON); + + handler = handler_syslog; + finalizer = finalizer_syslog; +} + +void +irc_log_to_console(void) +{ + irc_log_finish(); + + out = stdout; + err = stderr; + + handler = handler_files; + finalizer = NULL; +} + +void +irc_log_to_files(const char *stdout, const char *stderr) +{ + irc_log_finish(); + + if (!(out = fopen(stdout, "a"))) + irc_log_warn("%s: %s", stdout, strerror(errno)); + if (!(err = fopen(stderr, "a"))) + irc_log_warn("%s: %s", stdout, strerror(errno)); + + handler = handler_files; + finalizer = finalizer_files; +} + +void +irc_log_to_null(void) +{ + irc_log_finish(); + + handler = NULL; + finalizer = NULL; +} + +void +irc_log_set_verbose(bool mode) +{ + verbosity = mode; +} + +void +irc_log_info(const char *fmt, ...) +{ + assert(fmt); + + va_list ap; + + va_start(ap, fmt); + + if (verbosity && handler) + handler(LEVEL_INFO, fmt, ap); + + va_end(ap); +} + +void +irc_log_warn(const char *fmt, ...) +{ + assert(fmt); + + va_list ap; + + va_start(ap, fmt); + + if (handler) + handler(LEVEL_WARN, fmt, ap); + + va_end(ap); +} + +void +irc_log_debug(const char *fmt, ...) +{ +#if !defined(NDBEUG) + assert(fmt); + + va_list ap; + + va_start(ap, fmt); + + if (handler) + handler(LEVEL_DEBUG, fmt, ap); + + va_end(ap); +#else + (void)fmt; +#endif +} + +void +irc_log_finish(void) +{ + if (finalizer) + finalizer(); + + handler = NULL; + finalizer = NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/irccd/log.h Sun Jan 10 18:10:52 2021 +0100 @@ -0,0 +1,51 @@ +/* + * log.h -- loggers + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_LOG_H +#define IRCCD_LOG_H + +#include <stdbool.h> + +void +irc_log_to_syslog(void); + +void +irc_log_to_console(void); + +void +irc_log_to_files(const char *, const char *); + +void +irc_log_to_null(void); + +void +irc_log_set_verbose(bool); + +void +irc_log_info(const char *, ...); + +void +irc_log_warn(const char *, ...); + +void +irc_log_debug(const char *, ...); + +void +irc_log_finish(void); + +#endif /* !IRCCD_LOG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-log.c Sun Jan 10 18:10:52 2021 +0100 @@ -0,0 +1,164 @@ +/* + * test-log.c -- test log.h functions + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define GREATEST_USE_ABBREVS 0 +#include <greatest.h> + +#include <irccd/log.h> + +/* + * Note: we can't really test logs to console and syslog, so use files as best effort. Write some + * data and check that it is correctly written. + */ + +static void +clean(void *udata) +{ + (void)udata; + + remove("stdout.txt"); + remove("stderr.txt"); +} + +GREATEST_TEST +basics_info_verbose_off(void) +{ + FILE *fpout, *fperr; + char out[128] = {0}, err[128] = {0}; + + /* Default is quiet, should not log. */ + irc_log_to_files("stdout.txt", "stderr.txt"); + irc_log_info("hello world!"); + + if (!(fpout = fopen("stdout.txt", "r")) || !(fperr = fopen("stderr.txt", "r"))) + GREATEST_FAIL(); + + fgets(out, sizeof (out), fpout); + fgets(err, sizeof (err), fperr); + + GREATEST_ASSERT_STR_EQ("", out); + GREATEST_ASSERT_STR_EQ("", err); + + GREATEST_PASS(); +} + +GREATEST_TEST +basics_info_verbose_on(void) +{ + FILE *fpout, *fperr; + char out[128] = {0}, err[128] = {0}; + + irc_log_set_verbose(true); + irc_log_to_files("stdout.txt", "stderr.txt"); + irc_log_info("hello world!"); + irc_log_info("what's up?"); + + if (!(fpout = fopen("stdout.txt", "r")) || !(fperr = fopen("stderr.txt", "r"))) + GREATEST_FAIL(); + + GREATEST_ASSERT(fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("hello world!\n", out); + GREATEST_ASSERT(fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("what's up?\n", out); + + GREATEST_ASSERT(!fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("", err); + GREATEST_ASSERT(!fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("", err); + + GREATEST_PASS(); +} + +GREATEST_TEST +basics_warn(void) +{ + FILE *fpout, *fperr; + char out[128] = {0}, err[128] = {0}; + + /* Warning messages are printed even without verbosity. */ + irc_log_set_verbose(false); + irc_log_to_files("stdout.txt", "stderr.txt"); + irc_log_warn("error line 1"); + irc_log_warn("error line 2"); + + if (!(fpout = fopen("stdout.txt", "r")) || !(fperr = fopen("stderr.txt", "r"))) + GREATEST_FAIL(); + + GREATEST_ASSERT(!fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("", out); + GREATEST_ASSERT(!fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("", out); + + GREATEST_ASSERT(fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("error line 1\n", err); + GREATEST_ASSERT(fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("error line 2\n", err); + + GREATEST_PASS(); +} + +GREATEST_TEST +basics_debug(void) +{ +#if !defined(NDEBUG) + FILE *fpout, *fperr; + char out[128] = {0}, err[128] = {0}; + + /* Debug messages are printed even without verbosity but requires to be built in debug. */ + irc_log_set_verbose(false); + irc_log_to_files("stdout.txt", "stderr.txt"); + irc_log_debug("startup!"); + irc_log_debug("shutdown!"); + + if (!(fpout = fopen("stdout.txt", "r")) || !(fperr = fopen("stderr.txt", "r"))) + GREATEST_FAIL(); + + GREATEST_ASSERT(fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("startup!\n", out); + GREATEST_ASSERT(fgets(out, sizeof (out), fpout)); + GREATEST_ASSERT_STR_EQ("shutdown!\n", out); + + GREATEST_ASSERT(!fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("", err); + GREATEST_ASSERT(!fgets(err, sizeof (err), fperr)); + GREATEST_ASSERT_STR_EQ("", err); +#endif + GREATEST_PASS(); +} + +GREATEST_SUITE(suite_basics) +{ + GREATEST_SET_SETUP_CB(clean, NULL); + GREATEST_SET_TEARDOWN_CB(clean, NULL); + GREATEST_RUN_TEST(basics_info_verbose_off); + GREATEST_RUN_TEST(basics_info_verbose_on); + GREATEST_RUN_TEST(basics_warn); + GREATEST_RUN_TEST(basics_debug); +} + +GREATEST_MAIN_DEFS(); + +int +main(int argc, char **argv) +{ + GREATEST_MAIN_BEGIN(); + GREATEST_RUN_SUITE(suite_basics); + GREATEST_MAIN_END(); + + return 0; +}