Index

lognestmonster / b7d72fe

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
17526 Jan 2020 17:27cd3d5c4Update license and copyright comment block in headerJosh Stockin13117N

Blob @ lognestmonster / src / c / lognestmonster.h

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