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
,int 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 //! add a FLOG_MSG_T to FLOG_T and do all required logic (used by flog_print[f] functions)
171 //! internal use only, or when extending flog
172 //! @param[in,out] *p target log
173 //! @param[in] *msg message to add
174 //! @retval 0 success
175 int flog_add_msg(FLOG_T
*p
,FLOG_MSG_T
*msg
)
177 //! @todo since this function is recursive, a max stack usage limit would be in order
179 //compare if accepted message type
180 if(!(msg
->type
& p
->accepted_msg_type
))
183 //copy the input msg into a FLOG_MSG_T struct
187 //append name to subsystem
188 int free_subsystem
=0;
190 if(outmsg
.subsystem
) {
192 if(asprintf(&tmpstr
,"%s/%s",p
->name
,outmsg
.subsystem
)!=-1) { //We don't care if we can't allocate memory
193 outmsg
.subsystem
= tmpstr
;
197 outmsg
.subsystem
=p
->name
;
201 //! @todo add message to buffer
203 if(p->msg_amount<p->msg_max) {
204 FLOG_MSG_T **new_msg;
205 if((new_msg=realloc(p->msg,(p->msg_amount+1)*sizeof(FLOG_MSG_T *)))!=NULL) {
207 p->msg[p->msg_amount]=msg;
212 //! @todo invent a suitable error output strategy
215 //run output function
217 if(p
->output_stop_on_error
? !p
->output_error
: 1) {
218 if((e
=p
->output_func(p
,&outmsg
)))
223 //add message to sublogs
225 for(i
=0;i
<p
->sublog_amount
;i
++)
226 e
+=flog_add_msg(p
->sublog
[i
],&outmsg
);
228 //if we allocated a string, free it
230 free(outmsg
.subsystem
);
236 //! clear all messages stored in log
237 void flog_clear_msg_buffer(FLOG_T
*p
)
241 for(i
=0;i
<p
->msg_amount
;i
++)
242 destroy_flog_msg_t(p
->msg
[i
]);
250 //! add a sublog to a log
252 //! @param[in,out] *p target log
253 //! @param[in] *sublog log to add
254 //! @retval 0 success
255 int flog_append_sublog(FLOG_T
*p
,FLOG_T
*sublog
)
260 flog_print(p
->error_log
,NULL
,FLOG_ERROR
,0,"cannot append log to itself (causes circular dependency)");
264 if((new_sublog
=realloc(p
->sublog
,(p
->sublog_amount
+1)*sizeof(FLOG_T
*)))==NULL
)
266 p
->sublog
=new_sublog
;
267 p
->sublog
[p
->sublog_amount
]=sublog
;
273 //! Is the message used in any way if put in this log?
275 //! This function can be used to decide whether or not to drop a message immediately
276 //! @param[in] *p the log receiving the message
277 //! @param[in] type the type from message
278 //! @retval 0 Message is never used
279 //! @retval 1 Message is used
280 int flog_is_message_used(FLOG_T
*p
,FLOG_MSG_TYPE_T type
)
282 if(type
& p
->accepted_msg_type
) {
283 if(p
->msg_amount
<p
->msg_max
)
286 if(p
->output_stop_on_error
? !p
->output_error
: 1)
290 for(i
=0;i
<p
->sublog_amount
;i
++) {
291 if(flog_is_message_used(p
->sublog
[i
],type
))
299 //! do not call directly, use the flog_print() macro instead
301 //! emit an flog message
302 //! @param[in,out] *p log to emit message to
303 //! @param[in] *subsystem which part of the program is outputing this message
304 //! @param[in] *src_file source code file (flog_print() macro uses __FILE__ to fill this in)
305 //! @param[in] src_line source code line (flog_print() macro uses __LINE__ to fill this in)
306 //! @param[in] *src_func source code function (flog_print() macro uses __FUNCTION__ to fill this in)
307 //! @param[in] type use one of the FLOG_* defines
308 //! @param[in] msg_id optionally use errno or one of the FLOG_MSG_* defines
309 //! @param[in] *text message text
310 //! @retval 0 success
311 //! @see flog_print()
312 int _flog_print(FLOG_T
*p
,const char *subsystem
,
313 #ifdef FLOG_CONFIG_SRC_INFO
314 const char *src_file
,int src_line
,const char *src_func
,
316 FLOG_MSG_TYPE_T type
,FLOG_MSG_ID_T msg_id
,const char *text
)
320 //Only add message if it will be used
321 if(!flog_is_message_used(p
,type
))
324 //Convert the input into a FLOG_MSG_T struct
326 init_flog_msg_t(&msg
);
330 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
331 if(!msg
.msg_id
&& !msg
.text
)
333 #endif //FLOG_CONFIG_ALLOW_NULL_MESSAGES
334 if(subsystem
&& subsystem
[0])
335 msg
.subsystem
= subsystem
;
336 #ifdef FLOG_CONFIG_TIMESTAMP
337 #ifdef FLOG_CONFIG_TIMESTAMP_USEC
338 if(gettimeofday(&msg
.timestamp
,NULL
))
340 #else //FLOG_CONFIG_TIMESTAMP_USEC
341 msg
.timestamp
= time(NULL
);
342 #endif //FLOG_CONFIG_TIMESTAMP_USEC
343 #endif //FLOG_CONFIG_TIMESTAMP
344 #ifdef FLOG_CONFIG_SRC_INFO
345 if(src_file
&& src_file
[0])
346 msg
.src_file
= src_file
;
347 msg
.src_line
= src_line
;
348 if(src_func
&& src_func
[0])
349 msg
.src_func
= src_func
;
350 #endif //FLOG_CONFIG_SRC_INFO
357 if(flog_add_msg(p
,&msg
)) {
364 //! do not call directly, use the flog_printf() macro instead
366 //! emit a formatted flog message (calls _flog_print())
367 //! @param[in,out] *p log to emit message to
368 //! @param[in] *subsystem which part of the program is outputing this message
369 //! @param[in] *src_file source code file (flog_printf() macro uses __FILE__ to fill this in)
370 //! @param[in] src_line source code line (flog_printf() macro uses __LINE__ to fill this in)
371 //! @param[in] *src_func source code function (flog_printf() macro uses __FUNCTION__ to fill this in)
372 //! @param[in] type use one of the FLOG_* defines
373 //! @param[in] msg_id optionally use errno or one of the FLOG_MSG_* defines
374 //! @param[in] *textf formatted message text
375 //! @retval 0 success
376 //! @see flog_printf()
377 int _flog_printf(FLOG_T
*p
,const char *subsystem
,
378 #ifdef FLOG_CONFIG_SRC_INFO
379 const char *src_file
,int src_line
,const char *src_func
,
381 FLOG_MSG_TYPE_T type
,FLOG_MSG_ID_T msg_id
,const char *textf
, ...)
385 //Only add message if it will be used
386 if(!flog_is_message_used(p
,type
))
389 //Parse format string
393 if(vasprintf(&text
,textf
,ap
)==-1)
397 //Convert the input into a FLOG_MSG_T struct
399 init_flog_msg_t(&msg
);
403 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
404 if(!msg
.msg_id
&& !msg
.text
)
406 #endif //FLOG_CONFIG_ALLOW_NULL_MESSAGES
407 if(subsystem
&& subsystem
[0])
408 msg
.subsystem
= subsystem
;
409 #ifdef FLOG_CONFIG_TIMESTAMP
410 #ifdef FLOG_CONFIG_TIMESTAMP_USEC
411 if(gettimeofday(&msg
.timestamp
,NULL
))
413 #else //FLOG_CONFIG_TIMESTAMP_USEC
414 msg
.timestamp
= time(NULL
);
415 #endif //FLOG_CONFIG_TIMESTAMP_USEC
416 #endif //FLOG_CONFIG_TIMESTAMP
417 #ifdef FLOG_CONFIG_SRC_INFO
418 if(src_file
&& src_file
[0])
419 msg
.src_file
= src_file
;
420 msg
.src_line
= src_line
;
421 if(src_func
&& src_func
[0])
422 msg
.src_func
= src_func
;
423 #endif //FLOG_CONFIG_SRC_INFO
427 if(flog_add_msg(p
,&msg
)) {
437 //! Test various flog features
438 void flog_test(FLOG_T
*p
)
440 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
);
441 flog_printf(p
,__func__
,FLOG_CRIT
,0,"This is a test message with FLOG_CRIT (0x%02x) as type",FLOG_CRIT
);
442 flog_printf(p
,__func__
,FLOG_ERR
,0,"This is a test message with FLOG_ERR (0x%02x) as type",FLOG_ERR
);
443 flog_printf(p
,__func__
,FLOG_WARN
,0,"This is a test message with FLOG_WARN (0x%02x) as type",FLOG_WARN
);
444 flog_printf(p
,__func__
,FLOG_NOTE
,0,"This is a test message with FLOG_NOTE (0x%02x) as type",FLOG_NOTE
);
445 flog_printf(p
,__func__
,FLOG_INFO
,0,"This is a test message with FLOG_INFO (0x%02x) as type",FLOG_INFO
);
446 flog_printf(p
,__func__
,FLOG_VINFO
,0,"This is a test message with FLOG_VINFO (0x%02x) as type",FLOG_VINFO
);
447 flog_printf(p
,__func__
,FLOG_DEBUG
,0,"This is a test message with FLOG_DEBUG (0x%02x) as type",FLOG_DEBUG
);
448 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
);
449 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
);