Index

lognestmonster / 616a224

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
20130 Mar 2020 19:17824476cSimplify for loop to while loopJosh Stockin163G

Blob @ lognestmonster / src / c / lognestmonster.h

text/plain18833 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 and distributed under the terms of the MIT License:
6 * ----- BEGIN LICENSE -----
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 * ----- END LICENSE -----
25 *
26 * This comment block and its contents, including this disclaimer, MUST be
27 * preserved in all copies or distributions of this software's source.
28 */
29
30// lognestmonster.h
31// C header file for implementation of the lognestmonster logging library
32
33
34#ifndef LOGNESTMONSTER_H
35#define LOGNESTMONSTER_H 1
36
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42
43#include <stdint.h> // necessary include for type declaration
44
45
46enum lnmVerbosityLevel {lnmInfo, lnmDebug, lnmVerbose, lnmVeryVerbose, lnmWarning, lnmError};
47typedef uint8_t * lnmItem;
48typedef uint8_t * lnmQueue;
49
50
51lnmQueue lnmQueueInit(const char * name, const char * out_path);
52lnmQueue lnmQueueByName(const char * name);
53
54lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, const char * message);
55
56lnmItem lnmEvent(const char * tag);
57lnmItem lnmEventI(const char * tag, lnmItem item);
58lnmItem lnmEventS(const char * tag, enum lnmVerbosityLevel verbosity, const char * message);
59void lnmEventPush(lnmItem event, lnmItem item);
60void lnmEventPushS(lnmItem event, enum lnmVerbosityLevel verbosity, const char * message);
61
62
63#ifdef LNM_ALL // expose private utilities
64
65// type definitions
66typedef struct lnm_pushable lnm_pushable;
67typedef struct lnm_log_event lnm_log_event;
68typedef struct lnm_log_statement lnm_log_statement;
69typedef struct lnm_queue lnm_queue;
70
71// lnm_pushable utilities
72lnm_pushable * lnm_new_pushable(void);
73void lnm_pushable_realloc(lnm_pushable * pushable);
74void lnm_pushable_push(lnm_pushable * pushable, lnmItem item);
75void lnm_pushable_pop(lnm_pushable * pushable);
76void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index);
77void lnm_pushable_free(lnm_pushable * pushable);
78
79// misc utilities
80_Noreturn void lnm_abort(const char * function_traceback, const char * message);
81unsigned long lnm_getus(void);
82
83// registry utilities
84void lnm_registry_update(void);
85
86// memory management utilities
87void lnm_free_item(lnmItem item);
88void lnm_free_registry(void);
89void lnm_free_queue(lnmQueue queue);
90
91#endif // LNM_ALL
92
93
94#if defined(LNM_DEBUG) || defined(LNM_ALL)
95void lnm_debug_tabs(int tab_count);
96void lnm_debug_parse_item(lnmItem item, int tab_count);
97void lnm_debug_parse_registry(void);
98void lnm_debug_parse_queue(lnmQueue queue);
99#endif // LNM_DEBUG || LNM_ALL
100
101
102#ifdef LNM_INIT // define the library
103
104
105#include <stdint.h>
106#include <stdio.h>
107#include <stdlib.h>
108#include <string.h>
109
110
111enum LNM_ITEM_TYPE {
112 LNM_STATEMENT = 0,
113 LNM_EVENT = 1,
114};
115
116
117_Noreturn void lnm_abort(const char * function_traceback, const char * message) {
118 printf("lognestmonster (%s): %s. aborting...\n", function_traceback, message);
119 abort();
120}
121
122
123// lnm_pushable utilities
124
125
126typedef struct lnm_pushable {
127 uint32_t capacity;
128 uint32_t length;
129 lnmItem * frame;
130} lnm_pushable;
131
132
133void lnm_pushable_realloc(lnm_pushable * pushable) {
134 if (pushable->length > pushable->capacity) {
135 if (pushable->capacity > UINT32_MAX / 2) {
136 lnm_abort("lnm_pushable_realloc", "pushable can't surpass max capacity of 2^16");
137 }
138 pushable->frame = realloc(pushable->frame, sizeof(lnmItem) * (pushable->capacity *= 2));
139 if (pushable->frame == NULL) {
140 lnm_abort("lnm_pushable_realloc", "call to realloc() returned NULL");
141 }
142 } else if (pushable->length < (pushable->capacity / 2)) {
143 while (pushable->length < (pushable->capacity / 2) && pushable->capacity > 8) {
144 pushable->capacity /= 2;
145 }
146 pushable->frame = realloc(pushable->frame, sizeof(lnmItem) * (pushable->capacity));
147 if (pushable->frame == NULL) {
148 lnm_abort("lnm_pushable_realloc", "call to realloc() returned NULL");
149 }
150 }
151}
152
153
154lnm_pushable * lnm_new_pushable(void) {
155 lnm_pushable * new_pushable = calloc(1, sizeof(lnm_pushable));
156 if (new_pushable == NULL) {
157 lnm_abort("lnm_new_pushable", "call to calloc() returned NULL");
158 }
159 new_pushable->capacity = 8;
160 new_pushable->length = 0;
161 new_pushable->frame = calloc(8, sizeof(lnmItem));
162 if (new_pushable->frame == NULL) {
163 lnm_abort("lnm_new_pushable", "call to calloc() returned NULL");
164 }
165 return new_pushable;
166}
167
168
169void lnm_pushable_push(lnm_pushable * pushable, lnmItem item) {
170 pushable->length++;
171 lnm_pushable_realloc(pushable);
172 pushable->frame[pushable->length - 1] = item;
173}
174
175
176void lnm_pushable_pop(lnm_pushable * pushable) {
177 pushable->length--;
178 lnm_pushable_realloc(pushable);
179}
180
181
182void lnm_pushable_remove(lnm_pushable * pushable, uint32_t index) {
183 if (index >= pushable->length) {
184 lnm_abort("lnm_pushable_remove", "attempt to remove index out of pushable bounds");
185 }
186 if (index == pushable->length - 1) {
187 lnm_pushable_pop(pushable);
188 } else {
189 // shift entire array from index until end
190 for (uint32_t iter = index; iter < pushable->length - 1; iter++) {
191 pushable->frame[iter] = pushable->frame[iter + 1];
192 }
193 pushable->length--;
194 lnm_pushable_realloc(pushable);
195 }
196}
197
198
199void lnm_pushable_free(lnm_pushable * pushable) {
200 free(pushable->frame);
201 free(pushable);
202}
203
204
205// log event and log statement definitions
206
207
208typedef struct lnm_log_event {
209 // word 1, 1 byte data 7 bytes padding
210 uint8_t type:1; // used internally; 0 = statement, 1 = event
211 uint8_t pushed:1; // whether or not this log item has been pushed
212
213 // word 2, 8 bytes data
214 char * tag; // null-terminated tag string
215
216 // word 3, 8 bytes data
217 lnm_pushable * pushable; // pushable of lnmItems
218} lnm_log_event;
219
220
221typedef struct lnm_log_statement {
222 // word 1, 1 byte data 7 bytes padding
223 uint8_t type:1; // used internally; 0 = statement, 1 = event
224 uint8_t pushed:1; // whether or not this log item has been pushed
225 uint8_t verbosity:3; // lnmVerbosityLevel, 0-5
226
227 // word 2, 8 bytes data
228 uint64_t timestamp; // 64-bit millisecond timestamp
229
230 // word 3, 8 bytes data
231 char * log; // null-terminated message string
232} lnm_log_statement;
233
234
235// queue structure definition
236
237
238typedef struct lnm_queue {
239 char * name;
240 char * out_path;
241 uint64_t timestamp;
242 lnm_pushable * pushable;
243} lnm_queue;
244
245
246// time utilities
247
248
249#if defined(__unix__) || defined(unix) || defined(__unix) || defined(__CYGWIN__)
250#include <sys/time.h>
251uint64_t lnm_getus(void) {
252 uint64_t us;
253 struct timeval lnm_current_time;
254 gettimeofday(&lnm_current_time, NULL);
255 us = (lnm_current_time.tv_sec*1000000+lnm_current_time.tv_usec);
256 return us;
257}
258#elif defined(_WIN32) || defined(__WINDOWS__)
259#include <windows.h>
260#include <sysinfoapi.h>
261uint64_t lnm_getus(void) {
262 uint64_t us;
263 // get system time in ticks
264 FILETIME lnm_win32_filetime;
265 GetSystemTimeAsFileTime(&lnm_win32_filetime);
266 // load time from two 32-bit words into one 64-bit integer
267 us = lnm_win32_filetime.dwHighDateTime;
268 us = us << 32;
269 us |= lnm_win32_filetime.dwLowDateTime;
270 // convert to microseconds
271 us /= 10;
272 // convert from time since Windows NT epoch to time since Unix epoch
273 us -= 11644473600000000ULL;
274 return us;
275}
276#else
277#error lognestmonster: Neither Windows NT nor a POSIX-compliant system were detected.\
278 Implement your own system time functions or compile on a compliant system.
279#endif
280
281
282// item registry utils
283
284
285void lnm_free_item(lnmItem item);
286static lnm_pushable * lnm_registered_queues;
287static lnm_pushable * lnm_registered_items;
288
289
290void lnm_registry_update(void) {
291 // iterate through registry
292 for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) {
293 lnm_log_statement * item = (lnm_log_statement *)(lnm_registered_items->frame[iter]);
294 // if the registered item has been pushed elsewhere, remove it from the top level of the registry
295 if (item->pushed) {
296 lnm_pushable_remove(lnm_registered_items, iter);
297 iter--;
298 }
299 }
300}
301
302
303void lnm_registry_push(lnmItem item) {
304 if (lnm_registered_items == NULL) {
305 lnm_registered_items = lnm_new_pushable();
306 }
307 lnm_pushable_push(lnm_registered_items, item);
308}
309
310
311void lnm_registry_free() {
312 lnm_registry_update();
313 while (lnm_registered_items->length > 0) {
314 // free last item
315 // this allows lnm_pushable_free to pop instead of shuffling indices
316 lnm_free_item(lnm_registered_items->frame[lnm_registered_items->length-1]);
317 }
318 lnm_pushable_realloc(lnm_registered_items);
319}
320
321
322void lnm_registry_flush_item(lnmItem item) {
323 lnm_log_statement * item_cast = (lnm_log_statement *)item;
324 if (!item_cast->pushed) {
325 item_cast->pushed = 1;
326 lnm_registry_update();
327 }
328}
329
330
331// core library utilities
332
333
334int lnm_item_type(lnmItem item) {
335 return ((lnm_log_statement *)item)->type;
336}
337
338void lnm_free_item(lnmItem item) {
339 if (lnm_item_type(item) == LNM_STATEMENT) {
340 // cast item
341 lnm_log_statement * statement = (lnm_log_statement *)item;
342 // flush item out of registry
343 lnm_registry_flush_item(item);
344 // free item and its contents
345 free(statement->log);
346 free(statement);
347 } else if (lnm_item_type(item) == LNM_EVENT) {
348 // create breadcrumb navigation array with root 'item'
349 lnm_pushable * breadcrumb = lnm_new_pushable();
350 lnm_pushable_push(breadcrumb, item);
351 // continually iterate breadcrumb until it's empty
352 while (breadcrumb->length > 0) {
353 // get current item (deepest element of the breadcrumb nav, aka 'z' in 'x > y > z')
354 lnmItem current = breadcrumb->frame[breadcrumb->length - 1];
355 // pop it from the breadcrumb nav
356 lnm_pushable_pop(breadcrumb);
357 // flush item out of registry
358 lnm_registry_flush_item(current);
359 if (lnm_item_type(current) == LNM_STATEMENT) {
360 // cast current item
361 lnm_log_statement * current_statement = (lnm_log_statement *)current;
362 // free statement and its contents
363 free(current_statement->log);
364 free(current_statement);
365 continue;
366 } else if (lnm_item_type(current) == LNM_EVENT) {
367 // cast current item
368 lnm_log_event * current_event = (lnm_log_event *)current;
369 if (current_event->pushable->length > 0) {
370 // the event has children, add them to the breadcrumb
371 for (uint32_t iter = 0; iter < current_event->pushable->length; iter++) {
372 lnmItem current_event_child = current_event->pushable->frame[iter];
373 lnm_pushable_push(breadcrumb, current_event_child);
374 }
375 }
376 // free original event
377 lnm_pushable_free(current_event->pushable);
378 free(current_event->tag);
379 free(current_event);
380 continue;
381 } else {
382 lnm_abort("lnm_free_item", "item in log tree has non-statement and non-event type");
383 }
384 }
385 lnm_pushable_free(breadcrumb);
386 } else {
387 lnm_abort("lnm_free_item", "log tree is non-statement and non-event type");
388 }
389}
390
391
392// queue utilities
393
394
395void lnm_free_queue(lnmQueue queue) {
396 lnm_queue * queue_cast = (lnm_queue *)queue;
397 for (uint32_t iter = 0; iter < queue_cast->pushable->length; iter++) {
398 lnm_free_item(queue_cast->pushable->frame[iter]);
399 lnm_pushable_remove(queue_cast->pushable, iter);
400 iter--;
401 }
402}
403
404
405lnmQueue lnmQueueInit(const char * name, const char * out_path) {
406 // create queue and item registries if not created
407 if (lnm_registered_queues == NULL) {
408 lnm_registered_queues = lnm_new_pushable();
409 }
410 if (lnm_registered_items == NULL) {
411 lnm_registered_items = lnm_new_pushable();
412 }
413 // allocate and populate a new Queue object
414 lnm_queue * new_queue = calloc(1, sizeof(lnm_queue));
415 if (new_queue == NULL) {
416 lnm_abort("lnmQueueInit", "call to calloc() returned NULL");
417 }
418 new_queue->name = malloc(strlen(name)+1);
419 new_queue->out_path = malloc(strlen(out_path)+1);
420 if (new_queue->name == NULL || new_queue->out_path == NULL) {
421 lnm_abort("lnmQueueInit", "call to malloc() returned NULL");
422 }
423 strcpy(new_queue->name, name);
424 strcpy(new_queue->out_path, out_path);
425 new_queue->timestamp = lnm_getus();
426 new_queue->pushable = lnm_new_pushable();
427 // enter new Queue into registry
428 lnm_pushable_push(lnm_registered_queues, (lnmQueue)new_queue);
429 return (lnmQueue)new_queue;
430}
431
432
433lnmQueue lnmQueueByName(const char * name) {
434 if (lnm_registered_queues == NULL) {
435 lnm_abort("lnmQueueByName", "queue registry is nonexistent");
436 }
437 if (lnm_registered_queues->length == 0) {
438 lnm_abort("lnmQueueByName", "queue registry is empty");
439 }
440 for (uint32_t iter = 0; iter < lnm_registered_queues->length; iter++) {
441 lnm_queue * queue = (lnm_queue *)lnm_registered_queues->frame[iter];
442 if (strcmp(queue->name, name) == 0) {
443 return (lnmQueue)queue;
444 }
445 }
446 lnm_abort("lnmQueueByName", "queue not found in registry");
447}
448
449
450void lnmQueuePush(lnmQueue queue, lnmItem item) {
451 if (queue == NULL || item == NULL) {
452 lnm_abort("lnmQueuePush", "cannot perform operation on NULL arguments");
453 }
454 lnm_log_statement * statement = (lnm_log_statement *)item;
455 if (statement->pushed == 1) {
456 lnm_abort("lnmQueuePush", "attempt to push an already-pushed log item");
457 }
458 // flush out of registry
459 lnm_registry_flush_item(item);
460 // add to queue
461 lnm_pushable_push(((lnm_queue *)queue)->pushable, item);
462}
463
464
465lnmItem lnmStatement(enum lnmVerbosityLevel verbosity, const char * message) {
466 if (message == NULL) {
467 lnm_abort("lnmStatement", "cannot perform operation on NULL argument");
468 }
469 lnm_log_statement * new_statement = calloc(1, sizeof(lnm_log_statement));
470 if (new_statement == NULL) {
471 lnm_abort("lnmStatement", "call to calloc() returned NULL");
472 }
473 new_statement->type = LNM_STATEMENT;
474 new_statement->pushed = 0;
475 new_statement->verbosity = verbosity;
476 new_statement->timestamp = lnm_getus();
477 // enforce string lengths
478 int message_len = strlen(message) + 1;
479 // copy message to new_statement->log
480 new_statement->log = malloc(message_len);
481 if (new_statement->log == NULL) {
482 lnm_abort("lnmStatement", "call to malloc() returned NULL");
483 }
484 strcpy(new_statement->log, message);
485 // add to registry
486 lnm_registry_push((lnmItem)new_statement);
487 return (lnmItem)new_statement;
488}
489
490
491lnmItem lnmEvent(const char * tag) {
492 if (tag == NULL) {
493 lnm_abort("lnmEvent", "cannot perform operation on NULL argument");
494 }
495 lnm_log_event * new_event = calloc(1, sizeof(lnm_log_event));
496 if (new_event == NULL) {
497 lnm_abort("lnmEvent", "call to calloc() returned NULL");
498 }
499 new_event->type = LNM_EVENT;
500 new_event->pushed = 0;
501 new_event->pushable = lnm_new_pushable();
502 // copy tag to event
503 int tag_len = strlen(tag) + 1;
504 new_event->tag = malloc(tag_len);
505 if (new_event->tag == NULL) {
506 lnm_abort("lnmEvent", "call to malloc() returned NULL");
507 }
508 strcpy(new_event->tag, tag);
509 // add to registry
510 lnm_registry_push((lnmItem)new_event);
511 return (lnmItem)new_event;
512}
513
514
515void lnmEventPush(lnmItem event, lnmItem item) {
516 if (event == NULL || item == NULL) {
517 lnm_abort("lnmEventPush", "cannot perform operation on NULL arguments");
518 }
519 if (event == item) {
520 lnm_abort("lnmEventPush", "attempt to push event to self");
521 }
522 lnm_log_statement * item_cast = (lnm_log_statement *)item;
523 if (item_cast->pushed == 1) {
524 lnm_abort("lnmEventPush", "attempt to push an already-pushed log item");
525 }
526 if (lnm_item_type(event) != LNM_EVENT) {
527 lnm_abort("lnmEventPush", "cannot cast non-event to event type");
528 }
529 lnm_log_event * event_cast = (lnm_log_event *)event;
530 lnm_pushable_push(event_cast->pushable, item);
531 lnm_registry_flush_item(item);
532}
533
534
535void lnmEventPushS(lnmItem event, enum lnmVerbosityLevel verbosity, const char * message) {
536 lnmItem statement = lnmStatement(verbosity, message);
537 lnmEventPush(event, statement);
538}
539
540
541lnmItem lnmEventI(const char * tag, lnmItem item) {
542 lnmItem event = lnmEvent(tag);
543 lnmEventPush(event, item);
544 return event;
545}
546
547
548lnmItem lnmEventS(const char * tag, enum lnmVerbosityLevel verbosity, const char * message) {
549 lnmItem event = lnmEvent(tag);
550 lnmEventPushS(event, verbosity, message);
551 return event;
552}
553
554
555#ifdef LNM_DEBUG
556#include <inttypes.h>
557
558
559void lnm_debug_tabs(int tab_count) {
560 for (int i = 0; i < tab_count; i++) {
561 printf(" ");
562 }
563}
564
565
566void lnm_debug_parse_item(lnmItem item, int tab_count) {
567 if (lnm_item_type(item) == LNM_STATEMENT) {
568 lnm_log_statement * statement = (lnm_log_statement *) item;
569 lnm_debug_tabs(tab_count);
570 char * verbosity;
571 switch (statement->verbosity) {
572 case 0:
573 verbosity = "INFO";
574 break;
575 case 1:
576 verbosity = "DEBUG";
577 break;
578 case 2:
579 verbosity = "VERBOSE";
580 break;
581 case 3:
582 verbosity = "VERYVERBOSE";
583 break;
584 case 4:
585 verbosity = "WARNING";
586 break;
587 case 5:
588 verbosity = "ERROR";
589 break;
590 }
591 printf("%" PRIu64 " (%s) :: %s\n", statement->timestamp, verbosity, statement->log);
592 } else if (lnm_item_type(item) == LNM_EVENT) {
593 lnm_log_event * event = (lnm_log_event *) item;
594 lnm_debug_tabs(tab_count);
595 printf("Event (%" PRIu32 "/%" PRIu32 ") %s [\n", event->pushable->length, event->pushable->capacity, event->tag);
596 for (uint32_t iter = 0; iter < event->pushable->length; iter++) {
597 lnmItem item = event->pushable->frame[iter];
598 lnm_debug_parse_item(item, tab_count + 1);
599 }
600 lnm_debug_tabs(tab_count);
601 printf("]\n");
602 } else {
603 lnm_abort("lnm_debug_parse_item", "unknown item type");
604 }
605}
606
607
608void lnm_debug_parse_registry(void) {
609 printf("Top level registry (%" PRIu32 "/%" PRIu32 ") [\n", lnm_registered_items->length, lnm_registered_items->capacity);
610 for (uint32_t iter = 0; iter < lnm_registered_items->length; iter++) {
611 lnm_debug_parse_item(lnm_registered_items->frame[iter], 1);
612 }
613 printf("]\n");
614}
615
616
617void lnm_debug_parse_queue(lnmQueue queue) {
618 lnm_queue * queue_cast = (lnm_queue *)queue;
619 printf("Queue \"%s\" at %s (%" PRIu32 "/%" PRIu32 ") [\n", queue_cast->name, queue_cast->out_path, queue_cast->pushable->length, queue_cast->pushable->capacity);
620 for (uint32_t iter = 0; iter < queue_cast->pushable->length; iter++) {
621 lnm_debug_parse_item((lnmItem)queue_cast->pushable->frame[iter], 1);
622 }
623 printf("]\n");
624}
625
626
627#endif // LNM_DEBUG
628#endif // LNM_INIT
629
630
631#ifdef __cplusplus
632}
633#endif
634
635
636#endif // LOGNESTMONSTER_H
637