logging.c 18.3 KB
Newer Older
jehan's avatar
jehan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
bctoolbox
Copyright (C) 2016  Belledonne Communications SARL


This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

21 22 23 24
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

jehan's avatar
jehan committed
25 26
#include "bctoolbox/logging.h"
#include <time.h>
Benjamin REIS's avatar
Benjamin REIS committed
27
#include <stdio.h>
28 29
#include <sys/stat.h>
#include <sys/types.h>
jehan's avatar
jehan committed
30 31 32 33 34 35 36


typedef struct{
	char *domain;
	unsigned int logmask;
}BctoolboxLogDomain;

jehan's avatar
jehan committed
37 38 39
static void bctbx_log_domain_destroy(BctoolboxLogDomain *obj){
	if (obj->domain) bctbx_free(obj->domain);
	bctbx_free(obj);
jehan's avatar
jehan committed
40 41 42
}

typedef struct _BctoolboxLogger{
Benjamin REIS's avatar
Benjamin REIS committed
43
	bctbx_list_t *logv_outs;
jehan's avatar
jehan committed
44 45
	unsigned int log_mask; /*the default log mask, if no per-domain settings are found*/
	unsigned long log_thread_id;
jehan's avatar
jehan committed
46 47 48 49
	bctbx_list_t *log_stored_messages_list;
	bctbx_list_t *log_domains;
	bctbx_mutex_t log_stored_messages_mutex;
	bctbx_mutex_t domains_mutex;
jehan's avatar
jehan committed
50 51
}BctoolboxLogger;

52 53 54 55 56 57 58 59 60 61 62 63 64 65
struct _BctoolboxLogHandler{
	BctoolboxLogHandlerFunc func;
	BctoolboxLogHandlerDestroyFunc destroy;
	void* user_info;
};

typedef struct _BctoolboxFileLogHandler{
	char* path;
	char* name;
	uint64_t max_size;
	uint64_t size;
	FILE* file;
}BctoolboxFileLogHandler;

Benjamin REIS's avatar
Benjamin REIS committed
66
static BctoolboxLogger __bctbx_logger = { NULL, BCTBX_LOG_WARNING|BCTBX_LOG_ERROR|BCTBX_LOG_FATAL, 0};
jehan's avatar
jehan committed
67

68
static unsigned int bctbx_init_logger_refcount = 0;
jehan's avatar
jehan committed
69
void bctbx_init_logger(void){
Benjamin REIS's avatar
Benjamin REIS committed
70
	BctoolboxLogHandler* handler;
71 72
	if (bctbx_init_logger_refcount++ > 0) return; /*already initialized*/
	
jehan's avatar
jehan committed
73
	bctbx_mutex_init(&__bctbx_logger.domains_mutex, NULL);
74
	handler = bctbx_create_log_handler(bctbx_logv_out, bctbx_logv_out_destroy, NULL);
Benjamin REIS's avatar
Benjamin REIS committed
75
	bctbx_add_log_handler(handler);
jehan's avatar
jehan committed
76 77
}

78 79 80 81 82 83 84 85 86
void bctbx_log_handlers_free(void) {
	bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
	while (loggers) {
		BctoolboxLogHandler* handler = (BctoolboxLogHandler*)loggers->data;
		handler->destroy(handler);
		loggers = loggers->next;
	}
}

