~ubuntu-branches/ubuntu/wily/sflphone/wily

1.1.12 by Francois Marier
Import upstream version 1.4.1
1
/* $Id: log.c 4761 2014-02-24 09:02:44Z nanang $ */
2
/* 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program 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
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19
 */
20
#include <pj/types.h>
21
#include <pj/log.h>
22
#include <pj/string.h>
23
#include <pj/os.h>
24
#include <pj/compat/stdarg.h>
25
26
#if PJ_LOG_MAX_LEVEL >= 1
27
28
#if 0
29
PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL;
30
#else
31
static int pj_log_max_level = PJ_LOG_MAX_LEVEL;
32
#endif
33
34
static void *g_last_thread;
35
36
#if PJ_HAS_THREADS
37
static long thread_suspended_tls_id = -1;
38
#  if PJ_LOG_ENABLE_INDENT
39
static long thread_indent_tls_id = -1;
40
#  endif
41
#endif
42
43
#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS
44
static int log_indent;
45
#endif
46
47
static pj_log_func *log_writer = &pj_log_write;
48
static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
49
			    PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
50
			    PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC |
51
			    PJ_LOG_HAS_INDENT
52
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
53
    (defined(PJ_WIN64) && PJ_WIN64!=0)
54
			    | PJ_LOG_HAS_COLOR
55
#endif
56
			    ;
57
58
static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
59
static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
60
static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT | 
61
				   PJ_TERM_COLOR_R | 
62
				   PJ_TERM_COLOR_G;
63
static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | 
64
				   PJ_TERM_COLOR_R | 
65
				   PJ_TERM_COLOR_G | 
66
				   PJ_TERM_COLOR_B;
67
static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | 
68
				   PJ_TERM_COLOR_G | 
69
				   PJ_TERM_COLOR_B;
70
static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | 
71
				   PJ_TERM_COLOR_G | 
72
				   PJ_TERM_COLOR_B;
73
static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | 
74
				   PJ_TERM_COLOR_G | 
75
				   PJ_TERM_COLOR_B;
76
/* Default terminal color */
77
static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | 
78
				    PJ_TERM_COLOR_G | 
79
				    PJ_TERM_COLOR_B;
80
81
#if PJ_LOG_USE_STACK_BUFFER==0
82
static char log_buffer[PJ_LOG_MAX_SIZE];
83
#endif
84
85
#define LOG_MAX_INDENT		80
86
87
#if PJ_HAS_THREADS
88
static void logging_shutdown(void)
89
{
90
    if (thread_suspended_tls_id != -1) {
91
	pj_thread_local_free(thread_suspended_tls_id);
92
	thread_suspended_tls_id = -1;
93
    }
94
#  if PJ_LOG_ENABLE_INDENT
95
    if (thread_indent_tls_id != -1) {
96
	pj_thread_local_free(thread_indent_tls_id);
97
	thread_indent_tls_id = -1;
98
    }
99
#  endif
100
}
101
#endif	/* PJ_HAS_THREADS */
102
103
#if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
104
static void log_set_indent(int indent)
105
{
106
    if (indent < 0) indent = 0;
107
    pj_thread_local_set(thread_indent_tls_id, (void*)(pj_ssize_t)indent);
108
}
109
110
static int log_get_raw_indent(void)
111
{
112
    return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id);
