blob: fef431788ccf442e5ca698aeec839aa62fee0abf [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id: log.c 4624 2013-10-21 06:37:30Z ming $ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
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
29PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL;
30#else
31static int pj_log_max_level = PJ_LOG_MAX_LEVEL;
32#endif
33
34static void *g_last_thread;
35
36#if PJ_HAS_THREADS
37static long thread_suspended_tls_id = -1;
38# if PJ_LOG_ENABLE_INDENT
39static long thread_indent_tls_id = -1;
40# endif
41#endif
42
43#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS
44static int log_indent;
45#endif
46
47static pj_log_func *log_writer = &pj_log_write;
48static 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
58static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
59static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
60static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT |
61 PJ_TERM_COLOR_R |
62 PJ_TERM_COLOR_G;
63static 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;
67static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R |
68 PJ_TERM_COLOR_G |
69 PJ_TERM_COLOR_B;
70static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R |
71 PJ_TERM_COLOR_G |
72 PJ_TERM_COLOR_B;
73static 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 */
77static 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
82static char log_buffer[PJ_LOG_MAX_SIZE];
83#endif
84
85#define LOG_MAX_INDENT 80
86
87#if PJ_HAS_THREADS
88static 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
104static 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
Alexandre Lision8af73cb2013-12-10 14:11:20 -0500110static int log_get_raw_indent(void)
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400111{
112 return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id);
113}
114
115#else
116static void log_set_indent(int indent)
117{
118 log_indent = indent;
119 if (log_indent < 0) log_indent = 0;
120}
121
Alexandre Lision8af73cb2013-12-10 14:11:20 -0500122static int log_get_raw_indent(void)
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400123{
124 return log_indent;
125}
126#endif /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
127
Alexandre Lision8af73cb2013-12-10 14:11:20 -0500128static int log_get_indent(void)
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400129{
130 int indent = log_get_raw_indent();
131 return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
132}
133
134PJ_DEF(void) pj_log_add_indent(int indent)
135{
136 log_set_indent(log_get_raw_indent() + indent);
137}
138
139PJ_DEF(void) pj_log_push_indent(void)
140{
141 pj_log_add_indent(PJ_LOG_INDENT_SIZE);
142}
143
144PJ_DEF(void) pj_log_pop_indent(void)
145{
146 pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
147}
148
149pj_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
173PJ_DEF(void) pj_log_set_decor(unsigned decor)
174{
175 log_decor = decor;
176}
177
178PJ_DEF(unsigned) pj_log_get_decor(void)
179{
180 return log_decor;
181}
182
183PJ_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
210PJ_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
233PJ_DEF(void) pj_log_set_level(int level)
234{
235 pj_log_max_level = level;
236}
237
238#if 1
239PJ_DEF(int) pj_log_get_level(void)
240{
241 return pj_log_max_level;
242}
243#endif
244
245PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
246{
247 log_writer = func;
248}
249
250PJ_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 */
261static 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 */
282static 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, (void*)PJ_FALSE);
288 }
289 else
290#endif
291 {
292 /* Only revert the level if application doesn't change the
293 * logging level between suspend and resume.
294 */
295 if (pj_log_max_level==0 && *saved_level)
296 pj_log_max_level = *saved_level;
297 }
298}
299
300/* Is logging facility suspended for this thread? */
301static pj_bool_t is_logging_suspended(void)
302{
303#if PJ_HAS_THREADS
304 if (thread_suspended_tls_id != -1)
305 {
306 return pj_thread_local_get(thread_suspended_tls_id) != NULL;
307 }
308 else
309#endif
310 {
311 return pj_log_max_level == 0;
312 }
313}
314
315PJ_DEF(void) pj_log( const char *sender, int level,
316 const char *format, va_list marker)
317{
318 pj_time_val now;
319 pj_parsed_time ptime;
320 char *pre;
321#if PJ_LOG_USE_STACK_BUFFER
322 char log_buffer[PJ_LOG_MAX_SIZE];
323#endif
324 int saved_level, len, print_len, indent;
325
326 PJ_CHECK_STACK();
327
328 if (level > pj_log_max_level)
329 return;
330
331 if (is_logging_suspended())
332 return;
333
334 /* Temporarily disable logging for this thread. Some of PJLIB APIs that
335 * this function calls below will recursively call the logging function
336 * back, hence it will cause infinite recursive calls if we allow that.
337 */
338 suspend_logging(&saved_level);
339
340 /* Get current date/time. */
341 pj_gettimeofday(&now);
342 pj_time_decode(&now, &ptime);
343
344 pre = log_buffer;
345 if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
346 static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:",
347 " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
348 pj_ansi_strcpy(pre, ltexts[level]);
349 pre += 6;
350 }
351 if (log_decor & PJ_LOG_HAS_DAY_NAME) {
352 static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
353 "Thu", "Fri", "Sat"};
354 pj_ansi_strcpy(pre, wdays[ptime.wday]);
355 pre += 3;
356 }
357 if (log_decor & PJ_LOG_HAS_YEAR) {
358 if (pre!=log_buffer) *pre++ = ' ';
359 pre += pj_utoa(ptime.year, pre);
360 }
361 if (log_decor & PJ_LOG_HAS_MONTH) {
362 *pre++ = '-';
363 pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
364 }
365 if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
366 *pre++ = '-';
367 pre += pj_utoa_pad(ptime.day, pre, 2, '0');
368 }
369 if (log_decor & PJ_LOG_HAS_TIME) {
370 if (pre!=log_buffer) *pre++ = ' ';
371 pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
372 *pre++ = ':';
373 pre += pj_utoa_pad(ptime.min, pre, 2, '0');
374 *pre++ = ':';
375 pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
376 }
377 if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
378 *pre++ = '.';
379 pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
380 }
381 if (log_decor & PJ_LOG_HAS_SENDER) {
382 enum { SENDER_WIDTH = 14 };
383 pj_size_t sender_len = strlen(sender);
384 if (pre!=log_buffer) *pre++ = ' ';
385 if (sender_len <= SENDER_WIDTH) {
386 while (sender_len < SENDER_WIDTH)
387 *pre++ = ' ', ++sender_len;
388 while (*sender)
389 *pre++ = *sender++;
390 } else {
391 int i;
392 for (i=0; i<SENDER_WIDTH; ++i)
393 *pre++ = *sender++;
394 }
395 }
396 if (log_decor & PJ_LOG_HAS_THREAD_ID) {
397 enum { THREAD_WIDTH = 12 };
398 const char *thread_name = pj_thread_get_name(pj_thread_this());
399 pj_size_t thread_len = strlen(thread_name);
400 *pre++ = ' ';
401 if (thread_len <= THREAD_WIDTH) {
402 while (thread_len < THREAD_WIDTH)
403 *pre++ = ' ', ++thread_len;
404 while (*thread_name)
405 *pre++ = *thread_name++;
406 } else {
407 int i;
408 for (i=0; i<THREAD_WIDTH; ++i)
409 *pre++ = *thread_name++;
410 }
411 }
412
413 if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
414 *pre++ = ' ';
415
416 if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
417 void *current_thread = (void*)pj_thread_this();
418 if (current_thread != g_last_thread) {
419 *pre++ = '!';
420 g_last_thread = current_thread;
421 } else {
422 *pre++ = ' ';
423 }
424 } else if (log_decor & PJ_LOG_HAS_SPACE) {
425 *pre++ = ' ';
426 }
427
428#if PJ_LOG_ENABLE_INDENT
429 if (log_decor & PJ_LOG_HAS_INDENT) {
430 indent = log_get_indent();
431 if (indent > 0) {
432 pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
433 pre += indent;
434 }
435 }
436#endif
437
438 len = (int)(pre - log_buffer);
439
440 /* Print the whole message to the string log_buffer. */
441 print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
442 marker);
443 if (print_len < 0) {
444 level = 1;
445 print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
446 "<logging error: msg too long>");
447 }
Alexandre Lision8af73cb2013-12-10 14:11:20 -0500448 if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) {
449 print_len = sizeof(log_buffer) - len - 1;
450 }
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400451 len = len + print_len;
452 if (len > 0 && len < (int)sizeof(log_buffer)-2) {
453 if (log_decor & PJ_LOG_HAS_CR) {
454 log_buffer[len++] = '\r';
455 }
456 if (log_decor & PJ_LOG_HAS_NEWLINE) {
457 log_buffer[len++] = '\n';
458 }
459 log_buffer[len] = '\0';
460 } else {
461 len = sizeof(log_buffer)-1;
462 if (log_decor & PJ_LOG_HAS_CR) {
463 log_buffer[sizeof(log_buffer)-3] = '\r';
464 }
465 if (log_decor & PJ_LOG_HAS_NEWLINE) {
466 log_buffer[sizeof(log_buffer)-2] = '\n';
467 }
468 log_buffer[sizeof(log_buffer)-1] = '\0';
469 }
470
471 /* It should be safe to resume logging at this point. Application can
472 * recursively call the logging function inside the callback.
473 */
474 resume_logging(&saved_level);
475
476 if (log_writer)
477 (*log_writer)(level, log_buffer, len);
478}
479
480/*
481PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
482{
483 va_list arg;
484 va_start(arg, format);
485 pj_log(obj, 0, format, arg);
486 va_end(arg);
487}
488*/
489
490PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
491{
492 va_list arg;
493 va_start(arg, format);
494 pj_log(obj, 1, format, arg);
495 va_end(arg);
496}
497#endif /* PJ_LOG_MAX_LEVEL >= 1 */
498
499#if PJ_LOG_MAX_LEVEL >= 2
500PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
501{
502 va_list arg;
503 va_start(arg, format);
504 pj_log(obj, 2, format, arg);
505 va_end(arg);
506}
507#endif
508
509#if PJ_LOG_MAX_LEVEL >= 3
510PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
511{
512 va_list arg;
513 va_start(arg, format);
514 pj_log(obj, 3, format, arg);
515 va_end(arg);
516}
517#endif
518
519#if PJ_LOG_MAX_LEVEL >= 4
520PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
521{
522 va_list arg;
523 va_start(arg, format);
524 pj_log(obj, 4, format, arg);
525 va_end(arg);
526}
527#endif
528
529#if PJ_LOG_MAX_LEVEL >= 5
530PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
531{
532 va_list arg;
533 va_start(arg, format);
534 pj_log(obj, 5, format, arg);
535 va_end(arg);
536}
537#endif
538
539#if PJ_LOG_MAX_LEVEL >= 6
540PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
541{
542 va_list arg;
543 va_start(arg, format);
544 pj_log(obj, 6, format, arg);
545 va_end(arg);
546}
547#endif
548