Index

lognestmonster / 0b71b72

A general-purpose single-header C logging library and parser for event-based logs. (Incomplete)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
17320 Jan 2020 13:180b71b72Include MIT license in lognestmonster headerJosh Stockin180N

Blob @ lognestmonster / src / c / lognestmonster.h

text/plain16583 bytesdownload raw
1// lognestmonster Copyright (c) 2020 Joshua 'joshuas3' Stockin
2// <https://joshstock.in>
3// <https://github.com/JoshuaS3/lognestmonster>
4//
5// This software is licensed under the MIT License.
6// ----- BEGIN LICENSE -----
7// 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:
8//
9// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10//
11// 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.
12// ----- END LICENSE -----
13//
14// lognestmonster.h
15// C header file for implementation of the lognestmonster logging library
16
17
18// SEMANTICS
19// internal definitions: lnm_lower_camel_case
20// public definitions: lnmUpperCamelCase
21
22
23#ifdef __cplusplus // Linker protection
24extern "C" {
25#endif
26
27// Declarations
28#ifndef LOGNESTMONSTER_H
29#define LOGNESTMONSTER_H 1
30
31// stdc inclusions
32
33#include <stdint.h>
34#include <inttypes.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39// non-universal inclusions
40
41#include <sys/time.h>
42
43
44enum lnmVerbosityLevel {lnmInfo, lnmDebug, lnmVerbose, lnmVeryVerbose, lnmWarning, lnmError};
45typedef uint8_t * lnmItem;
46typedef uint8_t * lnmQueue;
47
48lnmQueue lnmQueueInit(char * name, char * out_path);
49lnmQueue lnmQueueByName(char * name);
50lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, char * tag, char * message);
51lnmItem lnmEvent(void);
52void lnmEventPush(lnmItem event, lnmItem item);
53void lnmEventPushS(lnmItem event, uint8_t verbosity, char * tag, char * message);
54lnmItem lnmEventI(lnmItem item);
55lnmItem lnmEventS(uint8_t verbosity, char * tag, char * message);
56
57#ifdef LNM_ALL // Exposes the private API declarations
58
59// Pushable utilities
60typedef struct lnm_pushable lnm_pushable;
61
62void lnm_pushable_realloc(lnm_pushable * pushable);
63lnm_pushable * lnm_new_pushable(void);
64void lnm_pushable_push(lnm_pushable * pushable, lnmItem item);
65int lnm_pushable_indexof(lnm_pushable * pushable, lnmItem item);
66void lnm_pushable_pop(lnm_pushable * pushable);
67void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index);
68void lnm_pushable_free(lnm_pushable * pushable);
69
70// Log item and object struct types
71typedef struct lnm_log_event lnm_log_event;
72typedef struct lnm_log_statement lnm_log_statement;
73typedef struct lnm_queue lnm_queue;
74
75// General utilities
76unsigned long lnm_getus(void);
77unsigned long lnm_getms(void);
78int lnm_isstatement(lnmItem item);
79
80// Registry utilities
81void lnm_registry_update(void);
82
83// Memory utilities
84void lnm_free_item(lnmItem item);
85void lnm_free_registry(void);
86void lnm_free_queue(lnmQueue queue);
87
88// Debug utilities
89void lnm_debug_tabs(int count);
90void lnm_debug_parse_item(lnmItem item, int tabcount);
91void lnm_debug_parse_registry(void);
92void lnm_debug_parse_queue(lnmQueue queue);
93
94#endif // LNM_ALL, private declarations
95#endif // LOGNESTMONSTER_H, public declarations
96
97
98#ifdef LNM_INIT // one-time definitions
99
100
101// Pushable structure
102
103typedef struct lnm_pushable {
104 uint32_t capacity;
105 uint32_t length;
106 lnmItem * pushed;
107} lnm_pushable;
108
109void lnm_pushable_realloc(lnm_pushable * pushable) {
110 if (pushable->length > pushable->capacity) {
111 if (pushable->capacity * 2 <= pushable->capacity) {
112 printf("lognestmonster (lnm_pushable_realloc): pushable reached max length of 2^32-1. exiting...\n");
113 exit(1);
114 }
115 pushable->pushed = realloc(pushable->pushed, sizeof(lnmItem) * (pushable->capacity *= 2));
116 } else if (pushable->length < (pushable->capacity / 2)) {
117 if (pushable->capacity > 8) {
118 pushable->pushed = realloc(pushable->pushed, sizeof(lnmItem) * (pushable->capacity /= 2));
119 }
120 }
121}
122
123lnm_pushable * lnm_new_pushable(void) {
124 lnm_pushable * new_pushable = malloc(sizeof(lnm_pushable));
125 new_pushable->capacity = 8;
126 new_pushable->length = 0;
127 new_pushable->pushed = malloc(sizeof(lnmItem)*new_pushable->capacity);
128 return new_pushable;
129}
130
131void lnm_pushable_push(lnm_pushable * pushable, lnmItem item) {
132 pushable->length++;
133 lnm_pushable_realloc(pushable);
134 pushable->pushed[pushable->length-1] = item;
135}
136
137int lnm_pushable_indexof(lnm_pushable * pushable, lnmItem item) {
138 int len = pushable->length;
139 for (int iter = 0; iter<len; iter++) {
140 if (item == pushable->pushed[iter]) return iter;
141 }
142 printf("lognestmonster (lnm_pushable_indexof): cannot find item in pushable. exiting...\n");
143 exit(1);
144}
145
146void lnm_pushable_pop(lnm_pushable * pushable) {
147 pushable->length--;
148 lnm_pushable_realloc(pushable);
149}
150
151void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index) {
152 if (index>=pushable->length) {
153 printf("lognestmonster (lnm_pushable_remove): attempt to remove index out of pushable bounds. exiting...\n");
154 exit(1);
155 }
156 for (uint32_t iter = index; iter<pushable->length-1; iter++) {
157 pushable->pushed[iter] = pushable->pushed[iter+1];
158 }
159 pushable->length--;
160 lnm_pushable_realloc(pushable);
161}
162
163void lnm_pushable_free(lnm_pushable * pushable) {
164 free(pushable->pushed);
165 free(pushable);
166}
167
168
169// Statement and event structure definitions
170
171typedef struct lnm_log_event {
172 // word 1, 1 byte data 7 bytes padding
173 uint8_t type:1; // Used internally; 0 = statement, 1 = event
174 uint8_t boolpushed:1; // whether or not this log item has been pushed
175 uint8_t verbosity:3; // lnmVerbosityLevel, 0-5
176
177 // word 2, 8 bytes data
178 char * tag; // tag string
179
180 // word 3, 8 bytes data
181 lnm_pushable * pushed; // array of memory locations for lnm_log_event and lnm_log_statement structs
182} lnm_log_event;
183
184typedef struct lnm_log_statement {
185 // word 1, 4 bytes data 4 bytes padding
186 uint8_t type:1; // Used internally; 0 = statement, 1 = event
187 uint8_t boolpushed:1; // whether or not this log item has been pushed
188 uint8_t verbosity:3; // lnmVerbosityLevel, 0-5
189 uint8_t tag_size; // character length of the tag
190 uint16_t message_size; // character length of the message
191
192 // word 2, 8 bytes data
193 uint64_t timestamp; // 64-bit millisecond timestamp
194
195 // word 3, 8 bytes data
196 char * log; // tag string + message string
197} lnm_log_statement;
198
199
200// Queue structure definition
201
202typedef struct lnm_queue {
203 char * name;
204 char * out_path;
205 uint64_t timestamp;
206 lnm_pushable * pushed;
207} lnm_queue;
208
209
210// Library utilities
211
212unsigned long lnm_getus(void) {
213 struct timeval current_time;
214 gettimeofday(&current_time, NULL);
215 unsigned long ms = (current_time.tv_sec*1000000+current_time.tv_usec);
216 return ms;
217}
218
219unsigned long lnm_getms(void) {
220 return lnm_getus()/1000;
221}
222
223
224int lnm_isstatement(lnmItem item) {
225 lnm_log_statement * s = (lnm_log_statement *)item;
226 return !s->type;
227}
228
229
230// Item registry utils
231
232static lnm_pushable * lnm_registered_queues;
233static lnm_pushable * lnm_registered_items;
234static int lnm_registry_update_count;
235
236void lnm_registry_update(void) { // scan each registered item
237 for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) {
238 lnm_log_statement * s = (lnm_log_statement *)lnm_registered_items->pushed[iter];
239 if (s->boolpushed == 1) { // if the registered item has been pushed elsewhere, remove it from the top level of the registry
240 lnm_registry_update_count++;
241 lnm_pushable_remove(lnm_registered_items, iter);
242 iter--;
243 }
244 }
245}
246
247
248// Core library
249
250void lnm_free_item(lnmItem item) {
251 lnm_log_statement * item_cast = (lnm_log_statement *)item;
252 if (item_cast->boolpushed == 0) { // flush item out of registry
253 item_cast->boolpushed = 1;
254 lnm_registry_update();
255 }
256 if (item_cast->type == 0) {
257 free(item_cast->log);
258 free(item_cast);
259 } else if (item_cast->type == 1) {
260 lnm_log_event * event_cast = (lnm_log_event *)item_cast;
261
262 lnm_pushable * breadcrumb = lnm_new_pushable();
263 lnm_pushable_push(breadcrumb, (lnmItem)event_cast); // add event_cast as the first step down the breadcrumb
264 while (breadcrumb->length > 0) { // while there are items still in the breadcrumb
265 lnm_log_statement * breadcrumb_item = (lnm_log_statement *)breadcrumb->pushed[breadcrumb->length-1]; // fetch the last (deepest) item
266 if (breadcrumb_item->type == 0) { // the item is a statement
267 lnm_pushable_pop(breadcrumb); // remove it from the breadcrumb
268 free(breadcrumb_item->log); // and free it
269 free(breadcrumb_item);
270 } else if (breadcrumb_item->type == 1) {
271 lnm_log_event * breadcrumb_item_cast = (lnm_log_event *)breadcrumb_item; // item is an event, cast pointer
272 if (breadcrumb_item_cast->pushed->length > 0) { // if the event is not empty
273 lnm_pushable_push(breadcrumb, breadcrumb_item_cast->pushed->pushed[breadcrumb_item_cast->pushed->length-1]); // push the last item of event into the breadcrumb
274 lnm_pushable_pop(breadcrumb_item_cast->pushed); // and remove it from this event's index
275 // there is now a new breadcrumb navigation layer. loop back to check the new item...
276 } else {
277 lnm_pushable_pop(breadcrumb); // event is finally empty, remove it from the breadcrumb and free it
278 lnm_pushable_free(breadcrumb_item_cast->pushed);
279 free(breadcrumb_item_cast);
280 }
281 }
282 }
283 } else {
284 printf("lognestmonster (lnm_free_item): non-log item passed to function. exiting...\n");
285 exit(1);
286 }
287}
288
289void lnm_free_registry() {
290 for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) {
291 lnm_free_item(lnm_registered_items->pushed[iter]);
292 }
293}
294
295void lnm_free_queue(lnmQueue queue) {
296 lnm_queue * queue_cast = (lnm_queue *)queue;
297 for (uint32_t iter = 0; iter < queue_cast->pushed->length; iter++) {
298 lnm_free_item(queue_cast->pushed->pushed[iter]);
299 lnm_pushable_remove(queue_cast->pushed, iter--);
300 }
301}
302
303
304lnmQueue lnmQueueInit(char * name, char * out_path) {
305 if (lnm_registered_queues == NULL) {
306 lnm_registered_queues = lnm_new_pushable();
307 }
308 if (lnm_registered_items == NULL) {
309 lnm_registered_items = lnm_new_pushable();
310 }
311
312 lnm_queue * new_queue = malloc(sizeof(lnm_queue));
313 new_queue->name = malloc(strlen(name)+1);
314 new_queue->out_path = malloc(strlen(out_path)+1);
315 strcpy(new_queue->name, name);
316 strcpy(new_queue->out_path, out_path);
317 new_queue->timestamp = lnm_getus();
318 new_queue->pushed = lnm_new_pushable();
319
320 lnm_pushable_push(lnm_registered_queues, (lnmQueue)new_queue);
321 return (lnmQueue)new_queue;
322}
323
324lnmQueue lnmQueueByName(char * name) {
325 if (lnm_registered_queues == NULL) {
326 printf("lognestmonster (lnmQueueByName): queue registry is nonexistant. exiting...\n");
327 exit(1);
328 }
329 if (lnm_registered_queues->length == 0) {
330 printf("lognestmonster (lnmQueueByName): queue registry is empty. exiting...\n");
331 exit(1);
332 }
333 for (uint32_t iter = 0; iter<lnm_registered_queues->length; iter++) {
334 lnm_queue * iterqueue = (lnm_queue *)lnm_registered_queues->pushed[iter];
335 if (strcmp(iterqueue->name, name)==0) {
336 return (lnmQueue)iterqueue;
337 }
338 }
339 printf("lognestmonster (lnmQueueByName): queue not found in registry. exiting...\n");
340 exit(1);
341}
342
343void lnmQueuePush(lnmQueue queue, lnmItem item) {
344 if (((lnm_log_statement *)item)->boolpushed == 1) {
345 printf("lognestmonster (lnmQueuePush): attempt to push an already-pushed log item. exiting...\n");
346 exit(1);
347 }
348 lnm_pushable_push(((lnm_queue *)queue)->pushed, item);
349 ((lnm_log_statement *)item)->boolpushed = 1;
350 lnm_registry_update();
351}
352
353
354lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, char * tag, char * message) {
355 lnm_log_statement * new_statement = malloc(sizeof(lnm_log_statement));
356 new_statement->type = 0;
357 new_statement->verbosity = verbosity;
358 new_statement->timestamp = lnm_getus();
359 int tlen = strlen(tag);
360 if (tlen > 256 || tlen < 0) {
361 printf("lognestmonster (lnmStatement): tag length %i is longer than the cap 256 characters. exiting...\n", tlen);
362 exit(1);
363 }
364 int mlen = strlen(message);
365 if (mlen > 65536 || mlen < 0) {
366 printf("lognestmonster (lnmStatement): message length %i is longer than the cap 65536 characters. exiting...\n", mlen);
367 exit(1);
368 }
369 new_statement->tag_size = tlen;
370 new_statement->message_size = mlen;
371 new_statement->log = malloc(tlen+mlen+1);
372 strcpy(new_statement->log, tag);
373 strcat(new_statement->log, message);
374 lnm_registry_update();
375 lnm_pushable_push(lnm_registered_items, (lnmItem)new_statement);
376 return (lnmItem)new_statement;
377}
378
379
380lnmItem lnmEvent(void) {
381 lnm_log_event * new_event = malloc(sizeof(lnm_log_event));
382 new_event->type = 1;
383 new_event->pushed = lnm_new_pushable();
384 lnm_registry_update();
385 lnm_pushable_push(lnm_registered_items, (lnmItem)new_event);
386 return (lnmItem)new_event;
387}
388
389void lnmEventPush(lnmItem event, lnmItem item) {
390 if (event == item) {
391 printf("lognestmonster (lnmEventPush): attempt to push event to self. exiting...\n");
392 exit(1);
393 }
394 lnm_log_statement * item_cast = (lnm_log_statement *)item;
395 if (item_cast->boolpushed == 1) {
396 printf("lognestmonster (lnmEventPush): attempt to push an already-pushed log item. exiting...\n");
397 exit(1);
398 }
399 lnm_log_event * event_t = (lnm_log_event *)event;
400 if (event_t->type != 1) {
401 printf("lognestmonster (lnmEventPush): cannot cast non-event to event type. exiting...\n");
402 exit(1);
403 }
404 lnm_pushable_push(event_t->pushed, item);
405 item_cast->boolpushed = 1;
406 lnm_registry_update();
407}
408
409void lnmEventPushS(lnmItem event, uint8_t verbosity, char * tag, char * message) {
410 lnmItem statement = lnmStatement(verbosity, tag, message);
411 lnmEventPush(event, statement);
412}
413
414lnmItem lnmEventI(lnmItem item) {
415 lnmItem event = lnmEvent();
416 lnmEventPush(event, item);
417 return event;
418}
419
420lnmItem lnmEventS(uint8_t verbosity, char * tag, char * message) {
421 lnmItem statement = lnmStatement(verbosity, tag, message);
422 return lnmEventI(statement);
423}
424
425
426void lnm_debug_tabs(int count) {
427 for (int i = 0; i < count; i++) {
428 printf(" ");
429 }
430}
431
432void lnm_debug_parse_item(lnmItem item, int tabcount) {
433 if (lnm_isstatement(item)) {
434 lnm_log_statement * statement = (lnm_log_statement *) item;
435 lnm_debug_tabs(tabcount);
436
437 char * verbosity;
438 switch (statement->verbosity) {
439 case 0:
440 verbosity = "INFO";
441 break;
442 case 1:
443 verbosity = "DEBUG";
444 break;
445 case 2:
446 verbosity = "VERBOSE";
447 break;
448 case 3:
449 verbosity = "VERYVERBOSE";
450 break;
451 case 4:
452 verbosity = "WARNING";
453 break;
454 case 5:
455 verbosity = "ERROR";
456 break;
457 }
458
459 char tag[statement->tag_size+1];
460 strncpy(tag, statement->log, statement->tag_size);
461 tag[statement->tag_size] = '\0';
462
463 char message[statement->message_size+1];
464 strncpy(message, statement->log+statement->tag_size, statement->message_size);
465 message[statement->message_size] = '\0';
466
467 printf("%" PRIu64 " (%s) %s :: %s\n", statement->timestamp, verbosity, tag, message);
468 } else if (!lnm_isstatement(item)) {
469 lnm_log_event * event = (lnm_log_event *) item;
470 lnm_debug_tabs(tabcount);
471 printf("Event (%" PRIu32 ") [%" PRIu32 "] [\n", event->pushed->length, event->pushed->capacity);
472 for (uint32_t i = 0; i < event->pushed->length; i++) {
473 lnmItem item = event->pushed->pushed[i];
474 lnm_debug_parse_item(item, tabcount + 1);
475 }
476 lnm_debug_tabs(tabcount);
477 printf("]\n");
478 } else {
479 printf("lognestmonster (lnm_debug_parse_item): unknown item type. exiting...\n");
480 exit(1);
481 }
482}
483
484void lnm_debug_parse_registry(void) {
485 printf("Top level registry (%" PRIu32 ") [%" PRIu32 "] [\n", lnm_registered_items->length, lnm_registered_items->capacity);
486 for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) {
487 lnm_debug_parse_item(lnm_registered_items->pushed[iter], 1);
488 }
489 printf("]\n");
490}
491
492void lnm_debug_parse_queue(lnmQueue queue) {
493 lnm_queue * queue_cast = (lnm_queue *)queue;
494 printf("Queue \"%s\" at %s (%" PRIu32 ") [%" PRIu32 "] [\n", queue_cast->name, queue_cast->out_path, queue_cast->pushed->length, queue_cast->pushed->capacity);
495 for (uint32_t iter = 0; iter < queue_cast->pushed->length; iter++) {
496 lnm_debug_parse_item((lnmItem)queue_cast->pushed->pushed[iter], 1);
497 }
498 printf("]\n");
499}
500#endif // DEFINE_LOGNESTMONSTER
501
502#ifdef __cplusplus // Linker protection
503}
504#endif
505