Index

lognestmonster / 955bc8d

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
16729 Dec 2019 19:58955bc8dChange license from GPLv3 to MITJosh Stockin1419N

Blob @ lognestmonster / src / c / lognestmonster.h

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