Index

lognestmonster / 19782d4

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
15429 Nov 2019 22:4819782d4Use new lnm_pushable_pop functionJosh Stockin195N

Blob @ lognestmonster / src / c / lognestmonster.h

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