Index

lognestmonster / 6dd952c

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
16108 Dec 2019 15:34bf45c1bAdd linker protection to maintain C-style function namesJosh Stockin180N

Blob @ lognestmonster / src / c / lognestmonster.h

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