From 8b4ce0ba86d0396bde2546dd248c863876da4106 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 11 Jun 2017 10:08:37 +0100 Subject: Another step toward filters working --- .editorconfig | 19 ++++ include/nslog/nslog.h | 82 +++++++++---- src/Makefile | 2 +- src/core.c | 28 ++--- src/filter.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nslog_internal.h | 29 +++++ test/Makefile | 2 +- test/basic.c | 10 +- test/explicitfilter.c | 50 ++++++++ test/runtest.sh | 2 +- 10 files changed, 488 insertions(+), 46 deletions(-) create mode 100644 .editorconfig create mode 100644 src/filter.c create mode 100644 src/nslog_internal.h create mode 100644 test/explicitfilter.c diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f24cd50 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# This is an EditorConfig file +# Learn more at http://editorconfig.org + +root = true + +# All files are UNIX-style and UTF-8 +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +# Makefiles use tabs +[Makefile] +indent_style = tab + +# C code uses tabs +[*.{c,h}] +indent_style = tab diff --git a/include/nslog/nslog.h b/include/nslog/nslog.h index ffa87bd..550bffb 100644 --- a/include/nslog/nslog.h +++ b/include/nslog/nslog.h @@ -4,7 +4,7 @@ * This file is part of libnslog. * * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/mit-license.php */ /** @@ -18,41 +18,44 @@ #include typedef enum { - NSLOG_LEVEL_DEEPDEBUG = 0, - NSLOG_LEVEL_DEBUG = 1, - NSLOG_LEVEL_VERBOSE = 2, - NSLOG_LEVEL_INFO = 3, - NSLOG_LEVEL_WARNING = 4, - NSLOG_LEVEL_ERROR = 5, - NSLOG_LEVEL_CRITICAL = 6, + NSLOG_LEVEL_DEEPDEBUG = 0, + NSLOG_LEVEL_DEBUG = 1, + NSLOG_LEVEL_VERBOSE = 2, + NSLOG_LEVEL_INFO = 3, + NSLOG_LEVEL_WARNING = 4, + NSLOG_LEVEL_ERROR = 5, + NSLOG_LEVEL_CRITICAL = 6, } nslog_level; const char *nslog_level_name(nslog_level level); -#define NSLOG_LEVEL_DD NSLOG_LEVEL_DEEPDEBUG -#define NSLOG_LEVEL_CHAT NSLOG_LEVEL_VERBOSE -#define NSLOG_LEVEL_WARN NSLOG_LEVEL_WARNING -#define NSLOG_LEVEL_ERR NSLOG_LEVEL_ERROR -#define NSLOG_LEVEL_CRIT NSLOG_LEVEL_CRITICAL +#define NSLOG_LEVEL_DD NSLOG_LEVEL_DEEPDEBUG +#define NSLOG_LEVEL_CHAT NSLOG_LEVEL_VERBOSE +#define NSLOG_LEVEL_WARN NSLOG_LEVEL_WARNING +#define NSLOG_LEVEL_ERR NSLOG_LEVEL_ERROR +#define NSLOG_LEVEL_CRIT NSLOG_LEVEL_CRITICAL #ifndef NSLOG_COMPILED_MIN_LEVEL #define NSLOG_COMPILED_MIN_LEVEL NSLOG_LEVEL_DEBUG #endif typedef struct nslog_category_s { - const char *cat_name; - const char *description; - struct nslog_category_s *parent; - char *name; - struct nslog_category_s *next; + const char *cat_name; + const char *description; + struct nslog_category_s *parent; + char *name; + int namelen; + struct nslog_category_s *next; } nslog_category_t; typedef struct nslog_entry_context_s { - nslog_category_t *category; - nslog_level level; - const char *filename; + nslog_category_t *category; + nslog_level level; + const char *filename; + int filenamelen; const char *funcname; - int lineno; + int funcnamelen; + int lineno; } nslog_entry_context_t; #define NSLOG_DECLARE_CATEGORY(catname) \ @@ -64,6 +67,7 @@ typedef struct nslog_entry_context_s { description, \ NULL, \ NULL, \ + 0, \ NULL, \ } @@ -73,6 +77,7 @@ typedef struct nslog_entry_context_s { description, \ &__nslog_category_##parentcatname, \ NULL, \ + 0, \ NULL, \ } @@ -83,7 +88,9 @@ typedef struct nslog_entry_context_s { &__nslog_category_##catname, \ NSLOG_LEVEL_##level, \ __FILE__, \ + sizeof(__FILE__) - 1, \ __PRETTY_FUNCTION__, \ + sizeof(__PRETTY_FUNCTION__) - 1, \ __LINE__, \ }; \ nslog__log(&ctx, logmsg, ##args); \ @@ -106,4 +113,35 @@ nslog_error nslog_set_render_callback(nslog_callback cb, void *context); nslog_error nslog_uncork(void); +typedef struct nslog_filter_s nslog_filter_t; + +nslog_error nslog_filter_category_new(const char *catname, + nslog_filter_t **filter); +nslog_error nslog_filter_level_new(nslog_level level, + nslog_filter_t **filter); +nslog_error nslog_filter_filename_new(const char *filename, + nslog_filter_t **filter); +nslog_error nslog_filter_dirname_new(const char *dirname, + nslog_filter_t **filter); +nslog_error nslog_filter_funcname_new(const char *funcname, + nslog_filter_t **filter); + +nslog_error nslog_filter_and_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter); +nslog_error nslog_filter_or_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter); +nslog_error nslog_filter_xor_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter); +nslog_error nslog_filter_not_new(nslog_filter_t *input, + nslog_filter_t **filter); + +nslog_filter_t *nslog_filter_ref(nslog_filter_t *filter); +nslog_filter_t *nslog_filter_unref(nslog_filter_t *filter); + +nslog_error nslog_filter_set_active(nslog_filter_t *filter, + nslog_filter_t **prev); + #endif /* NSLOG_NSLOG_H_ */ diff --git a/src/Makefile b/src/Makefile index 45ecb90..7fbad1c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,3 +1,3 @@ -DIR_SOURCES := core.c +DIR_SOURCES := core.c filter.c include $(NSBUILD)/Makefile.subdir diff --git a/src/core.c b/src/core.c index ee5f2bc..978f169 100644 --- a/src/core.c +++ b/src/core.c @@ -4,7 +4,7 @@ * This file is part of libnslog. * * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/mit-license.php */ /** @@ -12,21 +12,14 @@ * NetSurf Logging Core */ -#include "nslog/nslog.h" - -#include -#include -#include -#include -#include - +#include "nslog_internal.h" static bool nslog__corked = true; static struct nslog_cork_chain { struct nslog_cork_chain *next; nslog_entry_context_t context; - char message[0]; /* NUL terminated */ + char message[0]; /* NUL terminated */ } *nslog__cork_chain = NULL, *nslog__cork_chain_last = NULL; static nslog_callback nslog__cb = NULL; @@ -52,7 +45,7 @@ const char *nslog_level_name(nslog_level level) case NSLOG_LEVEL_CRITICAL: return "CRITICAL"; }; - + return "**UNKNOWN**"; } @@ -61,6 +54,7 @@ static void nslog__normalise_category(nslog_category_t *cat) { if (cat->parent == NULL) { cat->name = strdup(cat->cat_name); + cat->namelen = strlen(cat->name); } else { nslog__normalise_category(cat->parent); cat->name = malloc(strlen(cat->parent->name) + strlen(cat->cat_name) + 2); @@ -68,6 +62,7 @@ static void nslog__normalise_category(nslog_category_t *cat) strcat(cat->name, "/"); strcat(cat->name, cat->cat_name); cat->next = nslog__all_categories; + cat->namelen = strlen(cat->name); nslog__all_categories = cat; } } @@ -102,7 +97,8 @@ static void nslog__log_uncorked(nslog_entry_context_t *ctx, if (ctx->category->name == NULL) { nslog__normalise_category(ctx->category); } - (*nslog__cb)(nslog__cb_ctx, ctx, fmt, args); + if (nslog__filter_matches(ctx)) + (*nslog__cb)(nslog__cb_ctx, ctx, fmt, args); } } @@ -129,7 +125,7 @@ nslog_error nslog_set_render_callback(nslog_callback cb, void *context) { nslog__cb = cb; nslog__cb_ctx = context; - + return NSLOG_NO_ERROR; } @@ -155,12 +151,12 @@ nslog_error nslog_uncork() if (ent->context.category->name == NULL) { nslog__normalise_category(ent->context.category); } - __nslog__deliver_corked_entry(&ent->context, - "%s", ent->message); + if (nslog__filter_matches(&ent->context)) + __nslog__deliver_corked_entry(&ent->context, + "%s", ent->message); free(ent); } nslog__corked = false; } return NSLOG_NO_ERROR; } - diff --git a/src/filter.c b/src/filter.c new file mode 100644 index 0000000..1dc4e81 --- /dev/null +++ b/src/filter.c @@ -0,0 +1,310 @@ +/* + * Copyright 2017 Daniel Silverstone + * + * This file is part of libnslog. + * + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** + * \file + * NetSurf Logging Filters + */ + +#include "nslog_internal.h" + +typedef enum { + /* Fundamentals */ + NSLFK_CATEGORY = 0, + NSLFK_LEVEL, + NSLFK_FILENAME, + NSLFK_DIRNAME, + NSLFK_FUNCNAME, + /* logical operations */ + NSLFK_AND, + NSLFK_OR, + NSLFK_XOR, + NSLFK_NOT, +} nslog_filter_kind; + +struct nslog_filter_s { + nslog_filter_kind kind; + int refcount; + union { + struct { + char *ptr; + int len; + } str; + nslog_level level; + nslog_filter_t *unary_input; + struct { + nslog_filter_t *input1; + nslog_filter_t *input2; + } binary; + } params; +}; + +static nslog_filter_t *nslog__active_filter = NULL; + +nslog_error nslog_filter_category_new(const char *catname, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_CATEGORY; + ret->refcount = 1; + ret->params.str.ptr = strdup(catname); + ret->params.str.len = strlen(catname); + if (ret->params.str.ptr == NULL) { + free(ret); + return NSLOG_NO_MEMORY; + } + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_level_new(nslog_level level, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_LEVEL; + ret->refcount = 1; + ret->params.level = level; + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_filename_new(const char *filename, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_FILENAME; + ret->refcount = 1; + ret->params.str.ptr = strdup(filename); + ret->params.str.len = strlen(filename); + if (ret->params.str.ptr == NULL) { + free(ret); + return NSLOG_NO_MEMORY; + } + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_dirname_new(const char *dirname, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_DIRNAME; + ret->refcount = 1; + ret->params.str.ptr = strdup(dirname); + ret->params.str.len = strlen(dirname); + if (ret->params.str.ptr == NULL) { + free(ret); + return NSLOG_NO_MEMORY; + } + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_funcname_new(const char *funcname, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_FUNCNAME; + ret->refcount = 1; + ret->params.str.ptr = strdup(funcname); + ret->params.str.len = strlen(funcname); + if (ret->params.str.ptr == NULL) { + free(ret); + return NSLOG_NO_MEMORY; + } + *filter = ret; + return NSLOG_NO_ERROR; +} + + +nslog_error nslog_filter_and_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_AND; + ret->refcount = 1; + ret->params.binary.input1 = nslog_filter_ref(left); + ret->params.binary.input2 = nslog_filter_ref(right); + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_or_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_OR; + ret->refcount = 1; + ret->params.binary.input1 = nslog_filter_ref(left); + ret->params.binary.input2 = nslog_filter_ref(right); + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_xor_new(nslog_filter_t *left, + nslog_filter_t *right, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_XOR; + ret->refcount = 1; + ret->params.binary.input1 = nslog_filter_ref(left); + ret->params.binary.input2 = nslog_filter_ref(right); + *filter = ret; + return NSLOG_NO_ERROR; +} + +nslog_error nslog_filter_not_new(nslog_filter_t *input, + nslog_filter_t **filter) +{ + nslog_filter_t *ret = calloc(sizeof(*ret), 1); + if (ret == NULL) + return NSLOG_NO_MEMORY; + ret->kind = NSLFK_NOT; + ret->refcount = 1; + ret->params.unary_input = nslog_filter_ref(input); + *filter = ret; + return NSLOG_NO_ERROR; +} + + +nslog_filter_t *nslog_filter_ref(nslog_filter_t *filter) +{ + if (filter != NULL) + filter->refcount++; + + return filter; +} + +nslog_filter_t *nslog_filter_unref(nslog_filter_t *filter) +{ + if (filter != NULL && filter->refcount-- == 1) { + switch(filter->kind) { + case NSLFK_CATEGORY: + case NSLFK_FILENAME: + case NSLFK_DIRNAME: + case NSLFK_FUNCNAME: + free(filter->params.str.ptr); + break; + case NSLFK_AND: + case NSLFK_OR: + case NSLFK_XOR: + nslog_filter_unref(filter->params.binary.input1); + nslog_filter_unref(filter->params.binary.input2); + break; + case NSLFK_NOT: + nslog_filter_unref(filter->params.unary_input); + break; + default: + /* Nothing to do for the other kind(s) */ + break; + } + free(filter); + } + + return NULL; +} + +nslog_error nslog_filter_set_active(nslog_filter_t *filter, + nslog_filter_t **prev) +{ + if (prev != NULL) + *prev = nslog__active_filter; + else + nslog_filter_unref(nslog__active_filter); + + nslog__active_filter = nslog_filter_ref(filter); + + return NSLOG_NO_ERROR; +} + +static bool _nslog__filter_matches(nslog_entry_context_t *ctx, + nslog_filter_t *filter) +{ + switch (filter->kind) { + case NSLFK_CATEGORY: + if (filter->params.str.len > ctx->category->namelen) + return false; + if (ctx->category->name[filter->params.str.len] != '\0' && + ctx->category->name[filter->params.str.len] != '/') + return false; + return (strncmp(filter->params.str.ptr, + ctx->category->name, + filter->params.str.len) == 0); + + case NSLFK_LEVEL: + return (ctx->level >= filter->params.level); + case NSLFK_FILENAME: + if (filter->params.str.len > ctx->filenamelen) + return false; + if ((filter->params.str.len == ctx->filenamelen) && + (strcmp(filter->params.str.ptr, ctx->filename) == 0)) + return true; + if ((ctx->filename[ctx->filenamelen - filter->params.str.len - 1] == '/') + && (strcmp(filter->params.str.ptr, + ctx->filename + ctx->filenamelen - filter->params.str.len) == 0)) + return true; + return false; + case NSLFK_DIRNAME: + if (filter->params.str.len >= ctx->filenamelen) + return false; + if ((ctx->filename[filter->params.str.len] == '/') + && (strncmp(filter->params.str.ptr, + ctx->filename, + filter->params.str.len) == 0)) + return true; + return false; + case NSLFK_FUNCNAME: + return (filter->params.str.len == ctx->funcnamelen && + strcmp(ctx->funcname, filter->params.str.ptr) == 0); + case NSLFK_AND: + return (_nslog__filter_matches(ctx, filter->params.binary.input1) + && + _nslog__filter_matches(ctx, filter->params.binary.input2)); + case NSLFK_OR: + return (_nslog__filter_matches(ctx, filter->params.binary.input1) + || + _nslog__filter_matches(ctx, filter->params.binary.input2)); + case NSLFK_XOR: + return (_nslog__filter_matches(ctx, filter->params.binary.input1) + ^ + _nslog__filter_matches(ctx, filter->params.binary.input2)); + case NSLFK_NOT: + return !_nslog__filter_matches(ctx, filter->params.unary_input); + default: + /* unknown */ + assert("Unknown filter kind" == NULL); + return false; + } +} + +bool nslog__filter_matches(nslog_entry_context_t *ctx) +{ + if (nslog__active_filter == NULL) + return true; + return _nslog__filter_matches(ctx, nslog__active_filter); +} diff --git a/src/nslog_internal.h b/src/nslog_internal.h new file mode 100644 index 0000000..d5519e7 --- /dev/null +++ b/src/nslog_internal.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Daniel Silverstone + * + * This file is part of libnslog. + * + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** + * \file + * NetSurf Logging + */ + +#ifndef NSLOG_INTERNAL_H_ +#define NSLOG_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include + +#include "nslog/nslog.h" + +bool nslog__filter_matches(nslog_entry_context_t *ctx); + +#endif /* NSLOG_INTERNAL_H_ */ diff --git a/test/Makefile b/test/Makefile index 0cebdaa..033033f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,3 +1,3 @@ -DIR_TEST_ITEMS := basic:basic.c +DIR_TEST_ITEMS := basic:basic.c explicitfilter:explicitfilter.c include $(NSBUILD)/Makefile.subdir diff --git a/test/basic.c b/test/basic.c index 7626dd8..d514814 100644 --- a/test/basic.c +++ b/test/basic.c @@ -4,7 +4,7 @@ * This file is part of libnslog. * * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/mit-license.php */ #include "nslog/nslog.h" @@ -18,10 +18,10 @@ static void test_render_function( const char *fmt, va_list args) { (void)ctx; - fprintf(stderr, "%s %s:%d [%s] %s() ", - nslog_level_name(ctx->level), - ctx->filename, ctx->lineno, - ctx->category->name, + fprintf(stderr, "%s %s:%d [%s] %s() ", + nslog_level_name(ctx->level), + ctx->filename, ctx->lineno, + ctx->category->name, ctx->funcname); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); diff --git a/test/explicitfilter.c b/test/explicitfilter.c new file mode 100644 index 0000000..c7aca8a --- /dev/null +++ b/test/explicitfilter.c @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Daniel Silverstone + * + * This file is part of libnslog. + * + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +#include "nslog/nslog.h" + +#include +#include + +NSLOG_DEFINE_CATEGORY(test, "Test category"); + +static void test_render_function( + void *_ctx, nslog_entry_context_t *ctx, + const char *fmt, va_list args) +{ + (void)ctx; + fprintf(stderr, "%s %s:%d [%s] %s() ", + nslog_level_name(ctx->level), + ctx->filename, ctx->lineno, + ctx->category->name, + ctx->funcname); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) +{ + nslog_set_render_callback(test_render_function, NULL); + nslog_uncork(); + + nslog_filter_t *cat_test, *cat_another; + + assert(nslog_filter_category_new("test", &cat_test) == NSLOG_NO_ERROR); + assert(nslog_filter_category_new("another", &cat_another) == NSLOG_NO_ERROR); + + NSLOG(test, INFO, "Hurrah, a message!"); + assert(nslog_filter_set_active(cat_test, NULL) == NSLOG_NO_ERROR); + NSLOG(test, INFO, "You should see me."); + assert(nslog_filter_set_active(cat_another, NULL) == NSLOG_NO_ERROR); + NSLOG(test, INFO, "You should not see me!"); + assert(nslog_filter_set_active(NULL, NULL) == NSLOG_NO_ERROR); + NSLOG(test, INFO, "You should see this one though."); + + return 0; +} diff --git a/test/runtest.sh b/test/runtest.sh index 1b67a97..3bf903e 100755 --- a/test/runtest.sh +++ b/test/runtest.sh @@ -5,7 +5,7 @@ set -e TEST_PATH=$1 TEST_PFX=$4 -for TEST in basic; do +for TEST in basic explicitfilter; do ${TEST_PATH}/${TEST_PFX}${TEST} done -- cgit v1.2.3