1
/* $Id: log.c 4761 2014-02-24 09:02:44Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
22
#include <pj/string.h>
24
#include <pj/compat/stdarg.h>
26
#if PJ_LOG_MAX_LEVEL >= 1
29
PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL;
31
static int pj_log_max_level = PJ_LOG_MAX_LEVEL;
34
static void *g_last_thread;
37
static long thread_suspended_tls_id = -1;
38
# if PJ_LOG_ENABLE_INDENT
39
static long thread_indent_tls_id = -1;
43
#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS
44
static int log_indent;
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 |
52
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
53
(defined(PJ_WIN64) && PJ_WIN64!=0)
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 |
63
static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT |
67
static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R |
70
static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R |
73
static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R |
76
/* Default terminal color */
77
static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R |
81
#if PJ_LOG_USE_STACK_BUFFER==0
82
static char log_buffer[PJ_LOG_MAX_SIZE];
85
#define LOG_MAX_INDENT 80
88
static void logging_shutdown(void)
90
if (thread_suspended_tls_id != -1) {
91
pj_thread_local_free(thread_suspended_tls_id);
92
thread_suspended_tls_id = -1;
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;
101
#endif /* PJ_HAS_THREADS */
103
#if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
104
static void log_set_indent(int indent)
106
if (indent < 0) indent = 0;
107
pj_thread_local_set(thread_indent_tls_id, (void*)(pj_ssize_t)indent);
110
static int log_get_raw_indent(void)
112
return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id);
116
static void log_set_indent(int indent)
119
if (log_indent < 0) log_indent = 0;
122
static int log_get_raw_indent(void)
126
#endif /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
128
static int log_get_indent(void)
130
int indent = log_get_raw_indent();
131
return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
134
PJ_DEF(void) pj_log_add_indent(int indent)
136
log_set_indent(log_get_raw_indent() + indent);
139
PJ_DEF(void) pj_log_push_indent(void)
141
pj_log_add_indent(PJ_LOG_INDENT_SIZE);
144
PJ_DEF(void) pj_log_pop_indent(void)
146
pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
149
pj_status_t pj_log_init(void)
152
if (thread_suspended_tls_id == -1) {
154
status = pj_thread_local_alloc(&thread_suspended_tls_id);
155
if (status != PJ_SUCCESS)
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;
166
pj_atexit(&logging_shutdown);
169
g_last_thread = NULL;
173
PJ_DEF(void) pj_log_set_decor(unsigned decor)
178
PJ_DEF(unsigned) pj_log_get_decor(void)
183
PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
187
case 0: PJ_LOG_COLOR_0 = color;
189
case 1: PJ_LOG_COLOR_1 = color;
191
case 2: PJ_LOG_COLOR_2 = color;
193
case 3: PJ_LOG_COLOR_3 = color;
195
case 4: PJ_LOG_COLOR_4 = color;
197
case 5: PJ_LOG_COLOR_5 = color;
199
case 6: PJ_LOG_COLOR_6 = color;
201
/* Default terminal color */
202
case 77: PJ_LOG_COLOR_77 = color;
210
PJ_DEF(pj_color_t) pj_log_get_color(int level)
214
return PJ_LOG_COLOR_0;
216
return PJ_LOG_COLOR_1;
218
return PJ_LOG_COLOR_2;
220
return PJ_LOG_COLOR_3;
222
return PJ_LOG_COLOR_4;
224
return PJ_LOG_COLOR_5;
226
return PJ_LOG_COLOR_6;
228
/* Return default terminal color */
229
return PJ_LOG_COLOR_77;
233
PJ_DEF(void) pj_log_set_level(int level)
235
pj_log_max_level = level;
239
PJ_DEF(int) pj_log_get_level(void)
241
return pj_log_max_level;
245
PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
250
PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
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.
261
static void suspend_logging(int *saved_level)
263
/* Save the level regardless, just in case PJLIB is shutdown
264
* between suspend and resume.
266
*saved_level = pj_log_max_level;
269
if (thread_suspended_tls_id != -1)
271
pj_thread_local_set(thread_suspended_tls_id,
272
(void*)(pj_ssize_t)PJ_TRUE);
277
pj_log_max_level = 0;
281
/* Resume logging facility for this thread */
282
static void resume_logging(int *saved_level)
285
if (thread_suspended_tls_id != -1)
287
pj_thread_local_set(thread_suspended_tls_id,
288
(void*)(pj_size_t)PJ_FALSE);
293
/* Only revert the level if application doesn't change the
294
* logging level between suspend and resume.
296
if (pj_log_max_level==0 && *saved_level)
297
pj_log_max_level = *saved_level;
301
/* Is logging facility suspended for this thread? */
302
static pj_bool_t is_logging_suspended(void)
305
if (thread_suspended_tls_id != -1)
307
return pj_thread_local_get(thread_suspended_tls_id) != NULL;
312
return pj_log_max_level == 0;
316
PJ_DEF(void) pj_log( const char *sender, int level,
317
const char *format, va_list marker)
320
pj_parsed_time ptime;
322
#if PJ_LOG_USE_STACK_BUFFER
323
char log_buffer[PJ_LOG_MAX_SIZE];
325
int saved_level, len, print_len, indent;
329
if (level > pj_log_max_level)
332
if (is_logging_suspended())
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.
339
suspend_logging(&saved_level);
341
/* Get current date/time. */
342
pj_gettimeofday(&now);
343
pj_time_decode(&now, &ptime);
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]);
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]);
358
if (log_decor & PJ_LOG_HAS_YEAR) {
359
if (pre!=log_buffer) *pre++ = ' ';
360
pre += pj_utoa(ptime.year, pre);
362
if (log_decor & PJ_LOG_HAS_MONTH) {
364
pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
366
if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
368
pre += pj_utoa_pad(ptime.day, pre, 2, '0');
370
if (log_decor & PJ_LOG_HAS_TIME) {
371
if (pre!=log_buffer) *pre++ = ' ';
372
pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
374
pre += pj_utoa_pad(ptime.min, pre, 2, '0');
376
pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
378
if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
380
pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
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;
393
for (i=0; i<SENDER_WIDTH; ++i)
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);
402
if (thread_len <= THREAD_WIDTH) {
403
while (thread_len < THREAD_WIDTH)
404
*pre++ = ' ', ++thread_len;
406
*pre++ = *thread_name++;
409
for (i=0; i<THREAD_WIDTH; ++i)
410
*pre++ = *thread_name++;
414
if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
417
if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
418
void *current_thread = (void*)pj_thread_this();
419
if (current_thread != g_last_thread) {
421
g_last_thread = current_thread;
425
} else if (log_decor & PJ_LOG_HAS_SPACE) {
429
#if PJ_LOG_ENABLE_INDENT
430
if (log_decor & PJ_LOG_HAS_INDENT) {
431
indent = log_get_indent();
433
pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
439
len = (int)(pre - log_buffer);
441
/* Print the whole message to the string log_buffer. */
442
print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
446
print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
447
"<logging error: msg too long>");
449
if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) {
450
print_len = sizeof(log_buffer) - len - 1;
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';
457
if (log_decor & PJ_LOG_HAS_NEWLINE) {
458
log_buffer[len++] = '\n';
460
log_buffer[len] = '\0';
462
len = sizeof(log_buffer)-1;
463
if (log_decor & PJ_LOG_HAS_CR) {
464
log_buffer[sizeof(log_buffer)-3] = '\r';
466
if (log_decor & PJ_LOG_HAS_NEWLINE) {
467
log_buffer[sizeof(log_buffer)-2] = '\n';
469
log_buffer[sizeof(log_buffer)-1] = '\0';
472
/* It should be safe to resume logging at this point. Application can
473
* recursively call the logging function inside the callback.
475
resume_logging(&saved_level);
478
(*log_writer)(level, log_buffer, len);
482
PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
485
va_start(arg, format);
486
pj_log(obj, 0, format, arg);
491
PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
494
va_start(arg, format);
495
pj_log(obj, 1, format, arg);
498
#endif /* PJ_LOG_MAX_LEVEL >= 1 */
500
#if PJ_LOG_MAX_LEVEL >= 2
501
PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
504
va_start(arg, format);
505
pj_log(obj, 2, format, arg);
510
#if PJ_LOG_MAX_LEVEL >= 3
511
PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
514
va_start(arg, format);
515
pj_log(obj, 3, format, arg);
520
#if PJ_LOG_MAX_LEVEL >= 4
521
PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
524
va_start(arg, format);
525
pj_log(obj, 4, format, arg);
530
#if PJ_LOG_MAX_LEVEL >= 5
531
PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
534
va_start(arg, format);
535
pj_log(obj, 5, format, arg);
540
#if PJ_LOG_MAX_LEVEL >= 6
541
PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
544
va_start(arg, format);
545
pj_log(obj, 6, format, arg);