// lognestmonster Copyright (c) 2019 Joshua 'joshuas3' Stockin // lognestmonster.h // C header file for implementation of the lognestmonster logging library // . // This file is part of lognestmonster. // lognestmonster 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 // (at your option) any later version. // lognestmonster 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 lognestmonster. If not, see . #ifndef __LOGNESTMONSTER__ #define __LOGNESTMONSTER__ 1 // SEMANTICS // internal definitions: lnm_lower_camel_case // public definitions: lnmUpperCamelCase // stdc inclusions #include #include #include #include #include // Base definitions enum lnmVerbosityLevel {lnmInfo, lnmDebug, lnmVerbose, lnmVeryVerbose, lnmWarning, lnmError}; typedef uint8_t * lnmItem; typedef uint8_t * lnmQueue; // Pushable structure typedef struct { uint16_t length; lnmItem * pushed; } lnm_pushable; lnm_pushable * lnm_new_pushable() { lnm_pushable * new_pushable = malloc(sizeof(lnm_pushable)); new_pushable->length = 0; new_pushable->pushed = malloc(0); return new_pushable; } void lnm_pushable_push(lnm_pushable * pushable, lnmItem item) { if (pushable->length+1 >= 65535) { printf("lognestmonster (lnm_pushable_push): pushable reached cap length 65535. exiting...\n"); exit(1); } pushable->pushed = realloc(pushable->pushed, sizeof(lnmItem)*(pushable->length+1)); // reallocate with size: length+1 pushable->pushed[pushable->length] = item; pushable->length += 1; } 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_remove(lnm_pushable * pushable, int index) { if (index>=pushable->length || index < 0) { printf("lognestmonster (lnm_pushable_remove): attempt to remove index out of pushable bounds. exiting...\n"); exit(1); } lnmItem * new_pushed = malloc(sizeof(lnmItem)*(pushable->length-1)); // map array excluding index for (int iter = 0; iterpushed[iter]; } for (int iter = index+1; iterlength; iter++) { new_pushed[iter-1] = pushable->pushed[iter]; } free(pushable->pushed); pushable->length--; pushable->pushed = new_pushed; } void lnm_pushable_free(lnm_pushable * pushable) { free(pushable->pushed); free(pushable); } // Statement and event structure definitions typedef struct { uint8_t type:1; // Used internally; 0 = statement, 1 = event uint8_t boolpushed:1; // whether or not this log item has been pushed lnm_pushable * pushed; // array of memory locations for lnm_log_event and lnm_log_statement structs } lnm_log_event; typedef struct { // 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 { 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 lnm_pushable * lnm_registered_queues; lnm_pushable * lnm_registered_items; static int lnm_registry_update_count; void lnm_registry_update(void) { // scan each registered item for (int 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) { // i'm so sorry 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_remove(breadcrumb, breadcrumb->length-1); // 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_remove(breadcrumb_item_cast->pushed, breadcrumb_item_cast->pushed->length-1); // 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_remove(breadcrumb, breadcrumb->length-1); // 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"); } } void lnm_free_registry() { for (int iter = 0; iter < lnm_registered_items->length; iter++) { lnm_free_item(lnm_registered_items->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->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 (int 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); } lnmItem lnmStatement(uint8_t 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 > 255 || tlen < 0) { printf("lognestmonster (lnmStatement): tag length %i is longer than the cap 255 characters. exiting...\n", tlen); exit(1); } int mlen = strlen(message); if (mlen > 65535 || mlen < 0) { printf("lognestmonster (lnmStatement): message length %i is longer than the cap 65535 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); printf("Statement {\n"); lnm_debug_tabs(tabcount+1); 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; } printf("Verbosity %s\n", verbosity); lnm_debug_tabs(tabcount+1); printf("Timestamp %" PRIu64 "\n", statement->timestamp); lnm_debug_tabs(tabcount+1); char tag[statement->tag_size+1]; strncpy(tag, statement->log, statement->tag_size); tag[statement->tag_size] = '\0'; printf("Tag (%" PRIu8 ") \"%s\"\n", statement->tag_size, tag); lnm_debug_tabs(tabcount+1); char message[statement->message_size+1]; strncpy(message, statement->log+statement->tag_size, statement->message_size); message[statement->message_size] = '\0'; printf("Message (%" PRIu16 ") \"%s\"\n", statement->message_size, message); lnm_debug_tabs(tabcount); printf("}\n"); } else if (!lnm_isstatement(item)) { lnm_log_event * event = (lnm_log_event *) item; lnm_debug_tabs(tabcount); printf("Event (%" PRIu16 ") [\n", event->pushed->length); for (int 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() { printf("Top level registry (%" PRIu16 ") {\n", lnm_registered_items->length); for (int iter = 0; iter < lnm_registered_items->length; iter++) { lnm_debug_parse_item(lnm_registered_items->pushed[iter], 1); } printf("}\n"); } #endif