logging.c 12.7 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) implementation with additional features.
 * Copyright (C) 2017 Belledonne Communications SARL
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#include "ortp/utils.h"
#include "ortp/logging.h"


static bctbx_log_handler_t * __ortp_logger = NULL;
static OrtpLogFunc __ortp_log_func = bctbx_logv_out;
static unsigned int ortp_init_logger_refcount = 0;

static void wrapper(void* info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
	BctbxLogFunc func = (BctbxLogFunc)info;
	if (func) func(domain, lev, fmt,  args);
}

void ortp_init_logger(void){
	if (ortp_init_logger_refcount++ > 0) return; /*already initialized*/
	bctbx_init_logger(FALSE);
	__ortp_logger = bctbx_create_log_handler(wrapper, bctbx_logv_out_destroy, __ortp_log_func);
	bctbx_log_handler_set_domain(__ortp_logger, ORTP_LOG_DOMAIN);
	bctbx_add_log_handler(__ortp_logger);
	
}

void ortp_uninit_logger(void){
	if (--ortp_init_logger_refcount <= 0) {
		bctbx_remove_log_handler(__ortp_logger);
		bctbx_uninit_logger();
	}
}

void ortp_set_log_handler(OrtpLogFunc func){
	__ortp_log_func = func ;
	if (__ortp_logger)
		bctbx_log_handler_set_user_data(__ortp_logger,func);
}
OrtpLogFunc ortp_get_log_handler(void){
	return __ortp_log_func;
}
/**
 *@param file a FILE pointer where to output the ortp logs.
 *
 **/
void ortp_set_log_file(FILE *file){
	if (!__ortp_logger) {
		ortp_init_logger();
		ortp_error("ortp_init_logger must be call before ortp_set_log_file, please fix it!");
	}

	/*first remove log handler*/
	bctbx_remove_log_handler(__ortp_logger);
	
	if (file) {
		__ortp_logger = bctbx_create_file_log_handler(0, "unknown","unknown", file);
		
	} else {
		/*restaure default loguer*/
		__ortp_logger = bctbx_create_log_handler(wrapper, bctbx_logv_out_destroy, ortp_logv_out);
	}
	bctbx_log_handler_set_domain(__ortp_logger, ORTP_LOG_DOMAIN);
	bctbx_add_log_handler(__ortp_logger);

	
}


#if 0
85 86 87
#ifdef HAVE_CONFIG_H
#include "ortp-config.h"
#endif
88 89

#include "ortp/logging.h"
90
#include "utils.h"
jehan's avatar
jehan committed
91
#include <time.h>
92

93 94 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 121 122 123 124 125

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

static void ortp_log_domain_destroy(OrtpLogDomain *obj){
	if (obj->domain) ortp_free(obj->domain);
	ortp_free(obj);
}

typedef struct _OrtpLogger{
	OrtpLogFunc logv_out;
	unsigned int log_mask; /*the default log mask, if no per-domain settings are found*/
	FILE *log_file;
	unsigned long log_thread_id;
	OList *log_stored_messages_list;
	OList *log_domains;
	ortp_mutex_t log_stored_messages_mutex;
	ortp_mutex_t domains_mutex;
}OrtpLogger;


static OrtpLogger __ortp_logger = { &ortp_logv_out, ORTP_WARNING|ORTP_ERROR|ORTP_FATAL, 0};

void ortp_init_logger(void){
	ortp_mutex_init(&__ortp_logger.domains_mutex, NULL);
}

void ortp_uninit_logger(void){
	ortp_mutex_destroy(&__ortp_logger.domains_mutex);
	__ortp_logger.log_domains = o_list_free_with_data(__ortp_logger.log_domains, (void (*)(void*))ortp_log_domain_destroy);
}
126 127

/**
128 129
*@param file a FILE pointer where to output the ortp logs.
*
130 131 132
**/
void ortp_set_log_file(FILE *file)
{
133
	__ortp_logger.log_file=file;
134 135 136
}

