Index

lognestmonster / 4b380ef

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
16903 Jan 2020 21:44c564ba1Update MIT License noticesJosh Stockin121N

Blob @ lognestmonster / src / c / lognestmonster.h

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