Add (optional) support for FLOG_SRC_INFO, and now flog_file is working properly.
[flog.git] / flog.c
blob874873da8eaa78e21521aa10ca91eb77f4855edb
1 /*!
2 @file flog.c
3 @brief Flog logging library
4 @author Nabeel Sowan (nabeel.sowan@vibes.se)
6 Useful as the main logger of a program
7 */
9 #include "flog.h"
10 #define _GNU_SOURCE
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdarg.h>
16 //#include <time.h>
18 //! initialise a FLOG_MSG_T to defaults
19 void init_flog_msg_t(FLOG_MSG_T *p)
21 #ifdef FLOG_TIMESTAMP
22 p->time=0;
23 #endif
24 #ifdef FLOG_SRC_INFO
25 p->src_file=NULL;
26 p->src_line=-1;
27 p->src_func=NULL;
28 #endif
29 p->type=FLOG_NONE;
30 p->subsystem=NULL;
31 p->text=NULL;
34 //! create and return a FLOG_MSG_T type
35 /*!
36 @return NULL = error
38 #ifdef FLOG_SRC_INFO
39 FLOG_MSG_T * create_flog_msg_t(const char *src_file,int src_line,const char *src_func,FLOG_MSG_TYPE_T type,const char *subsystem,const char *text)
40 #else
41 FLOG_MSG_T * create_flog_msg_t(FLOG_MSG_TYPE_T type,const char *subsystem,const char *text)
42 #endif
44 if(text==NULL)
45 return(NULL);
46 FLOG_MSG_T *p;
47 if((p=malloc(sizeof(FLOG_MSG_T)))!=NULL) {
48 init_flog_msg_t(p);
49 #ifdef FLOG_TIMESTAMP
50 p->time=0; //TODO
51 #endif
52 #ifdef FLOG_SRC_INFO
53 if((src_file != NULL) && (strlen(src_file)>0)) {
54 if(asprintf(&p->src_file,src_file)==-1) {
55 destroy_flog_msg_t(p);
56 return(NULL);
59 p->src_line=src_line;
60 if((src_func != NULL) && (strlen(src_func)>0)) {
61 if(asprintf(&p->src_func,src_func)==-1) {
62 destroy_flog_msg_t(p);
63 return(NULL);
66 #endif
67 p->type=type;
68 if((subsystem != NULL) && (strlen(subsystem)>0)) {
69 if(asprintf(&p->subsystem,subsystem)==-1) {
70 destroy_flog_msg_t(p);
71 return(NULL);
74 if(asprintf(&p->text,text)==-1) {
75 destroy_flog_msg_t(p);
76 return(NULL);
79 return(p);
82 //! free a FLOG_MSG_T
83 void destroy_flog_msg_t(FLOG_MSG_T *p)
85 if(p!=NULL) {
86 #ifdef FLOG_SRC_INFO
87 free(p->src_file);
88 free(p->src_func);
89 #endif
90 free(p->subsystem);
91 free(p->text);
92 free(p);
96 //! initialise a FLOG_T to defaults
97 void init_flog_t(FLOG_T *p)
99 p->name=NULL;
100 p->accepted_msg_type=FLOG_ACCEPT_ALL;
101 p->output_func=NULL;
102 p->output_func_data=NULL;
103 p->output_error=0;
104 p->output_stop_on_error=1;
105 p->error_log=NULL;
106 p->msg=NULL;
107 p->msg_amount=0;
108 p->msg_max=0;
109 p->sublog=NULL;
110 p->sublog_amount=0;
113 //! create and return a FLOG_T type
115 @return NULL = error
117 FLOG_T * create_flog_t(const char *name, FLOG_MSG_TYPE_T accepted_msg_type)
119 FLOG_T *p;
120 if((p=malloc(sizeof(FLOG_T)))!=NULL) {
121 init_flog_t(p);
122 p->accepted_msg_type=accepted_msg_type;
123 if((name != NULL) && (strlen(name)>0)) {
124 if(asprintf(&p->name,name)==-1) {
125 destroy_flog_t(p);
126 return(NULL);
130 return(p);
133 //! free a FLOG_T
134 void destroy_flog_t(FLOG_T *p)
136 if(p!=NULL) {
137 free(p->name);
138 if(p->msg!=NULL) {
139 int i;
140 for(i=0;i<p->msg_amount;i++)
141 destroy_flog_msg_t(p->msg[i]);
142 free(p->msg);
144 free(p->sublog); //!< Note that the sublogs themselves are not freed
145 free(p);
149 //! add a FLOG_MSG_T to FLOG_T and do all required logic (used by flog_print[f] functions)
151 @param p target log
152 @param msg message to add
153 @return 0 = success
155 int flog_add_msg(FLOG_T *p,FLOG_MSG_T *msg)
157 int e=0;
159 //create temporary msg to allow reformatting the message
160 FLOG_MSG_T *tmpmsg;
161 #ifdef FLOG_SRC_INFO
162 if((tmpmsg=create_flog_msg_t(msg->src_file,msg->src_line,msg->src_func,msg->type,msg->subsystem,msg->text))==NULL)
163 #else
164 if((tmpmsg=create_flog_msg_t(msg->type,msg->subsystem,msg->text))==NULL)
165 #endif
166 return(1);
168 //append name to subsystem
169 if((p->name != NULL) && (strlen(p->name)==0))
170 free(p->name);
171 if((tmpmsg->subsystem != NULL) && (strlen(tmpmsg->subsystem)==0))
172 free(tmpmsg->subsystem);
173 if((p->name != NULL) && (tmpmsg->subsystem != NULL)) {
174 char *tmpstr;
175 if(asprintf(&tmpstr,"%s/%s",p->name,tmpmsg->subsystem)!=-1) {
176 free(tmpmsg->subsystem);
177 tmpmsg->subsystem=tmpstr;
180 else if((p->name != NULL) && (tmpmsg->subsystem == NULL))
181 asprintf(&tmpmsg->subsystem,p->name);
183 //compare if accepted message type
184 if(tmpmsg->type & p->accepted_msg_type) {
185 //add message to buffer
186 if(p->msg_amount<p->msg_max) {
187 FLOG_MSG_T **new_msg;
188 if((new_msg=realloc(p->msg,(p->msg_amount+1)*sizeof(FLOG_MSG_T *)))!=NULL) {
189 p->msg=new_msg;
190 p->msg[p->msg_amount]=msg;
191 p->msg_amount++;
195 //run output function
196 if(p->output_func != NULL) {
197 if(p->output_stop_on_error ? !p->output_error : 1) {
198 if((e=p->output_func(p,tmpmsg)))
199 p->output_error=e;
204 //add message to sublogs
205 int i;
206 for(i=0;i<p->sublog_amount;i++) {
207 if(flog_add_msg(p->sublog[i],tmpmsg))
208 e=1;
210 destroy_flog_msg_t(tmpmsg);
211 return(e);
214 //! clear all messages stored in log
215 void flog_clear_msg_buffer(FLOG_T *p)
217 if((p!=NULL) && (p->msg!=NULL)) {
218 int i;
219 for(i=0;i<p->msg_amount;i++)
220 destroy_flog_msg_t(p->msg[i]);
221 free(p->msg);
222 p->msg_amount=0;
226 //! add a sublog to a log
228 @param p target log
229 @param sublog log to add
230 @return 0 = success
232 int flog_append_sublog(FLOG_T *p,FLOG_T *sublog)
234 if(p==NULL)
235 return(1);
236 if(p==sublog) {
237 flog_print(p->error_log,FLOG_ERROR,"flog_append_sublog","cannot append log to itself (causes circular dependency)");
238 return(1);
240 FLOG_T **new_sublog;
241 if((new_sublog=realloc(p->sublog,(p->sublog_amount+1)*sizeof(FLOG_T *)))==NULL)
242 return(1);
243 p->sublog=new_sublog;
244 p->sublog[p->sublog_amount]=sublog;
245 p->sublog_amount++;
246 return(0);
249 //! output an flog message
251 @param type use one of the FLOG_* defines
252 @param subsystem which part of the program is outputing this message (use __func__ typically)
253 @param text message text
254 @return 0 = success
256 #ifdef FLOG_SRC_INFO
257 int _flog_print(FLOG_T *p,const char *src_file,int src_line,const char *src_func,FLOG_MSG_TYPE_T type,const char *subsystem,const char *text)
258 #else
259 int _flog_print(FLOG_T *p,FLOG_MSG_TYPE_T type,const char *subsystem,const char *text)
260 #endif
262 if(p==NULL)
263 return(1);
264 FLOG_MSG_T *msg;
265 #ifdef FLOG_SRC_INFO
266 if((msg=create_flog_msg_t(src_file,src_line,src_func,type,subsystem,text))==NULL)
267 #else
268 if((msg=create_flog_msg_t(type,subsystem,text))==NULL)
269 #endif
270 return(1);
271 if(flog_add_msg(p,msg)) {
272 destroy_flog_msg_t(msg);
273 return(1);
275 destroy_flog_msg_t(msg);
276 return(0);
279 //! output a formatted flog message (calls flog_print())
281 @param type use one of the FLOG_* defines
282 @param subsystem which part of the program is outputing this message (use __func__ typically)
283 @param textf formatted message text
284 @return 0 = success
286 #ifdef FLOG_SRC_INFO
287 int _flog_printf(FLOG_T *p,const char *src_file,int src_line,const char *src_func,FLOG_MSG_TYPE_T type,const char *subsystem,const char *textf, ...)
288 #else
289 int _flog_printf(FLOG_T *p,FLOG_MSG_TYPE_T type,const char *subsystem,const char *textf, ...)
290 #endif
292 if(p==NULL)
293 return(1);
294 char *text;
295 va_list ap;
296 va_start(ap,textf);
297 if(vasprintf(&text,textf,ap)==-1)
298 return(1);
299 va_end(ap);
301 #ifdef FLOG_SRC_INFO
302 if(_flog_print(p,src_file,src_line,src_func,type,subsystem,text)) {
303 #else
304 if(_flog_print(p,type,subsystem,text)) {
305 #endif
306 free(text);
307 return(1);
309 free(text);
310 return(0);
313 //! create and return a string from FLOG_MSG_T type
315 @return NULL = error
317 char * flog_msg_t_to_str(const FLOG_MSG_T *p)
319 char *str,*typestr;
320 typestr=flog_get_msg_type_str(p->type);
321 #ifdef FLOG_TIMESTAMP
322 //! @todo add timestamp support
323 #else
324 #ifdef FLOG_SRC_INFO
325 if((p->subsystem != NULL) && (typestr != NULL))
326 asprintf(&str,"%s:%d %s() [%s] %s%s\n",p->src_file,p->src_line,p->src_func,p->subsystem,typestr,p->text);
327 else if((p->subsystem != NULL) && (typestr == NULL))
328 asprintf(&str,"%s:%d %s() [%s] %s\n",p->src_file,p->src_line,p->src_func,p->subsystem,p->text);
329 else if((p->subsystem == NULL) && (typestr != NULL))
330 asprintf(&str,"%s:%d %s() %s%s\n",p->src_file,p->src_line,p->src_func,typestr,p->text);
331 else
332 asprintf(&str,"%s:%d %s() %s\n",p->src_file,p->src_line,p->src_func,p->text);
333 #else
334 if((p->subsystem != NULL) && (typestr != NULL))
335 asprintf(&str,"[%s] %s%s\n",p->subsystem,typestr,p->text);
336 else if((p->subsystem != NULL) && (typestr == NULL))
337 asprintf(&str,"[%s] %s\n",p->subsystem,p->text);
338 else if((p->subsystem == NULL) && (typestr != NULL))
339 asprintf(&str,"%s%s\n",typestr,p->text);
340 else
341 asprintf(&str,"%s\n",p->text);
342 #endif
343 #endif
344 free(typestr);
345 return(str);
348 //! Return a string or NULL according to message type
349 char * flog_get_msg_type_str(FLOG_MSG_TYPE_T type)
351 int e=-1;
352 char *str=NULL;
353 switch(type)
355 case FLOG_CRITICAL:
356 e=asprintf(&str,"Critical: ");
357 break;
358 case FLOG_ERROR:
359 e=asprintf(&str,"Error: ");
360 break;
361 case FLOG_WARNING:
362 e=asprintf(&str,"Warning: ");
363 break;
364 case FLOG_NOTIFY:
365 e=asprintf(&str,"!: ");
366 break;
367 case FLOG_INFO:
368 break;
369 case FLOG_VERBOSE:
370 break;
371 case FLOG_DEBUG:
372 e=asprintf(&str,"Debug: ");
373 break;
374 case FLOG_FLOG_DEBUG:
375 e=asprintf(&str,"Flog debug: ");
376 break;
377 default:
378 break;
380 if(e==-1)
381 str=NULL;
382 return(str);
385 void flog_test(FLOG_T *p)
387 flog_printf(p,FLOG_NONE,__func__,"This is a test message with FLOG_NONE (0x%02x) as type - This message should NEVER be visible",FLOG_NONE);
388 flog_printf(p,FLOG_CRIT,__func__,"This is a test message with FLOG_CRIT (0x%02x) as type",FLOG_CRIT);
389 flog_printf(p,FLOG_ERR,__func__,"This is a test message with FLOG_ERR (0x%02x) as type",FLOG_ERR);
390 flog_printf(p,FLOG_WARN,__func__,"This is a test message with FLOG_WARN (0x%02x) as type",FLOG_WARN);
391 flog_printf(p,FLOG_NOTE,__func__,"This is a test message with FLOG_NOTE (0x%02x) as type",FLOG_NOTE);
392 flog_printf(p,FLOG_INFO,__func__,"This is a test message with FLOG_INFO (0x%02x) as type",FLOG_INFO);
393 flog_printf(p,FLOG_VINFO,__func__,"This is a test message with FLOG_VINFO (0x%02x) as type",FLOG_VINFO);
394 flog_printf(p,FLOG_DEBUG,__func__,"This is a test message with FLOG_DEBUG (0x%02x) as type",FLOG_DEBUG);
395 flog_printf(p,FLOG_FLOG_DEBUG,__func__,"This is a test message with FLOG_FLOG_DEBUG (0x%02x) as type - This message should only be visible when debugging the flog library itself",FLOG_FLOG_DEBUG);
396 flog_dprintf(p,FLOG_CRITICAL,__func__,"This is a test message using flog_dprintf() macro with FLOG_CRITICAL (0x%02x) as type",FLOG_CRITICAL);
399 #ifdef FLOG_TIMESTAMP
400 //! Return a string with current timestamp in ISO-format
402 @todo this function is probably a memory leak by design :(
404 const char * get_timestamp(void)
406 static char str[30];
407 time_t rawtime;
408 struct tm *timeinfo;
409 time(&rawtime);
410 timeinfo = localtime(&rawtime);
411 sprintf(str,"%04d-%02d-%02d %02d:%02d:%02d",timeinfo->tm_year+1900,timeinfo->tm_mon+1,timeinfo->tm_mday,timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);
412 return(str);
414 #endif