/* * 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 Core */ #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 */ } *nslog__cork_chain = NULL, *nslog__cork_chain_last = NULL; static nslog_callback nslog__cb = NULL; static void *nslog__cb_ctx = NULL; static nslog_category_t *nslog__all_categories = NULL; const char *nslog_level_name(nslog_level level) { switch (level) { case NSLOG_LEVEL_DEEPDEBUG: return "DEEPDEBUG"; case NSLOG_LEVEL_DEBUG: return "DEBUG"; case NSLOG_LEVEL_VERBOSE: return "VERBOSE"; case NSLOG_LEVEL_INFO: return "INFO"; case NSLOG_LEVEL_WARNING: return "WARNING"; case NSLOG_LEVEL_ERROR: return "ERROR"; case NSLOG_LEVEL_CRITICAL: return "CRITICAL"; }; return "**UNKNOWN**"; } 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); strcpy(cat->name, cat->parent->name); strcat(cat->name, "/"); strcat(cat->name, cat->cat_name); cat->next = nslog__all_categories; cat->namelen = strlen(cat->name); nslog__all_categories = cat; } } static void nslog__log_corked(nslog_entry_context_t *ctx, int measured_len, const char *fmt, va_list args) { /* If corked, we need to store a copy */ struct nslog_cork_chain *newcork = calloc(sizeof(struct nslog_cork_chain) + measured_len + 1, 1); if (newcork == NULL) { /* Wow, something went wrong */ return; } newcork->context = *ctx; vsprintf(newcork->message, fmt, args); if (nslog__cork_chain == NULL) { nslog__cork_chain = nslog__cork_chain_last = newcork; } else { nslog__cork_chain_last->next = newcork; nslog__cork_chain_last = newcork; } } static void nslog__log_uncorked(nslog_entry_context_t *ctx, const char *fmt, va_list args) { /* TODO: Add filtering here */ if (nslog__cb != NULL) { if (ctx->category->name == NULL) { nslog__normalise_category(ctx->category); } if (nslog__filter_matches(ctx)) (*nslog__cb)(nslog__cb_ctx, ctx, fmt, args); } } void nslog__log(nslog_entry_context_t *ctx, const char *pattern, ...) { va_list ap; va_start(ap, pattern); if (nslog__corked) { va_list ap2; va_copy(ap2, ap); int slen = vsnprintf(NULL, 0, pattern, ap); va_end(ap); nslog__log_corked(ctx, slen, pattern, ap2); va_end(ap2); } else { nslog__log_uncorked(ctx, pattern, ap); va_end(ap); } } nslog_error nslog_set_render_callback(nslog_callback cb, void *context) { nslog__cb = cb; nslog__cb_ctx = context; return NSLOG_NO_ERROR; } static void __nslog__deliver_corked_entry(nslog_entry_context_t *ctx, const char *fmt, ...) { va_list args; va_start(args, fmt); if (nslog__cb != NULL) { (*nslog__cb)(nslog__cb_ctx, ctx, fmt, args); } va_end(args); } nslog_error nslog_uncork() { if (nslog__corked) { while (nslog__cork_chain != NULL) { struct nslog_cork_chain *ent = nslog__cork_chain; nslog__cork_chain = ent->next; if (ent->context.category->name == NULL) { nslog__normalise_category(ent->context.category); } if (nslog__filter_matches(&ent->context)) __nslog__deliver_corked_entry(&ent->context, "%s", ent->message); free(ent); } nslog__corked = false; return NSLOG_NO_ERROR; } else { return NSLOG_UNCORKED; } }