/**
137 138
*@param func: your logging function, compatible with the OrtpLogFunc prototype.
*
139 140
**/
void ortp_set_log_handler(OrtpLogFunc func){
141
	__ortp_logger.logv_out=func;
142
}
143 144 145

OrtpLogFunc ortp_get_log_handler(void){
	return __ortp_logger.logv_out;
146
}
147

148 149
static OrtpLogDomain * get_log_domain(const char *domain){
	OList *it;
150

151 152 153 154 155 156 157 158 159
	if (domain == NULL) return NULL;
	for (it = __ortp_logger.log_domains; it != NULL; it = it->next){
		OrtpLogDomain *ld = (OrtpLogDomain*)it->data;
		if (ld->domain && strcmp(ld->domain, domain) == 0 ){
			return ld;
		}
	}
	return NULL;
}
160

161 162
static OrtpLogDomain *get_log_domain_rw(const char *domain){
	OrtpLogDomain *ret;
163

164 165 166 167 168 169 170 171 172 173 174 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*/
	ortp_mutex_lock(&__ortp_logger.domains_mutex);
	ret = get_log_domain(domain);
	if (!ret){
		ret = ortp_new0(OrtpLogDomain,1);
		ret->domain = ortp_strdup(domain);
		ret->logmask = __ortp_logger.log_mask;
		__ortp_logger.log_domains = o_list_prepend(__ortp_logger.log_domains, ret);
	}
	ortp_mutex_unlock(&__ortp_logger.domains_mutex);
	return ret;
}
179 180

/**
181 182
* @ param levelmask a mask of ORTP_DEBUG, ORTP_MESSAGE, ORTP_WARNING, ORTP_ERROR
* ORTP_FATAL .
183
**/
184 185 186
void ortp_set_log_level_mask(const char *domain, int levelmask){
	if (domain == NULL) __ortp_logger.log_mask=levelmask;
	else get_log_domain_rw(domain)->logmask = levelmask;
187 188
}

Simon Morlat's avatar
Simon Morlat committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209

void ortp_set_log_level(const char *domain, OrtpLogLevel level){
	int levelmask = ORTP_FATAL;
	if (level<=ORTP_ERROR){
		levelmask |= ORTP_ERROR;
	}
	if (level<=ORTP_WARNING){
		levelmask |= ORTP_WARNING;
	}
	if (level<=ORTP_MESSAGE){
		levelmask |= ORTP_MESSAGE;
	}
	if (level<=ORTP_TRACE){
		levelmask |= ORTP_TRACE;
	}
	if (level<=ORTP_DEBUG){
		levelmask |= ORTP_DEBUG;
	}
	ortp_set_log_level_mask(domain, levelmask);
}

210 211 212 213
unsigned int ortp_get_log_level_mask(const char *domain) {
	OrtpLogDomain *ld;
	if (domain == NULL || (ld = get_log_domain(domain)) == NULL) return __ortp_logger.log_mask;
	else return ld->logmask;
214 215
}

216 217 218
void ortp_set_log_thread_id(unsigned long thread_id) {
	if (thread_id == 0) {
		ortp_logv_flush();
219
		ortp_mutex_destroy(&__ortp_logger.log_stored_messages_mutex);
220
	} else {
221
		ortp_mutex_init(&__ortp_logger.log_stored_messages_mutex, NULL);
222
	}
223
	__ortp_logger.log_thread_id = thread_id;
224 225
}

226 227
char * ortp_strdup_vprintf(const char *fmt, va_list ap)
{
228
/* Guess we need no more than 100 bytes. */
229 230
	int n, size = 200;
	char *p,*np;
231
#ifndef _WIN32
232
	va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
233 234 235
#endif
	if ((p = (char *) ortp_malloc (size)) == NULL)
		return NULL;
236
	while (1){
237
/* Try to print in the allocated space. */
238
#ifndef _WIN32
239 240 241 242
		va_copy(cap,ap);
		n = vsnprintf (p, size, fmt, cap);
		va_end(cap);
#else
243
		/*this works on 32 bits, luckily*/
244 245
		n = vsnprintf (p, size, fmt, ap);
#endif
246
		/* If that worked, return the string. */
247 248
		if (n > -1 && n < size)
			return p;
249
//printf("Reallocing space.\n");
250 251 252 253 254
		/* 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 */
255
		if ((np = (char *) ortp_realloc (p, size)) == NULL)
256
		{
257 258
			free(p);
			return NULL;
259
		} else {
260
			p = np;
261
		}
262 263 264 265 266 267 268 269 270 271 272 273
	}
}

