Index

lognestmonster / d727ad6

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
15127 Nov 2019 23:41d727ad6Include stdio.h for printf callsJosh Stockin110N

Blob @ lognestmonster / src / c / lognestmonster.h

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