1 //! Flog - The F logging library
4 //! @author Nabeel Sowan (nabeel.sowan@vibes.se)
6 //! Useful as the main logger of a program or embedded system.
7 //! Requires C99 + GNU support.
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
27 #ifdef FLOG_CONFIG_SRC_INFO
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
,
47 #ifdef FLOG_CONFIG_SRC_INFO
48 const char *src_file
,uint_fast16_t src_line
,const char *src_func
,
50 FLOG_MSG_TYPE_T type
,FLOG_MSG_ID_T msg_id
,const char *text
)
53 if((p
=malloc(sizeof(FLOG_MSG_T
)))!=NULL
) {
56 if(subsystem
&& subsystem
[0]) {
57 if((p
->subsystem
=strdup(subsystem
))==NULL
) {
58 destroy_flog_msg_t(p
);
62 #ifdef FLOG_CONFIG_TIMESTAMP
63 p
->timestamp
=timestamp
;
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
);
73 if(src_func
&& src_func
[0]) {
74 if((p
->src_func
=strdup(src_func
))==NULL
) {
75 destroy_flog_msg_t(p
);
82 if((p
->text
=strdup(text
))==NULL
) {
83 destroy_flog_msg_t(p
);
94 //! internal use only, or when creating flog output function
95 void destroy_flog_msg_t(FLOG_MSG_T
*p
)
99 #ifdef FLOG_CONFIG_SRC_INFO
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
));
117 p
->accepted_msg_type
=FLOG_ACCEPT_ALL
;
118 //p->output_func=NULL;
119 //p->output_func_data=NULL;
121 p
->output_stop_on_error
=1;
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
)
137 if((p
=malloc(sizeof(FLOG_T
)))!=NULL
) {
139 p
->accepted_msg_type
=accepted_msg_type
;
140 if(name
&& name
[0]) {
141 if((p
->name
=strdup(name
))==NULL
) {
152 void destroy_flog_t(FLOG_T
*p
)
158 for(i
=0;i
<p
->msg_amount
;i
++)
159 destroy_flog_msg_t(p
->msg
[i
]);
162 free(p
->sublog
); //! Note that sublogs are not freed
169 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
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
))
186 //copy the input msg into a FLOG_MSG_T struct
190 //append name to subsystem
191 int free_subsystem
=0;
193 if(outmsg
.subsystem
) {
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
;
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) {
210 p->msg[p->msg_amount]=msg;
215 //! @todo invent a suitable error output strategy
218 //run output function
220 if(p
->output_stop_on_error
? !p
->output_error
: 1) {
221 if((e
=p
->output_func(p
,&outmsg
)))
226 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
227 if(stack_depth
+1 < FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
) {
230 //add message to sublogs
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
239 //if we allocated a string, free it
241 free(outmsg
.subsystem
);
247 //! clear all messages stored in log
248 void flog_clear_msg_buffer(FLOG_T
*p
)
252 for(i
=0;i
<p
->msg_amount
;i
++)
253 destroy_flog_msg_t(p
->msg
[i
]);
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
)
271 flog_print(p
->error_log
,NULL
,FLOG_ERROR
,0,"cannot append log to itself (causes circular dependency)");
275 if((new_sublog
=realloc(p
->sublog
,(p
->sublog_amount
+1)*sizeof(FLOG_T
*)))==NULL
)
277 p
->sublog
=new_sublog
;
278 p
->sublog
[p
->sublog_amount
]=sublog
;
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
)
297 if(p
->output_stop_on_error
? !p
->output_error
: 1)
300 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
301 if(stack_depth
+1 < FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
) {
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
313 #ifdef FLOG_CONFIG_RECURSIVE_MAX_STACK_DEPTH
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
,
342 FLOG_MSG_TYPE_T type
,FLOG_MSG_ID_T msg_id
,const char *text
)
346 //Only add message if it will be used
347 if(!flog_is_message_used(p
,type
))
350 //Convert the input into a FLOG_MSG_T struct
352 init_flog_msg_t(&msg
);
356 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
357 if(!msg
.msg_id
&& !msg
.text
)
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
))
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
383 if(flog_add_msg(p
,&msg
)) {
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
,
410 FLOG_MSG_TYPE_T type
,FLOG_MSG_ID_T msg_id
,const char *textf
, ...)
414 //Only add message if it will be used
415 if(!flog_is_message_used(p
,type
))
418 //Parse format string
422 if(vasprintf(&text
,textf
,ap
)==-1)
426 //Convert the input into a FLOG_MSG_T struct
428 init_flog_msg_t(&msg
);
432 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
433 if(!msg
.msg_id
&& !msg
.text
)
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
))
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
456 if(flog_add_msg(p
,&msg
)) {
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
);