Index

lognestmonster / a46b7e5

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
16008 Dec 2019 13:02a46b7e5Update Queue utilitiesJosh Stockin13014N

Blob @ lognestmonster / src / c / lognestmonster.h

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