113
}
114
115
#else
116
static void log_set_indent(int indent)
117
{
118
    log_indent = indent;
119
    if (log_indent < 0) log_indent = 0;
120
}
121
122
static int log_get_raw_indent(void)
123
{
124
    return log_indent;
125
}
126
#endif	/* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
127
128
static int log_get_indent(void)
129
{
130
    int indent = log_get_raw_indent();
131
    return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
132
}
133
134
PJ_DEF(void) pj_log_add_indent(int indent)
135
{
136
    log_set_indent(log_get_raw_indent() + indent);
137
}
138
139
PJ_DEF(void) pj_log_push_indent(void)
140
{
141
    pj_log_add_indent(PJ_LOG_INDENT_SIZE);
142
}
143
144
PJ_DEF(void) pj_log_pop_indent(void)
145
{
146
    pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
147
}
148
149
pj_status_t pj_log_init(void)
150
{
151
#if PJ_HAS_THREADS
152
    if (thread_suspended_tls_id == -1) {
153
	pj_status_t status;
154
	status = pj_thread_local_alloc(&thread_suspended_tls_id);
155
	if (status != PJ_SUCCESS)
156
	    return status;
157
158
#  if PJ_LOG_ENABLE_INDENT
159
	status = pj_thread_local_alloc(&thread_indent_tls_id);
160
	if (status != PJ_SUCCESS) {
161
	    pj_thread_local_free(thread_suspended_tls_id);
162
	    thread_suspended_tls_id = -1;
163
	    return status;
164
	}
165
#  endif
166
	pj_atexit(&logging_shutdown);
167
    }
168
#endif
169
    g_last_thread = NULL;
170
    return PJ_SUCCESS;
171
}
172
173
PJ_DEF(void) pj_log_set_decor(unsigned decor)
174
{
175
    log_decor = decor;
176
}
177
178
PJ_DEF(unsigned) pj_log_get_decor(void)
179
{
180
    return log_decor;
181
}
182
183
PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
184
{
185
    switch (level) 
186
    {
187
	case 0: PJ_LOG_COLOR_0 = color; 
188
	    break;
189
	case 1: PJ_LOG_COLOR_1 = color; 
190
	    break;
191
	case 2: PJ_LOG_COLOR_2 = color; 
192
	    break;
193
	case 3: PJ_LOG_COLOR_3 = color; 
194
	    break;
195
	case 4: PJ_LOG_COLOR_4 = color; 
196
	    break;
197
	case 5: PJ_LOG_COLOR_5 = color; 
198
	    break;
199
	case 6: PJ_LOG_COLOR_6 = color; 
200
	    break;
201
	/* Default terminal color */
202
	case 77: PJ_LOG_COLOR_77 = color; 
203
	    break;
204
	default:
205
	    /* Do nothing */
206
	    break;
207
    }
208
}
209
210
PJ_DEF(pj_color_t) pj_log_get_color(int level)
211
{
212
    switch (level) {
213
	case 0:
214
	    return PJ_LOG_COLOR_0;
215
	case 1:
216
	    return PJ_LOG_COLOR_1;
217
	case 2:
218
	    return PJ_LOG_COLOR_2;
219
	case 3:
220
	    return PJ_LOG_COLOR_3;
221
	case 4:
222
	    return PJ_LOG_COLOR_4;
223
	case 5:
224
	    return PJ_LOG_COLOR_5;
225
	case 6:
226
	    return PJ_LOG_COLOR_6;
227
	default:
228
	    /* Return default terminal color */
229
	    return PJ_LOG_COLOR_77;
230
    }
231
}
232
233
PJ_DEF(void) pj_log_set_level(int level)
234
{
235
    pj_log_max_level = level;
236
}
237
238
#if 1
239
PJ_DEF(int) pj_log_get_level(void)
240
{
241
    return pj_log_max_level;
242
}
243
#endif
244
245
PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
246
{
247
    log_writer = func;
248
}
249
250
PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
251
{
252
    return log_writer;
253
}
254
255
/* Temporarily suspend logging facility for this thread.
256
 * If thread local storage/variable is not used or not initialized, then
257
 * we can only suspend the logging globally across all threads. This may
258
 * happen e.g. when log function is called before PJLIB is fully initialized
259
 * or after PJLIB is shutdown.
260
 */
261
static void suspend_logging(int *saved_level)
262
{
263
	/* Save the level regardless, just in case PJLIB is shutdown
264
	 * between suspend and resume.
265
	 */
266
	*saved_level = pj_log_max_level;
267
268
#if PJ_HAS_THREADS
269
    if (thread_suspended_tls_id != -1) 
270
    {
271
	pj_thread_local_set(thread_suspended_tls_id, 
272
			    (void*)(pj_ssize_t)PJ_TRUE);
273
    } 
274
    else
275
#endif
276
    {
277
	pj_log_max_level = 0;
278
    }
279
}
280
281
/* Resume logging facility for this thread */
282
static void resume_logging(int *saved_level)
283
{
284
#if PJ_HAS_THREADS
285
    if (thread_suspended_tls_id != -1) 
286
    {
287
	pj_thread_local_set(thread_suspended_tls_id,
288
			    (void*)(pj_size_t)PJ_FALSE);
289
    }
290
    else
291
#endif
292
    {
293
	/* Only revert the level if application doesn't change the
294
	 * logging level between suspend and resume.
295
	 */
296
	if (pj_log_max_level==0 && *saved_level)
297
	    pj_log_max_level = *saved_level;
298
    }
299
}
300
301
/* Is logging facility suspended for this thread? */
302
static pj_bool_t is_logging_suspended(void)
303
{
304
#if PJ_HAS_THREADS
305
    if (thread_suspended_tls_id != -1) 
306
    {
307
	return pj_thread_local_get(thread_suspended_tls_id) != NULL;
308
    }
309
    else
310
#endif
311
    {
312
	return pj_log_max_level == 0;
313
    }
314
}
315
316
PJ_DEF(void) pj_log( const char *sender, int level, 
317
		     const char *format, va_list marker)
