1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

log: add option to add a callback to C logging subsystem

This commit is contained in:
Steffen Vogel 2018-08-23 13:14:11 +02:00
parent f4d28262af
commit 0c7d7e1003
6 changed files with 55 additions and 417 deletions

View file

@ -36,11 +36,15 @@ extern "C" {
#include <villas/log_config.h>
/* The log level which is passed as first argument to print() */
#define LOG_LVL_DEBUG CLR_GRY("Debug")
#define LOG_LVL_INFO CLR_WHT("Info ")
#define LOG_LVL_WARN CLR_YEL("Warn ")
#define LOG_LVL_ERROR CLR_RED("Error")
#define LOG_LVL_STATS CLR_MAG("Stats")
enum log_level {
LOG_LVL_DEBUG,
LOG_LVL_INFO,
LOG_LVL_WARN,
LOG_LVL_ERROR,
LOG_LVL_STATS,
};
typedef void (*log_cb_t)(struct log *l, enum log_level lvl, const char *fmt, va_list va);
/** Debug facilities.
*
@ -84,6 +88,8 @@ enum log_facilities {
struct log {
enum state state;
const char *name;
struct timespec epoch; /**< A global clock used to prefix the log messages. */
struct winsize window; /**< Size of the terminal window. */
@ -99,15 +105,18 @@ struct log {
int syslog; /**< Whether or not to log to syslogd. */
bool tty; /**< Is the log file a tty? */
log_cb_t callback;
FILE *file; /**< Send all log output to this file / stdout / stderr. */
};
/** The global log instance. */
struct log *global_log;
struct log default_log;
extern struct log *global_log;
/** Initialize log object */
int log_init(struct log *l, int level, long faciltities);
int log_init(struct log *l, const char *name, int level, long faciltities);
void log_set_callback(struct log *l, log_cb_t cb);
int log_open(struct log *l);
@ -135,7 +144,7 @@ int log_set_facility_expression(struct log *l, const char *expression);
* @param lvl The log level
* @param fmt The format string (printf alike)
*/
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
void log_print(struct log *l, enum log_level lvl, const char *fmt, ...)
__attribute__ ((format(printf, 3, 4)));
/** Logs variadic messages to stdout.
@ -144,7 +153,7 @@ void log_print(struct log *l, const char *lvl, const char *fmt, ...)
* @param fmt The format string (printf alike)
* @param va The variadic argument list (see stdarg.h)
*/
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list va);
void log_vprint(struct log *l, enum log_level lvl, const char *fmt, va_list va);
/** Printf alike debug message with level. */
void debug(long lvl, const char *fmt, ...)

View file

@ -43,23 +43,33 @@
#endif
struct log *global_log;
struct log default_log;
/* We register a default log instance */
__attribute__((constructor))
void register_default_log()
{
int ret;
static struct log default_log;
ret = log_init(&default_log, V, LOG_ALL);
ret = log_init(&default_log, "default", V, LOG_ALL);
if (ret)
error("Failed to initalize log");
ret = log_open(&default_log);
if (ret)
error("Failed to start log");
global_log = &default_log;
}
static const char *level_strs[] = {
CLR_GRY("Debug"), /* LOG_LVL_DEBUG */
CLR_WHT("Info "), /* LOG_LVL_INFO */
CLR_YEL("Warn "), /* LOG_LVL_WARN */
CLR_RED("Error"), /* LOG_LVL_ERROR */
CLR_MAG("Stats") /* LOG_LVL_STATS */
};
/** List of debug facilities as strings */
static const char *facilities_strs[] = {
"pool", /* LOG_POOL */
@ -106,18 +116,20 @@ static void log_resize(int signal, siginfo_t *sinfo, void *ctx)
debug(LOG_LOG | 15, "New terminal size: %dx%x", global_log->window.ws_row, global_log->window.ws_col);
}
int log_init(struct log *l, int level, long facilitites)
int log_init(struct log *l, const char *name, int level, long facilitites)
{
int ret;
/* Register this log instance globally */
global_log = l;
l->name = name;
l->level = level;
l->syslog = 0;
l->facilities = facilitites;
l->file = stderr;
l->path = NULL;
l->callback = NULL;
l->epoch = time_now();
l->prefix = getenv("VILLAS_LOG_PREFIX");
@ -159,6 +171,11 @@ int log_init(struct log *l, int level, long facilitites)
return 0;
}
void log_set_callback(struct log *l, log_cb_t cb)
{
l->callback = cb;
}
int log_open(struct log *l)
{
if (l->path) {
@ -263,7 +280,7 @@ found: if (negate)
return l->facilities;
}
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
void log_print(struct log *l, enum log_level lvl, const char *fmt, ...)
{
va_list ap;
@ -272,11 +289,16 @@ void log_print(struct log *l, const char *lvl, const char *fmt, ...)
va_end(ap);
}
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap)
void log_vprint(struct log *l, enum log_level lvl, const char *fmt, va_list ap)
{
struct timespec ts = time_now();
static __thread char buf[1024];
if (l->callback) {
l->callback(l, lvl, fmt, ap);
return;
}
int off = 0;
int len = sizeof(buf);
@ -285,7 +307,7 @@ void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap)
off += snprintf(buf + off, len - off, "%s", l->prefix);
/* Timestamp & Severity */
off += snprintf(buf + off, len - off, "%10.3f %-5s ", time_delta(&l->epoch, &ts), lvl);
off += snprintf(buf + off, len - off, "%10.3f %-5s ", time_delta(&l->epoch, &ts), level_strs[lvl]);
/* Format String */
off += vsnprintf(buf + off, len - off, fmt, ap);

