fix bug in handling of FLOG_MSG_T.src_line (changed from signed to unsigned)
[flog.git] / flog.c
blobbfd1c93b17105d409ff2ca309c199a10a2ff254a
1 //! Flog - The F logging library
3 //! @file flog.c
4 //! @author Nabeel Sowan (nabeel.sowan@vibes.se)
5 //!
6 //! Useful as the main logger of a program or embedded system.
7 //! Requires C99 + GNU support.
9 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdarg.h>
15 #include "flog.h"
18 //! initialise a FLOG_MSG_T to defaults
20 //! internal use only, or when extending flog
21 void init_flog_msg_t(FLOG_MSG_T *p)
23 memset(p,0,sizeof(FLOG_MSG_T));
24 #ifdef FLOG_CONFIG_TIMESTAMP
25 //p->time=0;
26 #endif
27 #ifdef FLOG_CONFIG_SRC_INFO
28 //p->src_file=NULL;
29 //p->src_line=0;
30 //p->src_func=NULL;
31 #endif
32 //p->subsystem=NULL;
33 //p->type=FLOG_NONE;
34 //p->msg_id=0;
35 //p->text=NULL;
39 //! create and return a FLOG_MSG_T type
41 //! internal use only, or when creating flog output function
42 //! @retval NULL error
43 FLOG_MSG_T * create_flog_msg_t(const char *subsystem,
44 #ifdef FLOG_CONFIG_TIMESTAMP
45 FLOG_TIMESTAMP_T timestamp,
46 #endif
47 #ifdef FLOG_CONFIG_SRC_INFO
48 const char *src_file,uint_fast16_t src_line,const char *src_func,
49 #endif
50 FLOG_MSG_TYPE_T type,FLOG_MSG_ID_T msg_id,const char *text)
52 FLOG_MSG_T *p;
53 if((p=malloc(sizeof(FLOG_MSG_T)))!=NULL) {
54 init_flog_msg_t(p);
55 p->type=type;
56 if(subsystem && subsystem[0]) {
57 if((p->subsystem=strdup(subsystem))==NULL) {
58 destroy_flog_msg_t(p);
59 return(NULL);
62 #ifdef FLOG_CONFIG_TIMESTAMP
63 p->timestamp=timestamp;
64 #endif
65 #ifdef FLOG_CONFIG_SRC_INFO
66 if(src_file && src_file[0]) {
67 if((p->src_file=strdup(src_file))==NULL) {
68 destroy_flog_msg_t(p);
69 return(NULL);
72 p->src_line=src_line;
73 if(src_func && src_func[0]) {
74 if((p->src_func=strdup(src_func))==NULL) {
75 destroy_flog_msg_t(p);
76 return(NULL);
79 #endif
80 p->msg_id=msg_id;
81 if(text!=NULL) {
82 if((p->text=strdup(text))==NULL) {
83 destroy_flog_msg_t(p);
84 return(NULL);
88 return(p);
92 //! free a FLOG_MSG_T
94 //! internal use only, or when creating flog output function
95 void destroy_flog_msg_t(FLOG_MSG_T *p)
97 if(p) {
98 free(p->subsystem);
99 #ifdef FLOG_CONFIG_SRC_INFO
100 free(p->src_file);
101 free(p->src_func);
102 #endif
103 free(p->text);
104 free(p);
105 p=NULL;
110 //! initialise a FLOG_T to defaults
112 //! mainly internal use, or when extending flog
113 void init_flog_t(FLOG_T *p)
115 memset(p,0,sizeof(FLOG_T));
116 //p->name=NULL;
117 p->accepted_msg_type=FLOG_ACCEPT_ALL;
118 //p->output_func=NULL;
119 //p->output_func_data=NULL;
120 //p->output_error=0;
121 p->output_stop_on_error=1;
122 //p->error_log=NULL;
123 //p->msg=NULL;
124 //p->msg_amount=0;
125 //p->msg_max=0;
126 //p->sublog=NULL;
127 //p->sublog_amount=0;
131 //! create and return a FLOG_T type
133 //! @retval NULL error
134 FLOG_T * create_flog_t(const char *name, FLOG_MSG_TYPE_T accepted_msg_type)
136 FLOG_T *p;
137 if((p=malloc(sizeof(FLOG_T)))!=NULL) {
138 init_flog_t(p);
139 p->accepted_msg_type=accepted_msg_type;
140 if(name && name[0]) {
141 if((p->name=strdup(name))==NULL) {
142 destroy_flog_t(p);
143 return(NULL);
147 return(p);
151 //! free a FLOG_T
152 void destroy_flog_t(FLOG_T *p)
154 if(p) {
155 free(p->name);
156 if(p->msg) {
157 uint_fast16_t i;
158 for(i=0;i<p->msg_amount;i++)
159 destroy_flog_msg_t(p->msg[i]);
160 free(p->msg);
162 free(p->sublog); //! Note that sublogs are not freed
163 free(p);
164 p=NULL;
169 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
170 int stack_depth;
171 #endif
174 //! add a FLOG_MSG_T to FLOG_T and do all required logic (used by flog_print[f] functions)
176 //! internal use only, or when extending flog
177 //! @param[in,out] *p target log
178 //! @param[in] *msg message to add
179 //! @retval 0 success
180 int flog_add_msg(FLOG_T *p,FLOG_MSG_T *msg)
182 //compare if accepted message type
183 if(!(msg->type & p->accepted_msg_type))
184 return(0);
186 //copy the input msg into a FLOG_MSG_T struct
187 FLOG_MSG_T outmsg;
188 outmsg=*msg;
190 //append name to subsystem
191 int free_subsystem=0;
192 if(p->name) {
193 if(outmsg.subsystem) {
194 char *tmpstr;
195 if(asprintf(&tmpstr,"%s/%s",p->name,outmsg.subsystem)!=-1) { //We don't care if we can't allocate memory
196 outmsg.subsystem = tmpstr;
197 free_subsystem=1;
199 } else {
200 outmsg.subsystem=p->name;
204 //! @todo add message to buffer
206 if(p->msg_amount<p->msg_max) {
207 FLOG_MSG_T **new_msg;
208 if((new_msg=realloc(p->msg,(p->msg_amount+1)*sizeof(FLOG_MSG_T *)))!=NULL) {
209 p->msg=new_msg;
210 p->msg[p->msg_amount]=msg;
211 p->msg_amount++;
215 //! @todo invent a suitable error output strategy
216 int e=0;
218 //run output function
219 if(p->output_func) {
220 if(p->output_stop_on_error ? !p->output_error : 1) {
221 if((e=p->output_func(p,&outmsg)))
222 p->output_error=e;
226 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
227 if(stack_depth+1 < FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH) {
228 stack_depth++;
229 #endif
230 //add message to sublogs
231 uint_fast8_t i;
232 for(i=0;i<p->sublog_amount;i++)
233 e+=flog_add_msg(p->sublog[i],&outmsg);
234 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
235 stack_depth--;
237 #endif
239 //if we allocated a string, free it
240 if(free_subsystem)
241 free(outmsg.subsystem);
243 return(e);
247 //! clear all messages stored in log
248 void flog_clear_msg_buffer(FLOG_T *p)
250 if(p && p->msg) {
251 uint_fast16_t i;
252 for(i=0;i<p->msg_amount;i++)
253 destroy_flog_msg_t(p->msg[i]);
254 free(p->msg);
255 p->msg=NULL;
256 p->msg_amount=0;
261 //! add a sublog to a log
263 //! @param[in,out] *p target log
264 //! @param[in] *sublog log to add
265 //! @retval 0 success
266 int flog_append_sublog(FLOG_T *p,FLOG_T *sublog)
268 if(!p)
269 return(1);
270 if(p==sublog) {
271 flog_print(p->error_log,NULL,FLOG_ERROR,0,"cannot append log to itself (causes circular dependency)");
272 return(1);
274 FLOG_T **new_sublog;
275 if((new_sublog=realloc(p->sublog,(p->sublog_amount+1)*sizeof(FLOG_T *)))==NULL)
276 return(1);
277 p->sublog=new_sublog;
278 p->sublog[p->sublog_amount]=sublog;
279 p->sublog_amount++;
280 return(0);
284 //! Is the message used in any way if put in this log?
286 //! This function can be used to decide whether or not to drop a message immediately
287 //! @param[in] *p the log receiving the message
288 //! @param[in] type the type from message
289 //! @retval 0 Message is never used
290 //! @retval 1 Message is used
291 int flog_is_message_used(FLOG_T *p,FLOG_MSG_TYPE_T type)
293 if(type & p->accepted_msg_type) {
294 if(p->msg_amount<p->msg_max)
295 return(1);
296 if(p->output_func) {
297 if(p->output_stop_on_error ? !p->output_error : 1)
298 return(1);
300 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
301 if(stack_depth+1 < FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH) {
302 stack_depth++;
303 #endif
304 uint_fast8_t i;
305 for(i=0;i<p->sublog_amount;i++) {
306 if(flog_is_message_used(p->sublog[i],type)) {
307 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
308 stack_depth--;
309 #endif
310 return(1);
313 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
314 stack_depth--;
316 #endif
318 return(0);
322 //! do not call directly, use the flog_print() macro instead
324 //! emit an flog message
325 //! @param[in,out] *p log to emit message to
326 //! @param[in] *subsystem which part of the program is outputing this message
327 //! @param[in] *src_file source code file (flog_print() macro uses __FILE__ to fill this in)
328 //! @param[in] src_line source code line (flog_print() macro uses __LINE__ to fill this in)
329 //! @param[in] *src_func source code function (flog_print() macro uses __FUNCTION__ to fill this in)
330 //! @param[in] type use one of the FLOG_* defines
331 //! @param[in] msg_id optionally use errno or one of the FLOG_MSG_* defines
332 //! @param[in] *text message text
333 //! @retval 0 success
334 //! @retval 1 error while adding message to log
335 //! @retval 2 error unable to get time
336 //! @retval 3 did not add null message (flog is configured not to allow null messages)
337 //! @see flog_print()
338 int _flog_print(FLOG_T *p,const char *subsystem,
339 #ifdef FLOG_CONFIG_SRC_INFO
340 const char *src_file,uint_fast16_t src_line,const char *src_func,
341 #endif
342 FLOG_MSG_TYPE_T type,FLOG_MSG_ID_T msg_id,const char *text)
344 if(!p)
345 return(1);
346 //Only add message if it will be used
347 if(!flog_is_message_used(p,type))
348 return(0);
350 //Convert the input into a FLOG_MSG_T struct
351 FLOG_MSG_T msg;
352 init_flog_msg_t(&msg);
353 msg.msg_id = msg_id;
354 if(text && text[0])
355 msg.text = text;
356 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
357 if(!msg.msg_id && !msg.text)
358 return(3);
359 #endif //FLOG_CONFIG_ALLOW_NULL_MESSAGES
360 if(subsystem && subsystem[0])
361 msg.subsystem = subsystem;
362 #ifdef FLOG_CONFIG_TIMESTAMP
363 #ifdef FLOG_CONFIG_TIMESTAMP_USEC
364 if(gettimeofday(&msg.timestamp,NULL))
365 return(2);
366 #else //FLOG_CONFIG_TIMESTAMP_USEC
367 msg.timestamp = time(NULL);
368 #endif //FLOG_CONFIG_TIMESTAMP_USEC
369 #endif //FLOG_CONFIG_TIMESTAMP
370 #ifdef FLOG_CONFIG_SRC_INFO
371 if(src_file && src_file[0])
372 msg.src_file = src_file;
373 msg.src_line = src_line;
374 if(src_func && src_func[0])
375 msg.src_func = src_func;
376 #endif //FLOG_CONFIG_SRC_INFO
377 msg.type = type;
378 msg.msg_id = msg_id;
379 if(text && text[0])
380 msg.text = text;
382 //Add message to log
383 if(flog_add_msg(p,&msg)) {
384 return(1);
386 return(0);
390 //! do not call directly, use the flog_printf() macro instead
392 //! emit a formatted flog message (calls _flog_print())
393 //! @param[in,out] *p log to emit message to
394 //! @param[in] *subsystem which part of the program is outputing this message
395 //! @param[in] *src_file source code file (flog_printf() macro uses __FILE__ to fill this in)
396 //! @param[in] src_line source code line (flog_printf() macro uses __LINE__ to fill this in)
397 //! @param[in] *src_func source code function (flog_printf() macro uses __FUNCTION__ to fill this in)
398 //! @param[in] type use one of the FLOG_* defines
399 //! @param[in] msg_id optionally use errno or one of the FLOG_MSG_* defines
400 //! @param[in] *textf formatted message text
401 //! @retval 0 success
402 //! @retval 1 error while adding message to log
403 //! @retval 2 error unable to get time
404 //! @retval 3 did not add null message (flog is configured not to allow null messages)
405 //! @see flog_printf()
406 int _flog_printf(FLOG_T *p,const char *subsystem,
407 #ifdef FLOG_CONFIG_SRC_INFO
408 const char *src_file,uint_fast16_t src_line,const char *src_func,
409 #endif
410 FLOG_MSG_TYPE_T type,FLOG_MSG_ID_T msg_id,const char *textf, ...)
412 if(!p)
413 return(1);
414 //Only add message if it will be used
415 if(!flog_is_message_used(p,type))
416 return(0);
418 //Parse format string
419 char *text;
420 va_list ap;
421 va_start(ap,textf);
422 if(vasprintf(&text,textf,ap)==-1)
423 return(1);
424 va_end(ap);
426 //Convert the input into a FLOG_MSG_T struct
427 FLOG_MSG_T msg;
428 init_flog_msg_t(&msg);
429 msg.msg_id = msg_id;
430 if(text && text[0])
431 msg.text = text;
432 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
433 if(!msg.msg_id && !msg.text)
434 return(1);
435 #endif //FLOG_CONFIG_ALLOW_NULL_MESSAGES
436 if(subsystem && subsystem[0])
437 msg.subsystem = subsystem;
438 #ifdef FLOG_CONFIG_TIMESTAMP
439 #ifdef FLOG_CONFIG_TIMESTAMP_USEC
440 if(gettimeofday(&msg.timestamp,NULL))
441 return(1);
442 #else //FLOG_CONFIG_TIMESTAMP_USEC
443 msg.timestamp = time(NULL);
444 #endif //FLOG_CONFIG_TIMESTAMP_USEC
445 #endif //FLOG_CONFIG_TIMESTAMP
446 #ifdef FLOG_CONFIG_SRC_INFO
447 if(src_file && src_file[0])
448 msg.src_file = src_file;
449 msg.src_line = src_line;
450 if(src_func && src_func[0])
451 msg.src_func = src_func;
452 #endif //FLOG_CONFIG_SRC_INFO
453 msg.type = type;
455 //Add message to log
456 if(flog_add_msg(p,&msg)) {
457 free(text);
458 return(1);
460 free(text);
461 return(0);
465 #ifdef DEBUG
466 //! Test various flog features
467 void flog_test(FLOG_T *p)
469 flog_printf(p,__func__,FLOG_NONE,0,"This is a test message with FLOG_NONE (0x%02x) as type - This message should NEVER be visible",FLOG_NONE);
470 flog_printf(p,__func__,FLOG_CRIT,0,"This is a test message with FLOG_CRIT (0x%02x) as type",FLOG_CRIT);
471 flog_printf(p,__func__,FLOG_ERR,0,"This is a test message with FLOG_ERR (0x%02x) as type",FLOG_ERR);
472 flog_printf(p,__func__,FLOG_WARN,0,"This is a test message with FLOG_WARN (0x%02x) as type",FLOG_WARN);
473 flog_printf(p,__func__,FLOG_NOTE,0,"This is a test message with FLOG_NOTE (0x%02x) as type",FLOG_NOTE);
474 flog_printf(p,__func__,FLOG_INFO,0,"This is a test message with FLOG_INFO (0x%02x) as type",FLOG_INFO);
475 flog_printf(p,__func__,FLOG_VINFO,0,"This is a test message with FLOG_VINFO (0x%02x) as type",FLOG_VINFO);
476 flog_printf(p,__func__,FLOG_DEBUG,0,"This is a test message with FLOG_DEBUG (0x%02x) as type",FLOG_DEBUG);
477 flog_printf(p,__func__,FLOG_DEEP_DEBUG,0,"This is a test message with FLOG_DEEP_DEBUG (0x%02x) as type - This message should only be visible when implicitly switching on deep debugging",FLOG_DEEP_DEBUG);
478 flog_dprintf(p,__func__,FLOG_CRITICAL,0,"This is a test message using flog_dprintf() macro with FLOG_CRITICAL (0x%02x) as type",FLOG_CRITICAL);
480 #endif