char *ortp_strdup_printf(const char *fmt,...){
	char *ret;
	va_list args;
	va_start (args, fmt);
	ret=ortp_strdup_vprintf(fmt, args);
	va_end (args);
	return ret;
}

274 275
char * ortp_strcat_vprintf(char* dst, const char *fmt, va_list ap){
	char *ret;
276
	size_t dstlen, retlen;
277

278
	ret=ortp_strdup_vprintf(fmt, ap);
279
	if (!dst) return ret;
280

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
281 282
	dstlen = strlen(dst);
	retlen = strlen(ret);
283 284

	if ((dst = ortp_realloc(dst, dstlen+retlen+1)) != NULL){
285
		strcat(dst,ret);
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
		ortp_free(ret);
		return dst;
	} else {
		ortp_free(ret);
		return NULL;
	}
}

char *ortp_strcat_printf(char* dst, const char *fmt,...){
	char *ret;
	va_list args;
	va_start (args, fmt);
	ret=ortp_strcat_vprintf(dst, fmt, args);
	va_end (args);
	return ret;
}

303
#if	defined(_WIN32) || defined(_WIN32_WCE)
304 305 306 307 308
#define ENDLINE "\r\n"
#else
#define ENDLINE "\n"
#endif

309 310 311
typedef struct {
	int level;
	char *msg;
312
	char *domain;
313 314 315 316
} ortp_stored_log_t;

void _ortp_logv_flush(int dummy, ...) {
	OList *elem;
317
	OList *msglist;
318 319
	va_list empty_va_list;
	va_start(empty_va_list, dummy);
320 321 322 323
	ortp_mutex_lock(&__ortp_logger.log_stored_messages_mutex);
	msglist = __ortp_logger.log_stored_messages_list;
	__ortp_logger.log_stored_messages_list = NULL;
	ortp_mutex_unlock(&__ortp_logger.log_stored_messages_mutex);
324
	for (elem = msglist; elem != NULL; elem = o_list_next(elem)) {
325
		ortp_stored_log_t *l = (ortp_stored_log_t *)elem->data;
326
#ifdef _WIN32
327
		__ortp_logger.logv_out(l->domain, l->level, l->msg, empty_va_list);
328
#else
329 330
		va_list cap;
		va_copy(cap, empty_va_list);
331
		__ortp_logger.logv_out(l->domain, l->level, l->msg, cap);
332
		va_end(cap);
333
#endif
334
		if (l->domain) ortp_free(l->domain);
335 336 337
		ortp_free(l->msg);
		ortp_free(l);
	}
338
	o_list_free(msglist);
339 340 341 342 343 344 345
	va_end(empty_va_list);
}

void ortp_logv_flush(void) {
	_ortp_logv_flush(0);
}

346 347 348 349 350
void ortp_logv(const char *domain, OrtpLogLevel level, const char *fmt, va_list args) {
	if ((__ortp_logger.logv_out != NULL) && ortp_log_level_enabled(domain, level)) {
		if (__ortp_logger.log_thread_id == 0) {
			__ortp_logger.logv_out(domain, level, fmt, args);
		} else if (__ortp_logger.log_thread_id == ortp_thread_self()) {
351
			ortp_logv_flush();
352
			__ortp_logger.logv_out(domain, level, fmt, args);
353 354
		} else {
			ortp_stored_log_t *l = ortp_new(ortp_stored_log_t, 1);
355
			l->domain = domain ? ortp_strdup(domain) : NULL;
356 357
			l->level = level;
			l->msg = ortp_strdup_vprintf(fmt, args);
358 359 360
			ortp_mutex_lock(&__ortp_logger.log_stored_messages_mutex);
			__ortp_logger.log_stored_messages_list = o_list_append(__ortp_logger.log_stored_messages_list, l);
			ortp_mutex_unlock(&__ortp_logger.log_stored_messages_mutex);
361 362
		}
	}
363
#if !defined(_WIN32_WCE)
364 365 366 367
	if (level == ORTP_FATAL) {
		ortp_logv_flush();
		abort();
	}
368 369 370
#endif
}

