Index

lognestmonster / 89785fb

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
15026 Nov 2019 18:1289785fbUpdate string format typing for cross-arch usageJosh Stockin165N

Blob @ lognestmonster / src / c / lognestmonster.h

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