Index

lognestmonster / 33010ba

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
16412 Dec 2019 21:29fd2d2c3Use lnmVerbosityLevel as type instead of uint8_tJosh Stockin122N

Blob @ lognestmonster / src / c / lognestmonster.h

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