// lognestmonster Copyright (c) 2020 Joshua 'joshuas3' Stockin // // // // This software is licensed under the MIT License. // ----- BEGIN LICENSE ----- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ----- END LICENSE ----- // // lognestmonster.h // C header file for implementation of the lognestmonster logging library // SEMANTICS // internal definitions: lnm_lower_camel_case // public definitions: lnmUpperCamelCase #ifdef __cplusplus // Linker protection extern "C" { #endif // Declarations #ifndef LOGNESTMONSTER_H #define LOGNESTMONSTER_H 1 // stdc inclusions #include #include #include #include #include // non-universal inclusions #include enum lnmVerbosityLevel {lnmInfo, lnmDebug, lnmVerbose, lnmVeryVerbose, lnmWarning, lnmError}; typedef uint8_t * lnmItem; typedef uint8_t * lnmQueue; lnmQueue lnmQueueInit(char * name, char * out_path); lnmQueue lnmQueueByName(char * name); lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, char * tag, char * message); lnmItem lnmEvent(void); void lnmEventPush(lnmItem event, lnmItem item); void lnmEventPushS(lnmItem event, uint8_t verbosity, char * tag, char * message); lnmItem lnmEventI(lnmItem item); lnmItem lnmEventS(uint8_t verbosity, char * tag, char * message); #ifdef LNM_ALL // Exposes the private API declarations // Pushable utilities typedef struct lnm_pushable lnm_pushable; void lnm_pushable_realloc(lnm_pushable * pushable); lnm_pushable * lnm_new_pushable(void); void lnm_pushable_push(lnm_pushable * pushable, lnmItem item); int lnm_pushable_indexof(lnm_pushable * pushable, lnmItem item); void lnm_pushable_pop(lnm_pushable * pushable); void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index); void lnm_pushable_free(lnm_pushable * pushable); // Log item and object struct types typedef struct lnm_log_event lnm_log_event; typedef struct lnm_log_statement lnm_log_statement; typedef struct lnm_queue lnm_queue; // General utilities unsigned long lnm_getus(void); unsigned long lnm_getms(void); int lnm_isstatement(lnmItem item); // Registry utilities void lnm_registry_update(void); // Memory utilities void lnm_free_item(lnmItem item); void lnm_free_registry(void); void lnm_free_queue(lnmQueue queue); // Debug utilities void lnm_debug_tabs(int count); void lnm_debug_parse_item(lnmItem item, int tabcount); void lnm_debug_parse_registry(void); void lnm_debug_parse_queue(lnmQueue queue); #endif // LNM_ALL, private declarations #endif // LOGNESTMONSTER_H, public declarations #ifdef LNM_INIT // one-time definitions // Pushable structure typedef struct lnm_pushable { uint32_t capacity; uint32_t length; lnmItem * pushed; } lnm_pushable; void lnm_pushable_realloc(lnm_pushable * pushable) { if (pushable->length > pushable->capacity) { if (pushable->capacity * 2 <= pushable->capacity) { printf("lognestmonster (lnm_pushable_realloc): pushable reached max length of 2^32-1. exiting...\n"); exit(1); } pushable->pushed = realloc(pushable->pushed, sizeof(lnmItem) * (pushable->capacity *= 2)); } else if (pushable->length < (pushable->capacity / 2)) { if (pushable->capacity > 8) { pushable->pushed = realloc(pushable->pushed, sizeof(lnmItem) * (pushable->capacity /= 2)); } } } lnm_pushable * lnm_new_pushable(void) { lnm_pushable * new_pushable = malloc(sizeof(lnm_pushable)); new_pushable->capacity = 8; new_pushable->length = 0; new_pushable->pushed = malloc(sizeof(lnmItem)*new_pushable->capacity); return new_pushable; } void lnm_pushable_push(lnm_pushable * pushable, lnmItem item) { pushable->length++; lnm_pushable_realloc(pushable); pushable->pushed[pushable->length-1] = item; } int lnm_pushable_indexof(lnm_pushable * pushable, lnmItem item) { int len = pushable->length; for (int iter = 0; iterpushed[iter]) return iter; } printf("lognestmonster (lnm_pushable_indexof): cannot find item in pushable. exiting...\n"); exit(1); } void lnm_pushable_pop(lnm_pushable * pushable) { pushable->length--; lnm_pushable_realloc(pushable); } void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index) { if (index>=pushable->length) { printf("lognestmonster (lnm_pushable_remove): attempt to remove index out of pushable bounds. exiting...\n"); exit(1); } for (uint32_t iter = index; iterlength-1; iter++) { pushable->pushed[iter] = pushable->pushed[iter+1]; } pushable->length--; lnm_pushable_realloc(pushable); } void lnm_pushable_free(lnm_pushable * pushable) { free(pushable->pushed); free(pushable); } // Statement and event structure definitions typedef struct lnm_log_event { // word 1, 1 byte data 7 bytes padding uint8_t type:1; // Used internally; 0 = statement, 1 = event uint8_t boolpushed:1; // whether or not this log item has been pushed uint8_t verbosity:3; // lnmVerbosityLevel, 0-5 // word 2, 8 bytes data char * tag; // tag string // word 3, 8 bytes data lnm_pushable * pushed; // array of memory locations for lnm_log_event and lnm_log_statement structs } lnm_log_event; typedef struct lnm_log_statement { // word 1, 4 bytes data 4 bytes padding uint8_t type:1; // Used internally; 0 = statement, 1 = event uint8_t boolpushed:1; // whether or not this log item has been pushed uint8_t verbosity:3; // lnmVerbosityLevel, 0-5 uint8_t tag_size; // character length of the tag uint16_t message_size; // character length of the message // word 2, 8 bytes data uint64_t timestamp; // 64-bit millisecond timestamp // word 3, 8 bytes data char * log; // tag string + message string } lnm_log_statement; // Queue structure definition typedef struct lnm_queue { char * name; char * out_path; uint64_t timestamp; lnm_pushable * pushed; } lnm_queue; // Library utilities unsigned long lnm_getus(void) { struct timeval current_time; gettimeofday(¤t_time, NULL); unsigned long ms = (current_time.tv_sec*1000000+current_time.tv_usec); return ms; } unsigned long lnm_getms(void) { return lnm_getus()/1000; } int lnm_isstatement(lnmItem item) { lnm_log_statement * s = (lnm_log_statement *)item; return !s->type; } // Item registry utils static lnm_pushable * lnm_registered_queues; static lnm_pushable * lnm_registered_items; static int lnm_registry_update_count; void lnm_registry_update(void) { // scan each registered item for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) { lnm_log_statement * s = (lnm_log_statement *)lnm_registered_items->pushed[iter]; if (s->boolpushed == 1) { // if the registered item has been pushed elsewhere, remove it from the top level of the registry lnm_registry_update_count++; lnm_pushable_remove(lnm_registered_items, iter); iter--; } } } // Core library void lnm_free_item(lnmItem item) { lnm_log_statement * item_cast = (lnm_log_statement *)item; if (item_cast->boolpushed == 0) { // flush item out of registry item_cast->boolpushed = 1; lnm_registry_update(); } if (item_cast->type == 0) { free(item_cast->log); free(item_cast); } else if (item_cast->type == 1) { lnm_log_event * event_cast = (lnm_log_event *)item_cast; lnm_pushable * breadcrumb = lnm_new_pushable(); lnm_pushable_push(breadcrumb, (lnmItem)event_cast); // add event_cast as the first step down the breadcrumb while (breadcrumb->length > 0) { // while there are items still in the breadcrumb lnm_log_statement * breadcrumb_item = (lnm_log_statement *)breadcrumb->pushed[breadcrumb->length-1]; // fetch the last (deepest) item if (breadcrumb_item->type == 0) { // the item is a statement lnm_pushable_pop(breadcrumb); // remove it from the breadcrumb free(breadcrumb_item->log); // and free it free(breadcrumb_item); } else if (breadcrumb_item->type == 1) { lnm_log_event * breadcrumb_item_cast = (lnm_log_event *)breadcrumb_item; // item is an event, cast pointer if (breadcrumb_item_cast->pushed->length > 0) { // if the event is not empty lnm_pushable_push(breadcrumb, breadcrumb_item_cast->pushed->pushed[breadcrumb_item_cast->pushed->length-1]); // push the last item of event into the breadcrumb lnm_pushable_pop(breadcrumb_item_cast->pushed); // and remove it from this event's index // there is now a new breadcrumb navigation layer. loop back to check the new item... } else { lnm_pushable_pop(breadcrumb); // event is finally empty, remove it from the breadcrumb and free it lnm_pushable_free(breadcrumb_item_cast->pushed); free(breadcrumb_item_cast); } } } } else { printf("lognestmonster (lnm_free_item): non-log item passed to function. exiting...\n"); exit(1); } } void lnm_free_registry() { for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) { lnm_free_item(lnm_registered_items->pushed[iter]); } } void lnm_free_queue(lnmQueue queue) { lnm_queue * queue_cast = (lnm_queue *)queue; for (uint32_t iter = 0; iter < queue_cast->pushed->length; iter++) { lnm_free_item(queue_cast->pushed->pushed[iter]); lnm_pushable_remove(queue_cast->pushed, iter--); } } lnmQueue lnmQueueInit(char * name, char * out_path) { if (lnm_registered_queues == NULL) { lnm_registered_queues = lnm_new_pushable(); } if (lnm_registered_items == NULL) { lnm_registered_items = lnm_new_pushable(); } lnm_queue * new_queue = malloc(sizeof(lnm_queue)); new_queue->name = malloc(strlen(name)+1); new_queue->out_path = malloc(strlen(out_path)+1); strcpy(new_queue->name, name); strcpy(new_queue->out_path, out_path); new_queue->timestamp = lnm_getus(); new_queue->pushed = lnm_new_pushable(); lnm_pushable_push(lnm_registered_queues, (lnmQueue)new_queue); return (lnmQueue)new_queue; } lnmQueue lnmQueueByName(char * name) { if (lnm_registered_queues == NULL) { printf("lognestmonster (lnmQueueByName): queue registry is nonexistant. exiting...\n"); exit(1); } if (lnm_registered_queues->length == 0) { printf("lognestmonster (lnmQueueByName): queue registry is empty. exiting...\n"); exit(1); } for (uint32_t iter = 0; iterlength; iter++) { lnm_queue * iterqueue = (lnm_queue *)lnm_registered_queues->pushed[iter]; if (strcmp(iterqueue->name, name)==0) { return (lnmQueue)iterqueue; } } printf("lognestmonster (lnmQueueByName): queue not found in registry. exiting...\n"); exit(1); } void lnmQueuePush(lnmQueue queue, lnmItem item) { if (((lnm_log_statement *)item)->boolpushed == 1) { printf("lognestmonster (lnmQueuePush): attempt to push an already-pushed log item. exiting...\n"); exit(1); } lnm_pushable_push(((lnm_queue *)queue)->pushed, item); ((lnm_log_statement *)item)->boolpushed = 1; lnm_registry_update(); } lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, char * tag, char * message) { lnm_log_statement * new_statement = malloc(sizeof(lnm_log_statement)); new_statement->type = 0; new_statement->verbosity = verbosity; new_statement->timestamp = lnm_getus(); int tlen = strlen(tag); if (tlen > 256 || tlen < 0) { printf("lognestmonster (lnmStatement): tag length %i is longer than the cap 256 characters. exiting...\n", tlen); exit(1); } int mlen = strlen(message); if (mlen > 65536 || mlen < 0) { printf("lognestmonster (lnmStatement): message length %i is longer than the cap 65536 characters. exiting...\n", mlen); exit(1); } new_statement->tag_size = tlen; new_statement->message_size = mlen; new_statement->log = malloc(tlen+mlen+1); strcpy(new_statement->log, tag); strcat(new_statement->log, message); lnm_registry_update(); lnm_pushable_push(lnm_registered_items, (lnmItem)new_statement); return (lnmItem)new_statement; } lnmItem lnmEvent(void) { lnm_log_event * new_event = malloc(sizeof(lnm_log_event)); new_event->type = 1; new_event->pushed = lnm_new_pushable(); lnm_registry_update(); lnm_pushable_push(lnm_registered_items, (lnmItem)new_event); return (lnmItem)new_event; } void lnmEventPush(lnmItem event, lnmItem item) { if (event == item) { printf("lognestmonster (lnmEventPush): attempt to push event to self. exiting...\n"); exit(1); } lnm_log_statement * item_cast = (lnm_log_statement *)item; if (item_cast->boolpushed == 1) { printf("lognestmonster (lnmEventPush): attempt to push an already-pushed log item. exiting...\n"); exit(1); } lnm_log_event * event_t = (lnm_log_event *)event; if (event_t->type != 1) { printf("lognestmonster (lnmEventPush): cannot cast non-event to event type. exiting...\n"); exit(1); } lnm_pushable_push(event_t->pushed, item); item_cast->boolpushed = 1; lnm_registry_update(); } void lnmEventPushS(lnmItem event, uint8_t verbosity, char * tag, char * message) { lnmItem statement = lnmStatement(verbosity, tag, message); lnmEventPush(event, statement); } lnmItem lnmEventI(lnmItem item) { lnmItem event = lnmEvent(); lnmEventPush(event, item); return event; } lnmItem lnmEventS(uint8_t verbosity, char * tag, char * message) { lnmItem statement = lnmStatement(verbosity, tag, message); return lnmEventI(statement); } void lnm_debug_tabs(int count) { for (int i = 0; i < count; i++) { printf(" "); } } void lnm_debug_parse_item(lnmItem item, int tabcount) { if (lnm_isstatement(item)) { lnm_log_statement * statement = (lnm_log_statement *) item; lnm_debug_tabs(tabcount); char * verbosity; switch (statement->verbosity) { case 0: verbosity = "INFO"; break; case 1: verbosity = "DEBUG"; break; case 2: verbosity = "VERBOSE"; break; case 3: verbosity = "VERYVERBOSE"; break; case 4: verbosity = "WARNING"; break; case 5: verbosity = "ERROR"; break; } char tag[statement->tag_size+1]; strncpy(tag, statement->log, statement->tag_size); tag[statement->tag_size] = '\0'; char message[statement->message_size+1]; strncpy(message, statement->log+statement->tag_size, statement->message_size); message[statement->message_size] = '\0'; printf("%" PRIu64 " (%s) %s :: %s\n", statement->timestamp, verbosity, tag, message); } else if (!lnm_isstatement(item)) { lnm_log_event * event = (lnm_log_event *) item; lnm_debug_tabs(tabcount); printf("Event (%" PRIu32 ") [%" PRIu32 "] [\n", event->pushed->length, event->pushed->capacity); for (uint32_t i = 0; i < event->pushed->length; i++) { lnmItem item = event->pushed->pushed[i]; lnm_debug_parse_item(item, tabcount + 1); } lnm_debug_tabs(tabcount); printf("]\n"); } else { printf("lognestmonster (lnm_debug_parse_item): unknown item type. exiting...\n"); exit(1); } } void lnm_debug_parse_registry(void) { printf("Top level registry (%" PRIu32 ") [%" PRIu32 "] [\n", lnm_registered_items->length, lnm_registered_items->capacity); for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) { lnm_debug_parse_item(lnm_registered_items->pushed[iter], 1); } printf("]\n"); } void lnm_debug_parse_queue(lnmQueue queue) { lnm_queue * queue_cast = (lnm_queue *)queue; printf("Queue \"%s\" at %s (%" PRIu32 ") [%" PRIu32 "] [\n", queue_cast->name, queue_cast->out_path, queue_cast->pushed->length, queue_cast->pushed->capacity); for (uint32_t iter = 0; iter < queue_cast->pushed->length; iter++) { lnm_debug_parse_item((lnmItem)queue_cast->pushed->pushed[iter], 1); } printf("]\n"); } #endif // DEFINE_LOGNESTMONSTER #ifdef __cplusplus // Linker protection } #endif