View file

@ -84,14 +84,12 @@ void jerror(json_error_t *err, const char *fmt, ...)
va_list ap;
char *buf = NULL;
struct log *l = global_log ? global_log : &default_log;
va_start(ap, fmt);
vstrcatf(&buf, fmt, ap);
va_end(ap);
log_print(l, LOG_LVL_ERROR, "%s:", buf);
log_print(l, LOG_LVL_ERROR, " %s in %s:%d:%d", err->text, err->source, err->line, err->column);
log_print(global_log, LOG_LVL_ERROR, "%s:", buf);
log_print(global_log, LOG_LVL_ERROR, " %s in %s:%d:%d", err->text, err->source, err->line, err->column);
free(buf);

View file

@ -62,10 +62,8 @@ static int table_resize(struct table *t, int width)
void table_header(struct table *t)
{
struct log *l = global_log ? global_log : &default_log;
if (t->width != l->width)
table_resize(t, l->width);
if (t->width != global_log->width)
table_resize(t, global_log->width);
char *line0 = strf("\b");
char *line1 = strf("\b\b" BOX_UD);
@ -121,10 +119,8 @@ void table_header(struct table *t)
void table_row(struct table *t, ...)
{
struct log *l = global_log ? global_log : &default_log;
if (t->width != l->width) {
table_resize(t, l->width);
if (t->width != global_log->width) {
table_resize(t, global_log->width);
table_header(t);
}
@ -156,10 +152,8 @@ void table_row(struct table *t, ...)
void table_footer(struct table *t)
{
struct log *l = global_log ? global_log : &default_log;
if (t->width != l->width)
table_resize(t, l->width);
if (t->width != global_log->width)
table_resize(t, global_log->width);
char *line = strf("\b");

View file

@ -1,255 +0,0 @@
/** Unit tests for advio
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* 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
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <unistd.h>
#include <criterion/criterion.h>
#include <criterion/logging.h>
#include <villas/utils.h>
#include <villas/advio.h>
/** This URI points to a Sciebo share which contains some test files.
* The Sciebo share is read/write accessible via WebDAV. */
#define BASE_URI "https://1Nrd46fZX8HbggT:badpass@rwth-aachen.sciebo.de/public.php/webdav/node/tests"
Test(advio, islocal)
{
int ret;
ret = aislocal("/var/log/villas/dta.dat");
cr_assert_eq(ret, 1);
ret = aislocal("http://www.google.de");
cr_assert_eq(ret, 0);
ret = aislocal("torrent://www.google.de");
cr_assert_eq(ret, -1);
}
Test(advio, local)
{
AFILE *af;
int ret;
char *buf = NULL;
size_t buflen = 0;
/* We open this file and check the first line */
af = afopen(__FILE__, "r");
cr_assert(af, "Failed to open local file");
ret = getline(&buf, &buflen, af->file);
cr_assert_gt(ret, 1);
cr_assert_str_eq(buf, "/** Unit tests for advio\n");
}
Test(advio, download)
{
AFILE *af;
int ret;
size_t len;
char buffer[64];
char expect[64] = "ook4iekohC2Teegoghu6ayoo1OThooregheebaet8Zod1angah0che7quai4ID7A";
af = afopen(BASE_URI "/download" , "r");
cr_assert(af, "Failed to download file");
len = afread(buffer, 1, sizeof(buffer), af);
cr_assert_gt(len, 0, "len=%zu, feof=%u", len, afeof(af));
cr_assert_arr_eq(buffer, expect, sizeof(expect));
ret = afclose(af);
cr_assert_eq(ret, 0, "Failed to close file");
}
Test(advio, download_large)
{
AFILE *af;
int ret;
af = afopen(BASE_URI "/download-large" , "r");
cr_assert(af, "Failed to download file");
char line[4096];
char *f;
f = afgets(line, 4096, af);
cr_assert_not_null(f);
/* Check first line */
cr_assert_str_eq(line, "# VILLASnode signal params: type=mixed, values=4, rate=1000.000000, limit=100000, amplitude=1.000000, freq=1.000000\n");
while(afgets(line, 4096, af));
/* Check last line */
cr_assert_str_eq(line, "1497710478.862332239(99999) 0.752074 -0.006283 1.000000 0.996000\n");
ret = afclose(af);
cr_assert_eq(ret, 0, "Failed to close file");
}
Test(advio, resume)
{
int ret;
char *retp;
AFILE *af1, *af2;
char *fn, dir[] = "/tmp/temp.XXXXXX";
char line1[32];
char *line2 = NULL;
size_t linelen = 0;
retp = mkdtemp(dir);
cr_assert_not_null(retp);
ret = asprintf(&fn, "%s/file", dir);
cr_assert_gt(ret, 0);
af1 = afopen(fn, "w+");
cr_assert_not_null(af1);
/* We flush once the empty file in order to upload an empty file. */
aupload(af1, 0);
af2 = afopen(fn, "r");
cr_assert_not_null(af2);
for (int i = 0; i < 100; i++) {
snprintf(line1, sizeof(line1), "This is line %d\n", i);
afputs(line1, af1);
aupload(af1, 1);
adownload(af2, 1);
ret = agetline(&line2, &linelen, af2);
cr_assert_gt(ret, 0);
cr_assert_str_eq(line1, line2);
}
ret = afclose(af1);
cr_assert_eq(ret, 0);
ret = afclose(af2);
cr_assert_eq(ret, 0);
ret = unlink(fn);
cr_assert_eq(ret, 0);
ret = rmdir(dir);
cr_assert_eq(ret, 0);
free(line2);
}
Test(advio, upload)
{
AFILE *af;
int ret;
size_t len;
char upload[64];
char buffer[64];
/* Get some random data to upload */
len = read_random(upload, sizeof(upload));
cr_assert_eq(len, sizeof(upload));
/* Open file for writing */
af = afopen(BASE_URI "/upload", "w+");
cr_assert(af, "Failed to download file");
len = afwrite(upload, 1, sizeof(upload), af);
cr_assert_eq(len, sizeof(upload));
ret = afclose(af);
cr_assert_eq(ret, 0, "Failed to close/upload file");
/* Open for reading and comparison */
af = afopen(BASE_URI "/upload", "r");
cr_assert(af, "Failed to download file");
len = afread(buffer, 1, sizeof(upload), af);
cr_assert_eq(len, sizeof(upload));
cr_assert_arr_eq(buffer, upload, len);
ret = afclose(af);
cr_assert(ret == 0, "Failed to close file");
}
Test(advio, append)
{
AFILE *af;
int ret;
size_t len;
char append1[64] = "xa5gieTohlei9iu1uVaePae6Iboh3eeheeme5iejue5sheshae4uzisha9Faesei";
char append2[64] = "bitheeRae7igee2miepahJaefoGad1Ooxeif0Mooch4eojoumueYahn4ohc9poo2";
char expect[128] = "xa5gieTohlei9iu1uVaePae6Iboh3eeheeme5iejue5sheshae4uzisha9FaeseibitheeRae7igee2miepahJaefoGad1Ooxeif0Mooch4eojoumueYahn4ohc9poo2";
char buffer[128];
/* Open file for writing first chunk */
af = afopen(BASE_URI "/append", "w+");
cr_assert(af, "Failed to download file");
/* The append file might already exist and be not empty from a previous run. */
ret = ftruncate(afileno(af), 0);
cr_assert_eq(ret, 0);
char c;
fseek(af->file, 0, SEEK_SET);
if (af->file) {
while ((c = getc(af->file)) != EOF)
putchar(c);
}
len = afwrite(append1, 1, sizeof(append1), af);
cr_assert_eq(len, sizeof(append1));
ret = afclose(af);
cr_assert_eq(ret, 0, "Failed to close/upload file");
/* Open file for writing second chunk */
af = afopen(BASE_URI "/append", "a");
cr_assert(af, "Failed to download file");
len = afwrite(append2, 1, sizeof(append2), af);
cr_assert_eq(len, sizeof(append2));
ret = afclose(af);
cr_assert_eq(ret, 0, "Failed to close/upload file");
/* Open for reading and comparison */
af = afopen(BASE_URI "/append", "r");
cr_assert(af, "Failed to download file");
len = afread(buffer, 1, sizeof(buffer), af);
cr_assert_eq(len, sizeof(buffer));
ret = afclose(af);
cr_assert(ret == 0, "Failed to close file");
cr_assert_arr_eq(buffer, expect, sizeof(expect));
}

View file

@ -1,130 +0,0 @@
/** Unit tests for advio
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* 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
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <criterion/criterion.h>
#include <villas/bitset.h>
#include <villas/utils.h>
#define LEN 1027
Test(bitset, simple)
{
int ret;
struct bitset bs;
int bits[] = { 23, 223, 25, 111, 252, 86, 222, 454, LEN-1 };
ret = bitset_init(&bs, LEN);
cr_assert_eq(ret, 0);
for (int i = 0; i < ARRAY_LEN(bits); i++) {
bitset_set(&bs, bits[i]);
cr_assert_eq(ret, 0);
}
for (int i = 0; i < ARRAY_LEN(bits); i++) {
ret = bitset_test(&bs, bits[i]);
cr_assert_eq(ret, 1, "Failed at bit %d", i);
}
for (int i = 0; i < ARRAY_LEN(bits); i++) {
ret = bitset_clear(&bs, bits[i]);
cr_assert_eq(ret, 0, "Failed at bit %d", i);
}
for (int i = 0; i < LEN; i++) {
ret = bitset_test(&bs, i);
cr_assert_eq(ret, 0);
}
ret = bitset_destroy(&bs);
cr_assert_eq(ret, 0);
}
Test(bitset, outofbounds)
{
int ret;
struct bitset bs;
ret = bitset_init(&bs, LEN);
cr_assert_eq(ret, 0);
ret = bitset_set(&bs, LEN+1);
cr_assert_eq(ret, -1);
ret = bitset_test(&bs, LEN+1);
cr_assert_eq(ret, -1);
ret = bitset_destroy(&bs);
cr_assert_eq(ret, 0);
}
Test(bitset, cmp)
{
int ret;
struct bitset bs1, bs2;
ret = bitset_init(&bs1, LEN);
cr_assert_eq(ret, 0);
ret = bitset_init(&bs2, LEN);
cr_assert_eq(ret, 0);
ret = bitset_set(&bs1, 525);
cr_assert_eq(ret, 0);
ret = bitset_set(&bs2, 525);
cr_assert_eq(ret, 0);
ret = bitset_cmp(&bs1, &bs2);
cr_assert_eq(ret, 0);
ret = bitset_clear(&bs2, 525);
cr_assert_eq(ret, 0);
ret = bitset_cmp(&bs1, &bs2);
cr_assert_neq(ret, 0);
ret = bitset_destroy(&bs1);
cr_assert_eq(ret, 0);
ret = bitset_destroy(&bs2);
cr_assert_eq(ret, 0);
}
Test(bitset, all)
{
int ret;
struct bitset bs;
ret = bitset_init(&bs, LEN);
cr_assert_eq(ret, 0);
for (int i = 0; i < LEN; i++) {
bitset_test(&bs, i);
cr_assert_eq(ret, 0);
}
ret = bitset_destroy(&bs);
cr_assert_eq(ret, 0);
}