318
{
319
    pj_time_val now;
320
    pj_parsed_time ptime;
321
    char *pre;
322
#if PJ_LOG_USE_STACK_BUFFER
323
    char log_buffer[PJ_LOG_MAX_SIZE];
324
#endif
325
    int saved_level, len, print_len, indent;
326
327
    PJ_CHECK_STACK();
328
329
    if (level > pj_log_max_level)
330
	return;
331
332
    if (is_logging_suspended())
333
	return;
334
335
    /* Temporarily disable logging for this thread. Some of PJLIB APIs that
336
     * this function calls below will recursively call the logging function 
337
     * back, hence it will cause infinite recursive calls if we allow that.
338
     */
339
    suspend_logging(&saved_level);
340
341
    /* Get current date/time. */
342
    pj_gettimeofday(&now);
343
    pj_time_decode(&now, &ptime);
344
345
    pre = log_buffer;
346
    if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
347
	static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:", 
348
			      " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
349
	pj_ansi_strcpy(pre, ltexts[level]);
350
	pre += 6;
351
    }
352
    if (log_decor & PJ_LOG_HAS_DAY_NAME) {
353
	static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
354
				       "Thu", "Fri", "Sat"};
355
	pj_ansi_strcpy(pre, wdays[ptime.wday]);
356
	pre += 3;
357
    }
358
    if (log_decor & PJ_LOG_HAS_YEAR) {
359
	if (pre!=log_buffer) *pre++ = ' ';
360
	pre += pj_utoa(ptime.year, pre);
361
    }
362
    if (log_decor & PJ_LOG_HAS_MONTH) {
363
	*pre++ = '-';
364
	pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
365
    }
366
    if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
367
	*pre++ = '-';
368
	pre += pj_utoa_pad(ptime.day, pre, 2, '0');
369
    }
370
    if (log_decor & PJ_LOG_HAS_TIME) {
371
	if (pre!=log_buffer) *pre++ = ' ';
372
	pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
373
	*pre++ = ':';
374
	pre += pj_utoa_pad(ptime.min, pre, 2, '0');
375
	*pre++ = ':';
376
	pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
377
    }
378
    if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
379
	*pre++ = '.';
380
	pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
381
    }
382
    if (log_decor & PJ_LOG_HAS_SENDER) {
383
	enum { SENDER_WIDTH = 14 };
384
	pj_size_t sender_len = strlen(sender);
385
	if (pre!=log_buffer) *pre++ = ' ';
386
	if (sender_len <= SENDER_WIDTH) {
387
	    while (sender_len < SENDER_WIDTH)
388
		*pre++ = ' ', ++sender_len;
389
	    while (*sender)
390
		*pre++ = *sender++;
391
	} else {
392
	    int i;
393
	    for (i=0; i<SENDER_WIDTH; ++i)
394
		*pre++ = *sender++;
395
	}
396
    }
397
    if (log_decor & PJ_LOG_HAS_THREAD_ID) {
398
	enum { THREAD_WIDTH = 12 };
399
	const char *thread_name = pj_thread_get_name(pj_thread_this());
400
	pj_size_t thread_len = strlen(thread_name);
401
	*pre++ = ' ';
402
	if (thread_len <= THREAD_WIDTH) {
403
	    while (thread_len < THREAD_WIDTH)
404
		*pre++ = ' ', ++thread_len;
405
	    while (*thread_name)
406
		*pre++ = *thread_name++;
407
	} else {
408
	    int i;
409
	    for (i=0; i<THREAD_WIDTH; ++i)
410
		*pre++ = *thread_name++;
411
	}
412
    }