jehan's avatar
jehan committed
87
void bctbx_uninit_logger(void){
88
	if (--bctbx_init_logger_refcount <= 0) {
89
		bctbx_logv_flush();
90
		bctbx_mutex_destroy(&__bctbx_logger.domains_mutex);
91
		bctbx_log_handlers_free();
92 93 94
		bctbx_list_free(__bctbx_logger.logv_outs);
		__bctbx_logger.log_domains = bctbx_list_free_with_data(__bctbx_logger.log_domains, (void (*)(void*))bctbx_log_domain_destroy);
	}
jehan's avatar
jehan committed
95 96
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
BctoolboxLogHandler* bctbx_create_log_handler(BctoolboxLogHandlerFunc func, BctoolboxLogHandlerDestroyFunc destroy, void* user_info) {
	BctoolboxLogHandler* handler = (BctoolboxLogHandler*)bctbx_malloc(sizeof(BctoolboxLogHandler));
	handler->func = func;
	handler->destroy = destroy;
	handler->user_info = user_info;
	return handler;
}

BctoolboxLogHandler* bctbx_create_file_log_handler(uint64_t max_size, const char* path, const char* name, FILE* f) {
	BctoolboxLogHandler* handler = (BctoolboxLogHandler*)bctbx_malloc(sizeof(BctoolboxLogHandler));
	BctoolboxFileLogHandler* filehandler = (BctoolboxFileLogHandler*)bctbx_malloc(sizeof(BctoolboxFileLogHandler));
	handler->func=bctbx_logv_file;
	handler->destroy=bctbx_logv_file_destroy;
	filehandler->max_size = max_size;
	// init with actual file size
	fseek(f, 0L, SEEK_END);
	filehandler->size = ftell(f);
	filehandler->path = bctbx_strdup(path);
	filehandler->name = bctbx_strdup(name);
	filehandler->file = f;
	handler->user_info=(void*) filehandler;
	return handler;
}

jehan's avatar
jehan committed
121 122 123 124
/**
*@param func: your logging function, compatible with the BctoolboxLogFunc prototype.
*
**/
Benjamin REIS's avatar
Benjamin REIS committed
125
void bctbx_add_log_handler(BctoolboxLogHandler* handler){
126 127 128
	if (!bctbx_list_find(__bctbx_logger.logv_outs, handler))
		__bctbx_logger.logv_outs = bctbx_list_append(__bctbx_logger.logv_outs, (void*)handler);
	/*else, already in*/
jehan's avatar
jehan committed
129 130
}

131
static void wrapper(void* info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
Benjamin REIS's avatar
Benjamin REIS committed
132
	BctoolboxLogFunc func = (BctoolboxLogFunc)info;
133 134 135
	func(domain, lev, fmt,  args);
}

136
void bctbx_set_log_handler(BctoolboxLogFunc func){
137 138
	static BctoolboxLogHandler handler;
	handler.func=wrapper;
139
	handler.destroy=(BctoolboxLogHandlerDestroyFunc)bctbx_logv_out_destroy;
140 141
	handler.user_info=(void*)func;
	bctbx_add_log_handler(&handler);
142 143 144
}

void bctbx_set_log_file(FILE* f){
145
	static BctoolboxFileLogHandler filehandler;
146 147
	static BctoolboxLogHandler handler;
	handler.func=bctbx_logv_file;
148
	handler.destroy=(BctoolboxLogHandlerDestroyFunc)bctbx_logv_file_destroy;
149 150 151
	filehandler.max_size = -1;
	filehandler.file = f;
	handler.user_info=(void*) &filehandler;
152
	bctbx_add_log_handler(&handler);
153 154
}

Benjamin REIS's avatar
Benjamin REIS committed
155 156
bctbx_list_t* bctbx_get_log_handlers(void){
	return __bctbx_logger.logv_outs;
jehan's avatar
jehan committed
157 158 159
}

static BctoolboxLogDomain * get_log_domain(const char *domain){
jehan's avatar
jehan committed
160
	bctbx_list_t *it;
Erwan Croze's avatar
Erwan Croze committed
161

jehan's avatar
jehan committed
162
	if (domain == NULL) return NULL;
jehan's avatar
jehan committed
163 164
	for (it = __bctbx_logger.log_domains; it != NULL; it = bctbx_list_next(it)) {
		BctoolboxLogDomain *ld = (BctoolboxLogDomain*)bctbx_list_get_data(it);
jehan's avatar
jehan committed
165 166 167 168 169 170 171 172 173
		if (ld->domain && strcmp(ld->domain, domain) == 0 ){
			return ld;
		}
	}
	return NULL;
}

static BctoolboxLogDomain *get_log_domain_rw(const char *domain){
	BctoolboxLogDomain *ret;
Erwan Croze's avatar
Erwan Croze committed
174

jehan's avatar
jehan committed
175 176 177 178
	if (domain == NULL) return NULL;
	ret = get_log_domain(domain);
	if (ret) return ret;
	/*it does not exist, hence create it by taking the mutex*/
jehan's avatar
jehan committed
179
	bctbx_mutex_lock(&__bctbx_logger.domains_mutex);
jehan's avatar
jehan committed
180 181
	ret = get_log_domain(domain);
	if (!ret){
jehan's avatar
jehan committed
182 183 184 185
		ret = bctbx_new0(BctoolboxLogDomain,1);
		ret->domain = bctbx_strdup(domain);
		ret->logmask = __bctbx_logger.log_mask;
		__bctbx_logger.log_domains = bctbx_list_prepend(__bctbx_logger.log_domains, ret);
jehan's avatar
jehan committed
186
	}
jehan's avatar
jehan committed
187
	bctbx_mutex_unlock(&__bctbx_logger.domains_mutex);
jehan's avatar
jehan committed
188 189 190 191
	return ret;
}

/**
jehan's avatar
jehan committed
192 193
* @ param levelmask a mask of BCTBX_DEBUG, BCTBX_MESSAGE, BCTBX_WARNING, BCTBX_ERROR
* BCTBX_FATAL .
jehan's avatar
jehan committed
194
**/
jehan's avatar
jehan committed
195 196
void bctbx_set_log_level_mask(const char *domain, int levelmask){
	if (domain == NULL) __bctbx_logger.log_mask=levelmask;
jehan's avatar
jehan committed
197 198 199 200
	else get_log_domain_rw(domain)->logmask = levelmask;
}


jehan's avatar
jehan committed
201 202 203 204
void bctbx_set_log_level(const char *domain, BctbxLogLevel level){
	int levelmask = BCTBX_LOG_FATAL;
	if (level<=BCTBX_LOG_ERROR){
		levelmask |= BCTBX_LOG_ERROR;
jehan's avatar
jehan committed
205
	}
jehan's avatar
jehan committed
206 207
	if (level<=BCTBX_LOG_WARNING){
		levelmask |= BCTBX_LOG_WARNING;
jehan's avatar
jehan committed
208
	}
jehan's avatar
jehan committed
209 210
	if (level<=BCTBX_LOG_MESSAGE){
		levelmask |= BCTBX_LOG_MESSAGE;
jehan's avatar
jehan committed
211
	}
jehan's avatar
jehan committed
212 213
	if (level<=BCTBX_LOG_TRACE)	{
		levelmask |= BCTBX_LOG_TRACE;
jehan's avatar
jehan committed
214
	}
jehan's avatar
jehan committed
215 216
	if (level<=BCTBX_LOG_DEBUG){
		levelmask |= BCTBX_LOG_DEBUG;
jehan's avatar
jehan committed
217
	}
jehan's avatar
jehan committed
218
	bctbx_set_log_level_mask(domain, levelmask);
jehan's avatar
jehan committed
219 220
}

jehan's avatar
jehan committed
221
unsigned int bctbx_get_log_level_mask(const char *domain) {
jehan's avatar
jehan committed
222
	BctoolboxLogDomain *ld;
jehan's avatar
jehan committed
223
	if (domain == NULL || (ld = get_log_domain(domain)) == NULL) return __bctbx_logger.log_mask;
jehan's avatar
jehan committed
224 225 226
	else return ld->logmask;
}

jehan's avatar
jehan committed
227
void bctbx_set_log_thread_id(unsigned long thread_id) {
jehan's avatar
jehan committed
228
	if (thread_id == 0) {
jehan's avatar
jehan committed
229 230
		bctbx_logv_flush();
		bctbx_mutex_destroy(&__bctbx_logger.log_stored_messages_mutex);
jehan's avatar
jehan committed
231
	} else {
jehan's avatar
jehan committed
232
		bctbx_mutex_init(&__bctbx_logger.log_stored_messages_mutex, NULL);
jehan's avatar
jehan committed
233
	}
jehan's avatar
jehan committed
234
	__bctbx_logger.log_thread_id = thread_id;
jehan's avatar
jehan committed
235 236
}

jehan's avatar
jehan committed
237
char * bctbx_strdup_vprintf(const char *fmt, va_list ap)
jehan's avatar
jehan committed
238 239 240 241 242 243 244
{
/* Guess we need no more than 100 bytes. */
	int n, size = 200;
	char *p,*np;
#ifndef _WIN32
	va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
#endif
jehan's avatar
jehan committed
245
	if ((p = (char *) bctbx_malloc (size)) == NULL)
jehan's avatar
jehan committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
		return NULL;
	while (1){
/* Try to print in the allocated space. */
#ifndef _WIN32
		va_copy(cap,ap);
		n = vsnprintf (p, size, fmt, cap);
		va_end(cap);
#else
		/*this works on 32 bits, luckily*/
		n = vsnprintf (p, size, fmt, ap);
#endif
		/* If that worked, return the string. */
		if (n > -1 && n < size)
			return p;
//printf("Reallocing space.\n");
		/* Else try again with more space. */
		if (n > -1)	/* glibc 2.1 */
			size = n + 1;	/* precisely what is needed */
		else		/* glibc 2.0 */
			size *= 2;	/* twice the old size */
jehan's avatar
jehan committed
266
		if ((np = (char *) bctbx_realloc (p, size)) == NULL)
jehan's avatar
jehan committed
267 268 269 270 271 272 273 274 275
		{
			free(p);
			return NULL;
		} else {
			p = np;
		}
	}
}

jehan's avatar
jehan committed
276
char *bctbx_strdup_printf(const char *fmt,...){
jehan's avatar
jehan committed
277 278 279
	char *ret;
	va_list args;
	va_start (args, fmt);
jehan's avatar
jehan committed
280
	ret=bctbx_strdup_vprintf(fmt, args);
jehan's avatar
jehan committed
281 282 283 284
	va_end (args);
	return ret;
}

jehan's avatar
jehan committed
285
char * bctbx_strcat_vprintf(char* dst, const char *fmt, va_list ap){
jehan's avatar
jehan committed
286 287 288
	char *ret;
	size_t dstlen, retlen;

jehan's avatar
jehan committed
289
	ret=bctbx_strdup_vprintf(fmt, ap);
jehan's avatar
jehan committed
290 291 292 293 294
	if (!dst) return ret;

	dstlen = strlen(dst);
	retlen = strlen(ret);

jehan's avatar
jehan committed
295
	if ((dst = bctbx_realloc(dst, dstlen+retlen+1)) != NULL){
jehan's avatar
jehan committed
296 297
		strncat(dst,ret,retlen);
		dst[dstlen+retlen] = '\0';
jehan's avatar
jehan committed
298
		bctbx_free(ret);
jehan's avatar
jehan committed
299 300
		return dst;
	} else {
jehan's avatar
jehan committed
301
		bctbx_free(ret);
jehan's avatar
jehan committed
302 303 304 305
		return NULL;
	}
}

jehan's avatar
jehan committed
306
char *bctbx_strcat_printf(char* dst, const char *fmt,...){
jehan's avatar
jehan committed
307 308 309
	char *ret;
	va_list args;
	va_start (args, fmt);
jehan's avatar
jehan committed
310
	ret=bctbx_strcat_vprintf(dst, fmt, args);
jehan's avatar
jehan committed
311 312 313 314 315 316 317 318 319 320 321 322 323 324
	va_end (args);
	return ret;
}

#if	defined(_WIN32) || defined(_WIN32_WCE)
#define ENDLINE "\r\n"
#else
#define ENDLINE "\n"
#endif

typedef struct {
	int level;
	char *msg;
	char *domain;
jehan's avatar
jehan committed
325
} bctbx_stored_log_t;
jehan's avatar
jehan committed
326

jehan's avatar
jehan committed
327 328 329
void _bctbx_logv_flush(int dummy, ...) {
	bctbx_list_t *elem;
	bctbx_list_t *msglist;
jehan's avatar
jehan committed
330 331
	va_list empty_va_list;
	va_start(empty_va_list, dummy);
jehan's avatar
jehan committed
332 333 334 335 336 337
	bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
	msglist = __bctbx_logger.log_stored_messages_list;
	__bctbx_logger.log_stored_messages_list = NULL;
	bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
	for (elem = msglist; elem != NULL; elem = bctbx_list_next(elem)) {
		bctbx_stored_log_t *l = (bctbx_stored_log_t *)bctbx_list_get_data(elem);
Benjamin REIS's avatar
Benjamin REIS committed
338
		bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
jehan's avatar
jehan committed
339
#ifdef _WIN32
Benjamin REIS's avatar
Benjamin REIS committed
340 341 342 343 344
		while (loggers) {
			BctoolboxLogHandler* handler = (BctoolboxLogHandler*)loggers->data;
			handler->func(handler->user_info, l->domain, l->level, l->msg, empty_va_list);
			loggers = loggers->next;
		}
jehan's avatar
jehan committed
345 346 347
#else
		va_list cap;
		va_copy(cap, empty_va_list);
Benjamin REIS's avatar
Benjamin REIS committed
348 349 350 351 352
		while (loggers) {
			BctoolboxLogHandler* handler = (BctoolboxLogHandler*)loggers->data;
			handler->func(handler->user_info, l->domain, l->level, l->msg, cap);
			loggers = loggers->next;
		}
jehan's avatar
jehan committed
353 354
		va_end(cap);
#endif
jehan's avatar
jehan committed
355 356 357
		if (l->domain) bctbx_free(l->domain);
		bctbx_free(l->msg);
		bctbx_free(l);
jehan's avatar
jehan committed
358
	}
jehan's avatar
jehan committed
359
	bctbx_list_free(msglist);
jehan's avatar
jehan committed
360 361 362
	va_end(empty_va_list);
}

jehan's avatar
jehan committed
363 364
void bctbx_logv_flush(void) {
	_bctbx_logv_flush(0);
jehan's avatar
jehan committed
365 366
}

jehan's avatar
jehan committed
367
void bctbx_logv(const char *domain, BctbxLogLevel level, const char *fmt, va_list args) {
Benjamin REIS's avatar
Benjamin REIS committed
368
	if ((__bctbx_logger.logv_outs != NULL) && bctbx_log_level_enabled(domain, level)) {
jehan's avatar
jehan committed
369
		if (__bctbx_logger.log_thread_id == 0) {
Benjamin REIS's avatar
Benjamin REIS committed
370 371 372 373 374 375
			bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
			while (loggers) {
				BctoolboxLogHandler* handler = (BctoolboxLogHandler*)loggers->data;
				handler->func(handler->user_info, domain, level, fmt, args);
				loggers = loggers->next;
			}
jehan's avatar
jehan committed
376
		} else if (__bctbx_logger.log_thread_id == bctbx_thread_self()) {
Benjamin REIS's avatar
Benjamin REIS committed
377
			bctbx_list_t *loggers;
jehan's avatar
jehan committed
378
			bctbx_logv_flush();
Benjamin REIS's avatar
Benjamin REIS committed
379
			loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
Benjamin REIS's avatar
Benjamin REIS committed
380 381 382 383 384
			while (loggers) {
				BctoolboxLogHandler* handler = (BctoolboxLogHandler*)loggers->data;
				handler->func(handler->user_info, domain, level, fmt, args);
				loggers = loggers->next;
			}
jehan's avatar
jehan committed
385
		} else {
jehan's avatar
jehan committed
386 387
			bctbx_stored_log_t *l = bctbx_new(bctbx_stored_log_t, 1);
			l->domain = domain ? bctbx_strdup(domain) : NULL;
jehan's avatar
jehan committed
388
			l->level = level;
jehan's avatar
jehan committed
389 390 391 392
			l->msg = bctbx_strdup_vprintf(fmt, args);
			bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
			__bctbx_logger.log_stored_messages_list = bctbx_list_append(__bctbx_logger.log_stored_messages_list, l);
			bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
jehan's avatar
jehan committed
393 394 395
		}
	}
#if !defined(_WIN32_WCE)
jehan's avatar
jehan committed
396 397
	if (level == BCTBX_LOG_FATAL) {
		bctbx_logv_flush();
jehan's avatar
jehan committed
398 399 400 401 402 403
		abort();
	}
#endif
}

/*This function does the default formatting and output to file*/
Benjamin REIS's avatar
Benjamin REIS committed
404 405 406 407 408 409 410 411 412
void bctbx_logv_out(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
	const char *lname="undef";
	char *msg;
	struct timeval tp;
	struct tm *lt;
#ifndef _WIN32
	struct tm tmbuf;
#endif
	time_t tt;
Benjamin REIS's avatar
Benjamin REIS committed
413
	FILE *std = stdout;
Benjamin REIS's avatar
Benjamin REIS committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
	bctbx_gettimeofday(&tp,NULL);
	tt = (time_t)tp.tv_sec;

#ifdef _WIN32
	lt = localtime(&tt);
#else
	lt = localtime_r(&tt,&tmbuf);
#endif

	switch(lev){
		case BCTBX_LOG_DEBUG:
			lname = "debug";
		break;
		case BCTBX_LOG_MESSAGE:
			lname = "message";
		break;
		case BCTBX_LOG_WARNING:
			lname = "warning";
		break;
		case BCTBX_LOG_ERROR:
			lname = "error";
			std = stderr;
		break;
		case BCTBX_LOG_FATAL:
			lname = "fatal";
			std = stderr;
		break;
		default:
			lname = "badlevel";
	}
Erwan Croze's avatar
Erwan Croze committed
444

Benjamin REIS's avatar
Benjamin REIS committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
	msg=bctbx_strdup_vprintf(fmt,args);
#if defined(_MSC_VER) && !defined(_WIN32_WCE)
#ifndef _UNICODE
	OutputDebugStringA(msg);
	OutputDebugStringA("\r\n");
#else
	{
		size_t len=strlen(msg);
		wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
		mbstowcs(tmp,msg,len);
		OutputDebugStringW(tmp);
		OutputDebugStringW(L"\r\n");
		bctbx_free(tmp);
	}
#endif
#endif
	fprintf(std,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
			,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
		,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
	fflush(std);
	bctbx_free(msg);
}

468 469 470 471
void bctbx_logv_out_destroy(BctoolboxLogHandler* handler) {
	handler->user_info=NULL;
}

472 473 474 475 476 477 478 479 480 481 482 483
static int _try_open_log_collection_file(BctoolboxFileLogHandler* filehandler) {
	struct stat statbuf;
	char *log_filename;

	log_filename = bctbx_strdup_printf("%s/%s.log",
		filehandler->path,
		filehandler->name);
	filehandler->file = fopen(log_filename, "a");
	bctbx_free(log_filename);
	if (filehandler->file == NULL) return -1;

	fstat(fileno(filehandler->file), &statbuf);
484
	if ((uint64_t)statbuf.st_size > filehandler->max_size) {
485 486 487 488 489 490 491 492 493 494 495 496 497
		fclose(filehandler->file);
		return -1;
	}

	filehandler->size = statbuf.st_size;
	return 0;
}

static void _rotate_log_collection_files(BctoolboxFileLogHandler* filehandler) {
	char *log_filename;
	char *log_filename2;
	int n = 1;

498
	log_filename = bctbx_strdup_printf("%s/%s_1.log",
499 500 501 502 503
		filehandler->path,
		filehandler->name);
	while(access(log_filename, F_OK) != -1) {
    // file exists
		n++;
504
		log_filename = bctbx_strdup_printf("%s/%s_%d.log",
505 506 507 508 509
		filehandler->path,
		filehandler->name,
		n);
	}

510 511
	while(n > 1) {
		log_filename = bctbx_strdup_printf("%s/%s_%d.log",
512 513 514
		filehandler->path,
		filehandler->name,
		n-1);
515
		log_filename2 = bctbx_strdup_printf("%s/%s_%d.log",
516 517 518 519 520 521 522 523 524 525 526
		filehandler->path,
		filehandler->name,
		n);

		n--;
		rename(log_filename, log_filename2);
	}

	log_filename = bctbx_strdup_printf("%s/%s.log",
	filehandler->path,
	filehandler->name);
527
	log_filename2 = bctbx_strdup_printf("%s/%s_1.log",
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
	filehandler->path,
	filehandler->name);
	rename(log_filename, log_filename2);
	bctbx_free(log_filename);
	bctbx_free(log_filename2);
}

static void _open_log_collection_file(BctoolboxFileLogHandler* filehandler) {
	if (_try_open_log_collection_file(filehandler) < 0) {
		_rotate_log_collection_files(filehandler);
		_try_open_log_collection_file(filehandler);
	}
}

static void _close_log_collection_file(BctoolboxFileLogHandler* filehandler) {
	if (filehandler->file) {
		fclose(filehandler->file);
		filehandler->file = NULL;
		filehandler->size = 0;
	}
}

Benjamin REIS's avatar
Benjamin REIS committed
550
void bctbx_logv_file(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
jehan's avatar
jehan committed
551 552 553 554 555 556 557 558
	const char *lname="undef";
	char *msg;
	struct timeval tp;
	struct tm *lt;
#ifndef _WIN32
	struct tm tmbuf;
#endif
	time_t tt;
559 560 561
	int ret = -1;
	BctoolboxFileLogHandler* filehandler = (BctoolboxFileLogHandler *) user_info;
	FILE *f = filehandler->file;
jehan's avatar
jehan committed
562
	bctbx_gettimeofday(&tp,NULL);
jehan's avatar
jehan committed
563 564 565 566 567 568 569 570
	tt = (time_t)tp.tv_sec;

#ifdef _WIN32
	lt = localtime(&tt);
#else
	lt = localtime_r(&tt,&tmbuf);
#endif

Benjamin REIS's avatar
Benjamin REIS committed
571 572 573
	if(!f) {
		return;
	}
jehan's avatar
jehan committed
574
	switch(lev){
jehan's avatar
jehan committed
575
		case BCTBX_LOG_DEBUG:
jehan's avatar
jehan committed
576 577
			lname = "debug";
		break;
jehan's avatar
jehan committed
578
		case BCTBX_LOG_MESSAGE:
jehan's avatar
jehan committed
579 580
			lname = "message";
		break;
jehan's avatar
jehan committed
581
		case BCTBX_LOG_WARNING:
jehan's avatar
jehan committed
582 583
			lname = "warning";
		break;
jehan's avatar
jehan committed
584
		case BCTBX_LOG_ERROR:
jehan's avatar
jehan committed
585 586
			lname = "error";
		break;
jehan's avatar
jehan committed
587
		case BCTBX_LOG_FATAL:
jehan's avatar
jehan committed
588 589 590 591 592
			lname = "fatal";
		break;
		default:
			lname = "badlevel";
	}
Erwan Croze's avatar
Erwan Croze committed
593

jehan's avatar
jehan committed
594
	msg=bctbx_strdup_vprintf(fmt,args);
jehan's avatar
jehan committed
595 596 597 598 599 600 601
#if defined(_MSC_VER) && !defined(_WIN32_WCE)
#ifndef _UNICODE
	OutputDebugStringA(msg);
	OutputDebugStringA("\r\n");
#else
	{
		size_t len=strlen(msg);
jehan's avatar
jehan committed
602
		wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
jehan's avatar
jehan committed
603 604 605
		mbstowcs(tmp,msg,len);
		OutputDebugStringW(tmp);
		OutputDebugStringW(L"\r\n");
jehan's avatar
jehan committed
606
		bctbx_free(tmp);
jehan's avatar
jehan committed
607 608 609
	}
#endif
#endif
610
	ret = fprintf(f,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
jehan's avatar
jehan committed
611 612
			,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
		,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
Benjamin REIS's avatar
Benjamin REIS committed
613
	fflush(f);
614
	if (filehandler->max_size > 0 && ret > 0) {
615 616 617 618 619 620 621
		filehandler->size += ret;
		if (filehandler->size > filehandler->max_size) {
			_close_log_collection_file(filehandler);
			_open_log_collection_file(filehandler);
		}
	}

jehan's avatar
jehan committed
622
	bctbx_free(msg);
jehan's avatar
jehan committed
623 624
}

625 626 627 628 629 630 631 632
void bctbx_logv_file_destroy(BctoolboxLogHandler* handler) {
	BctoolboxFileLogHandler* filehandler = (BctoolboxFileLogHandler *) handler->user_info;
	fclose(filehandler->file);
	bctbx_free(filehandler->path);
	bctbx_free(filehandler->name);
	bctbx_logv_out_destroy(handler);
}

jehan's avatar
jehan committed
633 634 635 636 637 638 639
#ifdef __QNX__
#include <slog2.h>

static bool_t slog2_registered = FALSE;
static slog2_buffer_set_config_t slog2_buffer_config;
static slog2_buffer_t slog2_buffer_handle[2];

Ghislain MARY's avatar
Ghislain MARY committed
640
void bctbx_qnx_log_handler(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
jehan's avatar
jehan committed
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
	uint8_t severity = SLOG2_DEBUG1;
	uint8_t buffer_idx = 1;
	char* msg;

	if (slog2_registered != TRUE) {
		slog2_buffer_config.buffer_set_name = domain;
		slog2_buffer_config.num_buffers = 2;
		slog2_buffer_config.verbosity_level = SLOG2_DEBUG2;
		slog2_buffer_config.buffer_config[0].buffer_name = "hi_rate";
		slog2_buffer_config.buffer_config[0].num_pages = 6;
		slog2_buffer_config.buffer_config[1].buffer_name = "lo_rate";
		slog2_buffer_config.buffer_config[1].num_pages = 2;
		if (slog2_register(&slog2_buffer_config, slog2_buffer_handle, 0) == 0) {
			slog2_registered = TRUE;
		} else {
			fprintf(stderr, "Error registering slogger2 buffer!\n");
			return;
		}
	}

	switch(lev){
jehan's avatar
jehan committed
662
		case BCTBX_LOG_DEBUG:
jehan's avatar
jehan committed
663 664
			severity = SLOG2_DEBUG1;
		break;
jehan's avatar
jehan committed
665
		case BCTBX_LOG_MESSAGE:
jehan's avatar
jehan committed
666 667
			severity = SLOG2_INFO;
		break;
jehan's avatar
jehan committed
668
		case BCTBX_LOG_WARNING:
jehan's avatar
jehan committed
669 670
			severity = SLOG2_WARNING;
		break;
jehan's avatar
jehan committed
671
		case BCTBX_LOG_ERROR:
jehan's avatar
jehan committed
672 673
			severity = SLOG2_ERROR;
		break;
jehan's avatar
jehan committed
674
		case BCTBX_LOG_FATAL:
jehan's avatar
jehan committed
675 676 677 678 679 680
			severity = SLOG2_CRITICAL;
		break;
		default:
			severity = SLOG2_CRITICAL;
	}

jehan's avatar
jehan committed
681
	msg = bctbx_strdup_vprintf(fmt,args);
jehan's avatar
jehan committed
682 683
	slog2c(slog2_buffer_handle[buffer_idx], 0, severity, msg);
}
684

jehan's avatar
jehan committed
685
#endif /* __QNX__ */