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
57 static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
58 static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
59 static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT |
62 static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT |
66 static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R |
69 static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R |
72 static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R |
75 /* Default terminal color */
76 static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R |
80 #if PJ_LOG_USE_STACK_BUFFER==0
81 static char log_buffer[PJ_LOG_MAX_SIZE];
84 #define LOG_MAX_INDENT 80
87 static void logging_shutdown(void)
89 if (thread_suspended_tls_id != -1) {
90 pj_thread_local_free(thread_suspended_tls_id);
91 thread_suspended_tls_id = -1;
93 # if PJ_LOG_ENABLE_INDENT
94 if (thread_indent_tls_id != -1) {
95 pj_thread_local_free(thread_indent_tls_id);
96 thread_indent_tls_id = -1;
100 #endif /* PJ_HAS_THREADS */
102 #if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
103 static void log_set_indent(int indent)
105 if (indent < 0) indent = 0;
106 pj_thread_local_set(thread_indent_tls_id, (void*)(long)indent);
109 static int log_get_raw_indent(void)
111 return (long)pj_thread_local_get(thread_indent_tls_id);
115 static void log_set_indent(int indent)
118 if (log_indent < 0) log_indent = 0;
121 static int log_get_raw_indent(void)
125 #endif /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
127 static int log_get_indent(void)
129 int indent = log_get_raw_indent();
130 return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
133 PJ_DEF(void) pj_log_add_indent(int indent)
135 log_set_indent(log_get_raw_indent() + indent);
138 PJ_DEF(void) pj_log_push_indent(void)
140 pj_log_add_indent(PJ_LOG_INDENT_SIZE);
143 PJ_DEF(void) pj_log_pop_indent(void)
145 pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
148 pj_status_t pj_log_init(void)
151 if (thread_suspended_tls_id == -1) {
153 status = pj_thread_local_alloc(&thread_suspended_tls_id);
154 if (status != PJ_SUCCESS)
157 # if PJ_LOG_ENABLE_INDENT
158 status = pj_thread_local_alloc(&thread_indent_tls_id);
159 if (status != PJ_SUCCESS) {
160 pj_thread_local_free(thread_suspended_tls_id);
161 thread_suspended_tls_id = -1;
165 pj_atexit(&logging_shutdown);
168 g_last_thread = NULL;
172 PJ_DEF(void) pj_log_set_decor(unsigned decor)
177 PJ_DEF(unsigned) pj_log_get_decor(void)
182 PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
186 case 0: PJ_LOG_COLOR_0 = color;
188 case 1: PJ_LOG_COLOR_1 = color;
190 case 2: PJ_LOG_COLOR_2 = color;
192 case 3: PJ_LOG_COLOR_3 = color;
194 case 4: PJ_LOG_COLOR_4 = color;
196 case 5: PJ_LOG_COLOR_5 = color;
198 case 6: PJ_LOG_COLOR_6 = color;
200 /* Default terminal color */
201 case 77: PJ_LOG_COLOR_77 = color;
209 PJ_DEF(pj_color_t) pj_log_get_color(int level)
213 return PJ_LOG_COLOR_0;
215 return PJ_LOG_COLOR_1;
217 return PJ_LOG_COLOR_2;
219 return PJ_LOG_COLOR_3;
221 return PJ_LOG_COLOR_4;
223 return PJ_LOG_COLOR_5;
225 return PJ_LOG_COLOR_6;
227 /* Return default terminal color */
228 return PJ_LOG_COLOR_77;
232 PJ_DEF(void) pj_log_set_level(int level)
234 pj_log_max_level = level;
238 PJ_DEF(int) pj_log_get_level(void)
240 return pj_log_max_level;
244 PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
249 PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
254 /* Temporarily suspend logging facility for this thread.
255 * If thread local storage/variable is not used or not initialized, then
256 * we can only suspend the logging globally across all threads. This may
257 * happen e.g. when log function is called before PJLIB is fully initialized
258 * or after PJLIB is shutdown.
260 static void suspend_logging(int *saved_level)
262 /* Save the level regardless, just in case PJLIB is shutdown
263 * between suspend and resume.
265 *saved_level = pj_log_max_level;
268 if (thread_suspended_tls_id != -1)
270 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE);
275 pj_log_max_level = 0;
279 /* Resume logging facility for this thread */
280 static void resume_logging(int *saved_level)
283 if (thread_suspended_tls_id != -1)
285 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
290 /* Only revert the level if application doesn't change the
291 * logging level between suspend and resume.
293 if (pj_log_max_level==0 && *saved_level)
294 pj_log_max_level = *saved_level;
298 /* Is logging facility suspended for this thread? */
299 static pj_bool_t is_logging_suspended(void)
302 if (thread_suspended_tls_id != -1)
304 return pj_thread_local_get(thread_suspended_tls_id) != NULL;
309 return pj_log_max_level == 0;
313 PJ_DEF(void) pj_log( const char *sender, int level,
314 const char *format, va_list marker)
317 pj_parsed_time ptime;
319 #if PJ_LOG_USE_STACK_BUFFER
320 char log_buffer[PJ_LOG_MAX_SIZE];
322 int saved_level, len, print_len, indent;
326 if (level > pj_log_max_level)
329 if (is_logging_suspended())
332 /* Temporarily disable logging for this thread. Some of PJLIB APIs that
333 * this function calls below will recursively call the logging function
334 * back, hence it will cause infinite recursive calls if we allow that.
336 suspend_logging(&saved_level);
338 /* Get current date/time. */
339 pj_gettimeofday(&now);
340 pj_time_decode(&now, &ptime);
343 if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
344 static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:",
345 " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
346 pj_ansi_strcpy(pre, ltexts[level]);
349 if (log_decor & PJ_LOG_HAS_DAY_NAME) {
350 static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
351 "Thu", "Fri", "Sat"};
352 pj_ansi_strcpy(pre, wdays[ptime.wday]);
355 if (log_decor & PJ_LOG_HAS_YEAR) {
356 if (pre!=log_buffer) *pre++ = ' ';
357 pre += pj_utoa(ptime.year, pre);
359 if (log_decor & PJ_LOG_HAS_MONTH) {
361 pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
363 if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
365 pre += pj_utoa_pad(ptime.day, pre, 2, '0');
367 if (log_decor & PJ_LOG_HAS_TIME) {
368 if (pre!=log_buffer) *pre++ = ' ';
369 pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
371 pre += pj_utoa_pad(ptime.min, pre, 2, '0');
373 pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
375 if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
377 pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
379 if (log_decor & PJ_LOG_HAS_SENDER) {
380 enum { SENDER_WIDTH = 14 };
381 int sender_len = strlen(sender);
382 if (pre!=log_buffer) *pre++ = ' ';
383 if (sender_len <= SENDER_WIDTH) {
384 while (sender_len < SENDER_WIDTH)
385 *pre++ = ' ', ++sender_len;
390 for (i=0; i<SENDER_WIDTH; ++i)
394 if (log_decor & PJ_LOG_HAS_THREAD_ID) {
395 enum { THREAD_WIDTH = 12 };
396 const char *thread_name = pj_thread_get_name(pj_thread_this());
397 int thread_len = strlen(thread_name);
399 if (thread_len <= THREAD_WIDTH) {
400 while (thread_len < THREAD_WIDTH)
401 *pre++ = ' ', ++thread_len;
403 *pre++ = *thread_name++;
406 for (i=0; i<THREAD_WIDTH; ++i)
407 *pre++ = *thread_name++;
411 if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
414 if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
415 void *current_thread = (void*)pj_thread_this();
416 if (current_thread != g_last_thread) {
418 g_last_thread = current_thread;
422 } else if (log_decor & PJ_LOG_HAS_SPACE) {
426 #if PJ_LOG_ENABLE_INDENT
427 if (log_decor & PJ_LOG_HAS_INDENT) {
428 indent = log_get_indent();
430 pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
436 len = pre - log_buffer;
438 /* Print the whole message to the string log_buffer. */
439 print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
443 print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
444 "<logging error: msg too long>");
446 len = len + print_len;
447 if (len > 0 && len < (int)sizeof(log_buffer)-2) {
448 if (log_decor & PJ_LOG_HAS_CR) {
449 log_buffer[len++] = '\r';
451 if (log_decor & PJ_LOG_HAS_NEWLINE) {
452 log_buffer[len++] = '\n';
454 log_buffer[len] = '\0';
456 len = sizeof(log_buffer)-1;
457 if (log_decor & PJ_LOG_HAS_CR) {
458 log_buffer[sizeof(log_buffer)-3] = '\r';
460 if (log_decor & PJ_LOG_HAS_NEWLINE) {
461 log_buffer[sizeof(log_buffer)-2] = '\n';
463 log_buffer[sizeof(log_buffer)-1] = '\0';
466 /* It should be safe to resume logging at this point. Application can
467 * recursively call the logging function inside the callback.
469 resume_logging(&saved_level);
472 (*log_writer)(level, log_buffer, len);
476 PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
479 va_start(arg, format);
480 pj_log(obj, 0, format, arg);
485 PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
488 va_start(arg, format);
489 pj_log(obj, 1, format, arg);
492 #endif /* PJ_LOG_MAX_LEVEL >= 1 */
494 #if PJ_LOG_MAX_LEVEL >= 2
495 PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
498 va_start(arg, format);
499 pj_log(obj, 2, format, arg);
504 #if PJ_LOG_MAX_LEVEL >= 3
505 PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
508 va_start(arg, format);
509 pj_log(obj, 3, format, arg);
514 #if PJ_LOG_MAX_LEVEL >= 4
515 PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
518 va_start(arg, format);
519 pj_log(obj, 4, format, arg);
524 #if PJ_LOG_MAX_LEVEL >= 5
525 PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
528 va_start(arg, format);
529 pj_log(obj, 5, format, arg);
534 #if PJ_LOG_MAX_LEVEL >= 6
535 PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
538 va_start(arg, format);
539 pj_log(obj, 6, format, arg);