371 372
/*This function does the default formatting and output to file*/
void ortp_logv_out(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args){
373 374
	const char *lname="undef";
	char *msg;
375
	struct timeval tp;
Simon Morlat's avatar
Simon Morlat committed
376
	struct tm *lt;
377
#ifndef _WIN32
Simon Morlat's avatar
Simon Morlat committed
378 379
	struct tm tmbuf;
#endif
380
	time_t tt;
jehan's avatar
jehan committed
381
	ortp_gettimeofday(&tp,NULL);
382
	tt = (time_t)tp.tv_sec;
Simon Morlat's avatar
Simon Morlat committed
383

384
#ifdef _WIN32
Simon Morlat's avatar
Simon Morlat committed
385 386 387 388
	lt = localtime(&tt);
#else
	lt = localtime_r(&tt,&tmbuf);
#endif
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	if (__ortp_logger.log_file==NULL) __ortp_logger.log_file=stderr;
	switch(lev){
		case ORTP_DEBUG:
			lname = "debug";
		break;
		case ORTP_MESSAGE:
			lname = "message";
		break;
		case ORTP_WARNING:
			lname = "warning";
		break;
		case ORTP_ERROR:
			lname = "error";
		break;
		case ORTP_FATAL:
			lname = "fatal";
		break;
		default:
			lname = "badlevel";
409
	}
410

411 412
	msg=ortp_strdup_vprintf(fmt,args);
#if defined(_MSC_VER) && !defined(_WIN32_WCE)
413 414 415 416 417 418 419 420 421 422 423 424 425
#ifndef _UNICODE
	OutputDebugStringA(msg);
	OutputDebugStringA("\r\n");
#else
	{
		size_t len=strlen(msg);
		wchar_t *tmp=(wchar_t*)ortp_malloc0((len+1)*sizeof(wchar_t));
		mbstowcs(tmp,msg,len);
		OutputDebugStringW(tmp);
		OutputDebugStringW(L"\r\n");
		ortp_free(tmp);
	}
#endif
426
#endif
427
	fprintf(__ortp_logger.log_file,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i ortp-%s-%s" ENDLINE
428
		,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
429 430
		,(int)(tp.tv_usec/1000), lname, msg);
	fflush(__ortp_logger.log_file);
431 432 433 434 435 436 437 438 439

	// fatal messages should go to stderr too, even if we are using a file since application will abort right after
	if (__ortp_logger.log_file != stderr && lev == ORTP_FATAL) {
		fprintf(stderr,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i ortp-%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), lname, msg);
		fflush(stderr);
	}

440 441
	ortp_free(msg);
}
442 443 444 445 446 447 448 449 450 451


#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];

void ortp_qnx_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args) {
452
	uint8_t severity = SLOG2_DEBUG1;
453
	uint8_t buffer_idx = 1;
Sylvain Berfini's avatar
Sylvain Berfini committed
454
	char* msg;
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

	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;
		}
	}

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
	switch(lev){
		case ORTP_DEBUG:
			severity = SLOG2_DEBUG1;
		break;
		case ORTP_MESSAGE:
			severity = SLOG2_INFO;
		break;
		case ORTP_WARNING:
			severity = SLOG2_WARNING;
		break;
		case ORTP_ERROR:
			severity = SLOG2_ERROR;
		break;
		case ORTP_FATAL:
			severity = SLOG2_CRITICAL;
		break;
		default:
			severity = SLOG2_CRITICAL;
490 491
	}

492 493
	msg = ortp_strdup_vprintf(fmt,args);
	slog2c(slog2_buffer_handle[buffer_idx], 0, severity, msg);
494 495
}
#endif /* __QNX__ */
496
#endif