Index

lognestmonster / ab5e2b9

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
14926 Nov 2019 17:21ab5e2b9Update lnm_free_item implementation to prevent recursionJosh Stockin12511N

Blob @ lognestmonster / src / c / lognestmonster.h

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