413
414
    if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
415
	*pre++ = ' ';
416
417
    if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
418
	void *current_thread = (void*)pj_thread_this();
419
	if (current_thread != g_last_thread) {
420
	    *pre++ = '!';
421
	    g_last_thread = current_thread;
422
	} else {
423
	    *pre++ = ' ';
424
	}
425
    } else if (log_decor & PJ_LOG_HAS_SPACE) {
426
	*pre++ = ' ';
427
    }
428
429
#if PJ_LOG_ENABLE_INDENT
430
    if (log_decor & PJ_LOG_HAS_INDENT) {
431
	indent = log_get_indent();
432
	if (indent > 0) {
433
	    pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
434
	    pre += indent;
435
	}
436
    }
437
#endif
438
439
    len = (int)(pre - log_buffer);
440
441
    /* Print the whole message to the string log_buffer. */
442
    print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, 
443
				  marker);
444
    if (print_len < 0) {
445
	level = 1;
446
	print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, 
447
				     "<logging error: msg too long>");
448
    }
449
    if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) {
450
	print_len = sizeof(log_buffer) - len - 1;
451
    }
452
    len = len + print_len;
453
    if (len > 0 && len < (int)sizeof(log_buffer)-2) {
454
	if (log_decor & PJ_LOG_HAS_CR) {
455
	    log_buffer[len++] = '\r';
456
	}
457
	if (log_decor & PJ_LOG_HAS_NEWLINE) {
458
	    log_buffer[len++] = '\n';
459
	}
460
	log_buffer[len] = '\0';
461
    } else {
462
	len = sizeof(log_buffer)-1;
463
	if (log_decor & PJ_LOG_HAS_CR) {
464
	    log_buffer[sizeof(log_buffer)-3] = '\r';
465
	}
466
	if (log_decor & PJ_LOG_HAS_NEWLINE) {
467
	    log_buffer[sizeof(log_buffer)-2] = '\n';
468
	}
469
	log_buffer[sizeof(log_buffer)-1] = '\0';
470
    }
471
472
    /* It should be safe to resume logging at this point. Application can
473
     * recursively call the logging function inside the callback.
474
     */
475
    resume_logging(&saved_level);
476
477
    if (log_writer)
478
	(*log_writer)(level, log_buffer, len);
479
}
480
481
/*
482
PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
483
{
484
    va_list arg;
485
    va_start(arg, format);
486
    pj_log(obj, 0, format, arg);
487
    va_end(arg);
488
}
489
*/
490
491
PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
492
{
493
    va_list arg;
494
    va_start(arg, format);
495
    pj_log(obj, 1, format, arg);
496
    va_end(arg);
497
}
498
#endif	/* PJ_LOG_MAX_LEVEL >= 1 */
499
500
#if PJ_LOG_MAX_LEVEL >= 2
501
PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
502
{
503
    va_list arg;
504
    va_start(arg, format);
505
    pj_log(obj, 2, format, arg);
506
    va_end(arg);
507
}
508
#endif
509
510
#if PJ_LOG_MAX_LEVEL >= 3
511
PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
512
{
513
    va_list arg;
514
    va_start(arg, format);
515
    pj_log(obj, 3, format, arg);
516
    va_end(arg);
517
}
518
#endif
519
520
#if PJ_LOG_MAX_LEVEL >= 4
521
PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
522
{
523
    va_list arg;
524
    va_start(arg, format);
525
    pj_log(obj, 4, format, arg);
526
    va_end(arg);
527
}
528
#endif
529
530
#if PJ_LOG_MAX_LEVEL >= 5
531
PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
532
{
533
    va_list arg;
534
    va_start(arg, format);
535
    pj_log(obj, 5, format, arg);
536
    va_end(arg);
537
}
538
#endif
539
540
#if PJ_LOG_MAX_LEVEL >= 6
541
PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
542
{
543
    va_list arg;
544
    va_start(arg, format);
545
    pj_log(obj, 6, format, arg);
546
    va_end(arg);
547
}
548
#endif
549