Makefile: add test.o to clean
[flog.git] / flog.c
blobe4cb55f496e8b6a700f0a8c0cdb8e55dbe8591bc
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=-1;
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,int 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 //! 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))
181 return(0);
183 //copy the input msg into a FLOG_MSG_T struct
184 FLOG_MSG_T outmsg;
185 outmsg=*msg;
187 //append name to subsystem
188 int free_subsystem=0;
189 if(p->name) {
190 if(outmsg.subsystem) {
191 char *tmpstr;
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;
194 free_subsystem=1;
196 } else {
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) {
206 p->msg=new_msg;
207 p->msg[p->msg_amount]=msg;
208 p->msg_amount++;
212 //! @todo invent a suitable error output strategy
213 int e=0;
215 //run output function
216 if(p->output_func) {
217 if(p->output_stop_on_error ? !p->output_error : 1) {
218 if((e=p->output_func(p,&outmsg)))
219 p->output_error=e;
223 //add message to sublogs
224 uint_fast8_t i;
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
229 if(free_subsystem)
230 free(outmsg.subsystem);
232 return(e);
236 //! clear all messages stored in log
237 void flog_clear_msg_buffer(FLOG_T *p)
239 if(p && p->msg) {
240 uint_fast16_t i;
241 for(i=0;i<p->msg_amount;i++)
242 destroy_flog_msg_t(p->msg[i]);
243 free(p->msg);
244 p->msg=NULL;
245 p->msg_amount=0;
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)
257 if(!p)
258 return(1);
259 if(p==sublog) {
260 flog_print(p->error_log,NULL,FLOG_ERROR,0,"cannot append log to itself (causes circular dependency)");
261 return(1);
263 FLOG_T **new_sublog;
264 if((new_sublog=realloc(p->sublog,(p->sublog_amount+1)*sizeof(FLOG_T *)))==NULL)
265 return(1);
266 p->sublog=new_sublog;
267 p->sublog[p->sublog_amount]=sublog;
268 p->sublog_amount++;
269 return(0);
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)
284 return(1);
285 if(p->output_func) {
286 if(p->output_stop_on_error ? !p->output_error : 1)
287 return(1);
289 uint_fast8_t i;
290 for(i=0;i<p->sublog_amount;i++) {
291 if(flog_is_message_used(p->sublog[i],type))
292 return(1);
295 return(0);
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,
315 #endif
316 FLOG_MSG_TYPE_T type,FLOG_MSG_ID_T msg_id,const char *text)
318 if(!p)
319 return(1);
320 //Only add message if it will be used
321 if(!flog_is_message_used(p,type))
322 return(0);
324 //Convert the input into a FLOG_MSG_T struct
325 FLOG_MSG_T msg;
326 init_flog_msg_t(&msg);
327 msg.msg_id = msg_id;
328 if(text && text[0])
329 msg.text = text;
330 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
331 if(!msg.msg_id && !msg.text)
332 return(1);
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))
339 return(1);
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
351 msg.type = type;
352 msg.msg_id = msg_id;
353 if(text && text[0])
354 msg.text = text;
356 //Add message to log
357 if(flog_add_msg(p,&msg)) {
358 return(1);
360 return(0);
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,
380 #endif
381 FLOG_MSG_TYPE_T type,FLOG_MSG_ID_T msg_id,const char *textf, ...)
383 if(!p)
384 return(1);
385 //Only add message if it will be used
386 if(!flog_is_message_used(p,type))
387 return(0);
389 //Parse format string
390 char *text;
391 va_list ap;
392 va_start(ap,textf);
393 if(vasprintf(&text,textf,ap)==-1)
394 return(1);
395 va_end(ap);
397 //Convert the input into a FLOG_MSG_T struct
398 FLOG_MSG_T msg;
399 init_flog_msg_t(&msg);
400 msg.msg_id = msg_id;
401 if(text && text[0])
402 msg.text = text;
403 #ifndef FLOG_CONFIG_ALLOW_NULL_MESSAGES
404 if(!msg.msg_id && !msg.text)
405 return(1);
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))
412 return(1);
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
424 msg.type = type;
426 //Add message to log
427 if(flog_add_msg(p,&msg)) {
428 free(text);
429 return(1);
431 free(text);
432 return(0);
436 #ifdef DEBUG
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);
451 #endif