1 /*
2 bctoolbox
3 Copyright (C) 2016 Belledonne Communications SARL
4
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "bctoolbox/logging.h"
26 #include <time.h>
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30
31 #ifdef _MSC_VER
32 #ifndef access
33 #define access _access
34 #endif
35 #ifndef fileno
36 #define fileno _fileno
37 #endif
38 #endif
39
40
41 typedef struct{
42 char *domain;
43 unsigned int logmask;
44 }BctoolboxLogDomain;
45
bctbx_log_domain_destroy(BctoolboxLogDomain * obj)46 static void bctbx_log_domain_destroy(BctoolboxLogDomain *obj){
47 if (obj->domain) bctbx_free(obj->domain);
48 bctbx_free(obj);
49 }
50
51 typedef struct _bctbx_logger_t {
52 bctbx_list_t *logv_outs;
53 unsigned int log_mask; /*the default log mask, if no per-domain settings are found*/
54 unsigned long log_thread_id;
55 bctbx_list_t *log_stored_messages_list;
56 bctbx_list_t *log_domains;
57 bctbx_mutex_t log_stored_messages_mutex;
58 bctbx_mutex_t domains_mutex;
59 bctbx_mutex_t log_mutex;
60 } bctbx_logger_t;
61
62 struct _bctbx_log_handler_t {
63 BctbxLogHandlerFunc func;
64 BctbxLogHandlerDestroyFunc destroy;
65 void* user_info;
66 };
67
68 typedef struct _bctbx_file_log_handler_t {
69 char* path;
70 char* name;
71 uint64_t max_size;
72 uint64_t size;
73 FILE* file;
74 } bctbx_file_log_handler_t;
75
76 static bctbx_logger_t __bctbx_logger = { NULL, BCTBX_LOG_WARNING|BCTBX_LOG_ERROR|BCTBX_LOG_FATAL, 0};
77
78 static unsigned int bctbx_init_logger_refcount = 0;
79
bctbx_init_logger(bool_t create)80 void bctbx_init_logger(bool_t create){
81 if (bctbx_init_logger_refcount++ > 0) return; /*already initialized*/
82
83 bctbx_mutex_init(&__bctbx_logger.domains_mutex, NULL);
84 bctbx_mutex_init(&__bctbx_logger.log_mutex, NULL);
85 if(create) {
86 bctbx_log_handler_t* handler = bctbx_create_log_handler(bctbx_logv_out, bctbx_logv_out_destroy, NULL);
87 bctbx_add_log_handler(handler);
88 }
89 }
90
bctbx_log_handlers_free(void)91 void bctbx_log_handlers_free(void) {
92 bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
93 while (loggers) {
94 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
95 handler->destroy(handler);
96 loggers = loggers->next;
97 }
98 }
99
bctbx_uninit_logger(void)100 void bctbx_uninit_logger(void){
101 if (--bctbx_init_logger_refcount <= 0) {
102 bctbx_logv_flush();
103 bctbx_mutex_destroy(&__bctbx_logger.domains_mutex);
104 bctbx_mutex_destroy(&__bctbx_logger.log_mutex);
105 bctbx_log_handlers_free();
106 __bctbx_logger.logv_outs = bctbx_list_free(__bctbx_logger.logv_outs);
107 __bctbx_logger.log_domains = bctbx_list_free_with_data(__bctbx_logger.log_domains, (void (*)(void*))bctbx_log_domain_destroy);
108 }
109 }
110
bctbx_create_log_handler(BctbxLogHandlerFunc func,BctbxLogHandlerDestroyFunc destroy,void * user_info)111 bctbx_log_handler_t* bctbx_create_log_handler(BctbxLogHandlerFunc func, BctbxLogHandlerDestroyFunc destroy, void* user_info) {
112 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)bctbx_malloc(sizeof(bctbx_log_handler_t));
113 handler->func = func;
114 handler->destroy = destroy;
115 handler->user_info = user_info;
116 return handler;
117 }
118
bctbx_create_file_log_handler(uint64_t max_size,const char * path,const char * name,FILE * f)119 bctbx_log_handler_t* bctbx_create_file_log_handler(uint64_t max_size, const char* path, const char* name, FILE* f) {
120 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)bctbx_malloc0(sizeof(bctbx_log_handler_t));
121 bctbx_file_log_handler_t* filehandler = (bctbx_file_log_handler_t*)bctbx_malloc(sizeof(bctbx_file_log_handler_t));
122 char *full_name = bctbx_strdup_printf("%s/%s",
123 path,
124 name);
125 struct stat buf;
126 memset(&buf, 0, sizeof(buf));
127 handler->func=bctbx_logv_file;
128 handler->destroy=bctbx_logv_file_destroy;
129 filehandler->max_size = max_size;
130 // init with actual file size
131 if(stat(full_name, &buf) != 0) {
132 fprintf(stderr,"Error while creating file log handler. \n");
133 return NULL;
134 }
135 bctbx_free(full_name);
136 filehandler->size = buf.st_size;
137 filehandler->path = bctbx_strdup(path);
138 filehandler->name = bctbx_strdup(name);
139 filehandler->file = f;
140 handler->user_info=(void*) filehandler;
141 return handler;
142 }
143
144 /**
145 *@param func: your logging function, compatible with the BctoolboxLogFunc prototype.
146 *
147 **/
bctbx_add_log_handler(bctbx_log_handler_t * handler)148 void bctbx_add_log_handler(bctbx_log_handler_t* handler){
149 if (handler && !bctbx_list_find(__bctbx_logger.logv_outs, handler))
150 __bctbx_logger.logv_outs = bctbx_list_append(__bctbx_logger.logv_outs, (void*)handler);
151 /*else, already in*/
152 }
153
wrapper(void * info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)154 static void wrapper(void* info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
155 BctbxLogFunc func = (BctbxLogFunc)info;
156 func(domain, lev, fmt, args);
157 }
158
bctbx_set_log_handler(BctbxLogFunc func)159 void bctbx_set_log_handler(BctbxLogFunc func){
160 static bctbx_log_handler_t handler;
161 handler.func=wrapper;
162 handler.destroy=(BctbxLogHandlerDestroyFunc)bctbx_logv_out_destroy;
163 handler.user_info=(void*)func;
164 bctbx_add_log_handler(&handler);
165 }
166
bctbx_set_log_file(FILE * f)167 void bctbx_set_log_file(FILE* f){
168 static bctbx_file_log_handler_t filehandler;
169 static bctbx_log_handler_t handler;
170 handler.func=bctbx_logv_file;
171 handler.destroy=(BctbxLogHandlerDestroyFunc)bctbx_logv_file_destroy;
172 filehandler.max_size = -1;
173 filehandler.file = f;
174 handler.user_info=(void*) &filehandler;
175 bctbx_add_log_handler(&handler);
176 }
177
bctbx_get_log_handlers(void)178 bctbx_list_t* bctbx_get_log_handlers(void){
179 return __bctbx_logger.logv_outs;
180 }
181
get_log_domain(const char * domain)182 static BctoolboxLogDomain * get_log_domain(const char *domain){
183 bctbx_list_t *it;
184
185 if (domain == NULL) return NULL;
186 for (it = __bctbx_logger.log_domains; it != NULL; it = bctbx_list_next(it)) {
187 BctoolboxLogDomain *ld = (BctoolboxLogDomain*)bctbx_list_get_data(it);
188 if (ld->domain && strcmp(ld->domain, domain) == 0 ){
189 return ld;
190 }
191 }
192 return NULL;
193 }
194
get_log_domain_rw(const char * domain)195 static BctoolboxLogDomain *get_log_domain_rw(const char *domain){
196 BctoolboxLogDomain *ret;
197
198 if (domain == NULL) return NULL;
199 ret = get_log_domain(domain);
200 if (ret) return ret;
201 /*it does not exist, hence create it by taking the mutex*/
202 bctbx_mutex_lock(&__bctbx_logger.domains_mutex);
203 ret = get_log_domain(domain);
204 if (!ret){
205 ret = bctbx_new0(BctoolboxLogDomain,1);
206 ret->domain = bctbx_strdup(domain);
207 ret->logmask = __bctbx_logger.log_mask;
208 __bctbx_logger.log_domains = bctbx_list_prepend(__bctbx_logger.log_domains, ret);
209 }
210 bctbx_mutex_unlock(&__bctbx_logger.domains_mutex);
211 return ret;
212 }
213
214 /**
215 * @ param levelmask a mask of BCTBX_DEBUG, BCTBX_MESSAGE, BCTBX_WARNING, BCTBX_ERROR
216 * BCTBX_FATAL .
217 **/
bctbx_set_log_level_mask(const char * domain,int levelmask)218 void bctbx_set_log_level_mask(const char *domain, int levelmask){
219 if (domain == NULL) __bctbx_logger.log_mask=levelmask;
220 else get_log_domain_rw(domain)->logmask = levelmask;
221 }
222
223
bctbx_set_log_level(const char * domain,BctbxLogLevel level)224 void bctbx_set_log_level(const char *domain, BctbxLogLevel level){
225 int levelmask = BCTBX_LOG_FATAL;
226 if (level<=BCTBX_LOG_ERROR){
227 levelmask |= BCTBX_LOG_ERROR;
228 }
229 if (level<=BCTBX_LOG_WARNING){
230 levelmask |= BCTBX_LOG_WARNING;
231 }
232 if (level<=BCTBX_LOG_MESSAGE){
233 levelmask |= BCTBX_LOG_MESSAGE;
234 }
235 if (level<=BCTBX_LOG_TRACE) {
236 levelmask |= BCTBX_LOG_TRACE;
237 }
238 if (level<=BCTBX_LOG_DEBUG){
239 levelmask |= BCTBX_LOG_DEBUG;
240 }
241 bctbx_set_log_level_mask(domain, levelmask);
242 }
243
bctbx_get_log_level_mask(const char * domain)244 unsigned int bctbx_get_log_level_mask(const char *domain) {
245 BctoolboxLogDomain *ld;
246 if (domain == NULL || (ld = get_log_domain(domain)) == NULL) return __bctbx_logger.log_mask;
247 else return ld->logmask;
248 }
249
bctbx_set_log_thread_id(unsigned long thread_id)250 void bctbx_set_log_thread_id(unsigned long thread_id) {
251 if (thread_id == 0) {
252 bctbx_logv_flush();
253 bctbx_mutex_destroy(&__bctbx_logger.log_stored_messages_mutex);
254 } else {
255 bctbx_mutex_init(&__bctbx_logger.log_stored_messages_mutex, NULL);
256 }
257 __bctbx_logger.log_thread_id = thread_id;
258 }
259
bctbx_strdup_vprintf(const char * fmt,va_list ap)260 char * bctbx_strdup_vprintf(const char *fmt, va_list ap)
261 {
262 /* Guess we need no more than 100 bytes. */
263 int n, size = 200;
264 char *p,*np;
265 #ifndef _WIN32
266 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
267 #endif
268 if ((p = (char *) bctbx_malloc (size)) == NULL)
269 return NULL;
270 while (1){
271 /* Try to print in the allocated space. */
272 #ifndef _WIN32
273 va_copy(cap,ap);
274 n = vsnprintf (p, size, fmt, cap);
275 va_end(cap);
276 #else
277 /*this works on 32 bits, luckily*/
278 n = vsnprintf (p, size, fmt, ap);
279 #endif
280 /* If that worked, return the string. */
281 if (n > -1 && n < size)
282 return p;
283 //printf("Reallocing space.\n");
284 /* Else try again with more space. */
285 if (n > -1) /* glibc 2.1 */
286 size = n + 1; /* precisely what is needed */
287 else /* glibc 2.0 */
288 size *= 2; /* twice the old size */
289 if ((np = (char *) bctbx_realloc (p, size)) == NULL)
290 {
291 free(p);
292 return NULL;
293 } else {
294 p = np;
295 }
296 }
297 }
298
bctbx_strdup_printf(const char * fmt,...)299 char *bctbx_strdup_printf(const char *fmt,...){
300 char *ret;
301 va_list args;
302 va_start (args, fmt);
303 ret=bctbx_strdup_vprintf(fmt, args);
304 va_end (args);
305 return ret;
306 }
307
bctbx_strcat_vprintf(char * dst,const char * fmt,va_list ap)308 char * bctbx_strcat_vprintf(char* dst, const char *fmt, va_list ap){
309 char *ret;
310 size_t dstlen, retlen;
311
312 ret=bctbx_strdup_vprintf(fmt, ap);
313 if (!dst) return ret;
314
315 dstlen = strlen(dst);
316 retlen = strlen(ret);
317
318 if ((dst = bctbx_realloc(dst, dstlen+retlen+1)) != NULL){
319 strncat(dst,ret,retlen);
320 dst[dstlen+retlen] = '\0';
321 bctbx_free(ret);
322 return dst;
323 } else {
324 bctbx_free(ret);
325 return NULL;
326 }
327 }
328
bctbx_strcat_printf(char * dst,const char * fmt,...)329 char *bctbx_strcat_printf(char* dst, const char *fmt,...){
330 char *ret;
331 va_list args;
332 va_start (args, fmt);
333 ret=bctbx_strcat_vprintf(dst, fmt, args);
334 va_end (args);
335 return ret;
336 }
337
338 #if defined(_WIN32) || defined(_WIN32_WCE)
339 #define ENDLINE "\r\n"
340 #else
341 #define ENDLINE "\n"
342 #endif
343
344 typedef struct {
345 int level;
346 char *msg;
347 char *domain;
348 } bctbx_stored_log_t;
349
_bctbx_logv_flush(int dummy,...)350 void _bctbx_logv_flush(int dummy, ...) {
351 bctbx_list_t *elem;
352 bctbx_list_t *msglist;
353 va_list empty_va_list;
354 va_start(empty_va_list, dummy);
355 bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
356 msglist = __bctbx_logger.log_stored_messages_list;
357 __bctbx_logger.log_stored_messages_list = NULL;
358 bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
359 for (elem = msglist; elem != NULL; elem = bctbx_list_next(elem)) {
360 bctbx_stored_log_t *l = (bctbx_stored_log_t *)bctbx_list_get_data(elem);
361 bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
362 #ifdef _WIN32
363 while (loggers) {
364 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
365 if(handler) {
366 va_list cap;
367 va_copy(cap, empty_va_list);
368 handler->func(handler->user_info, l->domain, l->level, l->msg, cap);
369 va_end(cap);
370 }
371 loggers = loggers->next;
372 }
373 #else
374
375 while (loggers) {
376 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
377 if(handler) {
378 va_list cap;
379 va_copy(cap, empty_va_list);
380 handler->func(handler->user_info, l->domain, l->level, l->msg, cap);
381 va_end(cap);
382 }
383 loggers = loggers->next;
384 }
385
386 #endif
387 if (l->domain) bctbx_free(l->domain);
388 bctbx_free(l->msg);
389 bctbx_free(l);
390 }
391 bctbx_list_free(msglist);
392 va_end(empty_va_list);
393 }
394
bctbx_logv_flush(void)395 void bctbx_logv_flush(void) {
396 _bctbx_logv_flush(0);
397 }
398
bctbx_logv(const char * domain,BctbxLogLevel level,const char * fmt,va_list args)399 void bctbx_logv(const char *domain, BctbxLogLevel level, const char *fmt, va_list args) {
400 if ((__bctbx_logger.logv_outs != NULL) && bctbx_log_level_enabled(domain, level)) {
401 if (__bctbx_logger.log_thread_id == 0) {
402 bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
403 while (loggers) {
404 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
405 if(handler) {
406 va_list tmp;
407 va_copy(tmp, args);
408 handler->func(handler->user_info, domain, level, fmt, tmp);
409 va_end(tmp);
410 }
411 loggers = loggers->next;
412 }
413 } else if (__bctbx_logger.log_thread_id == bctbx_thread_self()) {
414 bctbx_list_t *loggers;
415 bctbx_logv_flush();
416 loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
417 while (loggers) {
418 bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
419 if(handler) {
420 va_list tmp;
421 va_copy(tmp, args);
422 handler->func(handler->user_info, domain, level, fmt, tmp);
423 va_end(tmp);
424 }
425 loggers = loggers->next;
426 }
427 } else {
428 bctbx_stored_log_t *l = bctbx_new(bctbx_stored_log_t, 1);
429 l->domain = domain ? bctbx_strdup(domain) : NULL;
430 l->level = level;
431 l->msg = bctbx_strdup_vprintf(fmt, args);
432 bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
433 __bctbx_logger.log_stored_messages_list = bctbx_list_append(__bctbx_logger.log_stored_messages_list, l);
434 bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
435 }
436 }
437 #if !defined(_WIN32_WCE)
438 if (level == BCTBX_LOG_FATAL) {
439 bctbx_logv_flush();
440 abort();
441 }
442 #endif
443 }
444
445 /*This function does the default formatting and output to file*/
bctbx_logv_out(void * user_info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)446 void bctbx_logv_out(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
447 const char *lname="undef";
448 char *msg;
449 struct timeval tp;
450 struct tm *lt;
451 #ifndef _WIN32
452 struct tm tmbuf;
453 #endif
454 time_t tt;
455 FILE *std = stdout;
456 bctbx_gettimeofday(&tp,NULL);
457 tt = (time_t)tp.tv_sec;
458
459 #ifdef _WIN32
460 lt = localtime(&tt);
461 #else
462 lt = localtime_r(&tt,&tmbuf);
463 #endif
464
465 switch(lev){
466 case BCTBX_LOG_DEBUG:
467 lname = "debug";
468 break;
469 case BCTBX_LOG_MESSAGE:
470 lname = "message";
471 break;
472 case BCTBX_LOG_WARNING:
473 lname = "warning";
474 break;
475 case BCTBX_LOG_ERROR:
476 lname = "error";
477 std = stderr;
478 break;
479 case BCTBX_LOG_FATAL:
480 lname = "fatal";
481 std = stderr;
482 break;
483 default:
484 lname = "badlevel";
485 }
486
487 msg=bctbx_strdup_vprintf(fmt,args);
488 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
489 #ifndef _UNICODE
490 OutputDebugStringA(msg);
491 OutputDebugStringA("\r\n");
492 #else
493 {
494 size_t len=strlen(msg);
495 wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
496 mbstowcs(tmp,msg,len);
497 OutputDebugStringW(tmp);
498 OutputDebugStringW(L"\r\n");
499 bctbx_free(tmp);
500 }
501 #endif
502 #endif
503 fprintf(std,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
504 ,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
505 ,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
506 fflush(std);
507 bctbx_free(msg);
508 }
509
bctbx_logv_out_destroy(bctbx_log_handler_t * handler)510 void bctbx_logv_out_destroy(bctbx_log_handler_t* handler) {
511 handler->user_info=NULL;
512 }
513
_try_open_log_collection_file(bctbx_file_log_handler_t * filehandler)514 static int _try_open_log_collection_file(bctbx_file_log_handler_t *filehandler) {
515 struct stat statbuf;
516 char *log_filename;
517
518 log_filename = bctbx_strdup_printf("%s/%s",
519 filehandler->path,
520 filehandler->name);
521 filehandler->file = fopen(log_filename, "a");
522 bctbx_free(log_filename);
523 if (filehandler->file == NULL) return -1;
524
525 fstat(fileno(filehandler->file), &statbuf);
526 if ((uint64_t)statbuf.st_size > filehandler->max_size) {
527 fclose(filehandler->file);
528 return -1;
529 }
530
531 filehandler->size = statbuf.st_size;
532 return 0;
533 }
534
_rotate_log_collection_files(bctbx_file_log_handler_t * filehandler)535 static void _rotate_log_collection_files(bctbx_file_log_handler_t *filehandler) {
536 char *log_filename;
537 char *log_filename2;
538 char *file_no_extension = bctbx_strdup(filehandler->name);
539 char *extension = strrchr(file_no_extension, '.');
540 char *extension2 = bctbx_strdup(extension);
541 int n = 1;
542 file_no_extension[extension - file_no_extension] = '\0';
543
544 log_filename = bctbx_strdup_printf("%s/%s_1%s",
545 filehandler->path,
546 file_no_extension,
547 extension2);
548 while(access(log_filename, F_OK) != -1) {
549 // file exists
550 n++;
551 log_filename = bctbx_strdup_printf("%s/%s_%d%s",
552 filehandler->path,
553 file_no_extension,
554 n,
555 extension2);
556 }
557
558 while(n > 1) {
559 log_filename = bctbx_strdup_printf("%s/%s_%d%s",
560 filehandler->path,
561 file_no_extension,
562 n-1,
563 extension2);
564 log_filename2 = bctbx_strdup_printf("%s/%s_%d%s",
565 filehandler->path,
566 file_no_extension,
567 n,
568 extension2);
569
570 n--;
571 rename(log_filename, log_filename2);
572 }
573
574 log_filename = bctbx_strdup_printf("%s/%s",
575 filehandler->path,
576 filehandler->name);
577 log_filename2 = bctbx_strdup_printf("%s/%s_1%s",
578 filehandler->path,
579 file_no_extension,
580 extension2);
581 rename(log_filename, log_filename2);
582 bctbx_free(log_filename);
583 bctbx_free(log_filename2);
584 bctbx_free(extension2);
585 bctbx_free(file_no_extension);
586 }
587
_open_log_collection_file(bctbx_file_log_handler_t * filehandler)588 static void _open_log_collection_file(bctbx_file_log_handler_t *filehandler) {
589 if (_try_open_log_collection_file(filehandler) < 0) {
590 _rotate_log_collection_files(filehandler);
591 _try_open_log_collection_file(filehandler);
592 }
593 }
594
_close_log_collection_file(bctbx_file_log_handler_t * filehandler)595 static void _close_log_collection_file(bctbx_file_log_handler_t *filehandler) {
596 if (filehandler->file) {
597 fclose(filehandler->file);
598 filehandler->file = NULL;
599 filehandler->size = 0;
600 }
601 }
602
bctbx_logv_file(void * user_info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)603 void bctbx_logv_file(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
604 const char *lname="undef";
605 char *msg;
606 struct timeval tp;
607 struct tm *lt;
608 #ifndef _WIN32
609 struct tm tmbuf;
610 #endif
611 time_t tt;
612 int ret = -1;
613 bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) user_info;
614 FILE *f;
615 bctbx_mutex_lock(&__bctbx_logger.log_mutex);
616 f = filehandler->file;
617 bctbx_gettimeofday(&tp,NULL);
618 tt = (time_t)tp.tv_sec;
619
620 #ifdef _WIN32
621 lt = localtime(&tt);
622 #else
623 lt = localtime_r(&tt,&tmbuf);
624 #endif
625
626 if(!f) {
627 return;
628 }
629 switch(lev){
630 case BCTBX_LOG_DEBUG:
631 lname = "debug";
632 break;
633 case BCTBX_LOG_MESSAGE:
634 lname = "message";
635 break;
636 case BCTBX_LOG_WARNING:
637 lname = "warning";
638 break;
639 case BCTBX_LOG_ERROR:
640 lname = "error";
641 break;
642 case BCTBX_LOG_FATAL:
643 lname = "fatal";
644 break;
645 default:
646 lname = "badlevel";
647 }
648
649 msg=bctbx_strdup_vprintf(fmt,args);
650 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
651 #ifndef _UNICODE
652 OutputDebugStringA(msg);
653 OutputDebugStringA("\r\n");
654 #else
655 {
656 size_t len=strlen(msg);
657 wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
658 mbstowcs(tmp,msg,len);
659 OutputDebugStringW(tmp);
660 OutputDebugStringW(L"\r\n");
661 bctbx_free(tmp);
662 }
663 #endif
664 #endif
665 ret = fprintf(f,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
666 ,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
667 ,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
668 fflush(f);
669 if (filehandler->max_size > 0 && ret > 0) {
670 filehandler->size += ret;
671 if (filehandler->size > filehandler->max_size) {
672 _close_log_collection_file(filehandler);
673 _open_log_collection_file(filehandler);
674 }
675 }
676 bctbx_mutex_unlock(&__bctbx_logger.log_mutex);
677
678 bctbx_free(msg);
679 }
680
bctbx_logv_file_destroy(bctbx_log_handler_t * handler)681 void bctbx_logv_file_destroy(bctbx_log_handler_t* handler) {
682 bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) handler->user_info;
683 fclose(filehandler->file);
684 bctbx_free(filehandler->path);
685 bctbx_free(filehandler->name);
686 bctbx_logv_out_destroy(handler);
687 }
688
689 #ifdef __QNX__
690 #include <slog2.h>
691
692 static bool_t slog2_registered = FALSE;
693 static slog2_buffer_set_config_t slog2_buffer_config;
694 static slog2_buffer_t slog2_buffer_handle[2];
695
bctbx_qnx_log_handler(const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)696 void bctbx_qnx_log_handler(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
697 uint8_t severity = SLOG2_DEBUG1;
698 uint8_t buffer_idx = 1;
699 char* msg;
700
701 if (slog2_registered != TRUE) {
702 slog2_buffer_config.buffer_set_name = domain;
703 slog2_buffer_config.num_buffers = 2;
704 slog2_buffer_config.verbosity_level = SLOG2_DEBUG2;
705 slog2_buffer_config.buffer_config[0].buffer_name = "hi_rate";
706 slog2_buffer_config.buffer_config[0].num_pages = 6;
707 slog2_buffer_config.buffer_config[1].buffer_name = "lo_rate";
708 slog2_buffer_config.buffer_config[1].num_pages = 2;
709 if (slog2_register(&slog2_buffer_config, slog2_buffer_handle, 0) == 0) {
710 slog2_registered = TRUE;
711 } else {
712 fprintf(stderr, "Error registering slogger2 buffer!\n");
713 return;
714 }
715 }
716
717 switch(lev){
718 case BCTBX_LOG_DEBUG:
719 severity = SLOG2_DEBUG1;
720 break;
721 case BCTBX_LOG_MESSAGE:
722 severity = SLOG2_INFO;
723 break;
724 case BCTBX_LOG_WARNING:
725 severity = SLOG2_WARNING;
726 break;
727 case BCTBX_LOG_ERROR:
728 severity = SLOG2_ERROR;
729 break;
730 case BCTBX_LOG_FATAL:
731 severity = SLOG2_CRITICAL;
732 break;
733 default:
734 severity = SLOG2_CRITICAL;
735 }
736
737 msg = bctbx_strdup_vprintf(fmt,args);
738 slog2c(slog2_buffer_handle[buffer_idx], 0, severity, msg);
739 }
740
741 #endif /* __QNX__ */
742