Multiple revisions 380735-380736
[asterisk/asterisk.git] / res / pjproject / pjlib / src / pj / log.c
1 /* $Id$ */
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                             | PJ_LOG_HAS_COLOR
54 #endif
55                             ;
56
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 | 
60                                    PJ_TERM_COLOR_R | 
61                                    PJ_TERM_COLOR_G;
62 static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | 
63                                    PJ_TERM_COLOR_R | 
64                                    PJ_TERM_COLOR_G | 
65                                    PJ_TERM_COLOR_B;
66 static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | 
67                                    PJ_TERM_COLOR_G | 
68                                    PJ_TERM_COLOR_B;
69 static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | 
70                                    PJ_TERM_COLOR_G | 
71                                    PJ_TERM_COLOR_B;
72 static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | 
73                                    PJ_TERM_COLOR_G | 
74                                    PJ_TERM_COLOR_B;
75 /* Default terminal color */
76 static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | 
77                                     PJ_TERM_COLOR_G | 
78                                     PJ_TERM_COLOR_B;
79
80 #if PJ_LOG_USE_STACK_BUFFER==0
81 static char log_buffer[PJ_LOG_MAX_SIZE];
82 #endif
83
84 #define LOG_MAX_INDENT          80
85
86 #if PJ_HAS_THREADS
87 static void logging_shutdown(void)
88 {
89     if (thread_suspended_tls_id != -1) {
90         pj_thread_local_free(thread_suspended_tls_id);
91         thread_suspended_tls_id = -1;
92     }
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;
97     }
98 #  endif
99 }
100 #endif  /* PJ_HAS_THREADS */
101
102 #if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
103 static void log_set_indent(int indent)
104 {
105     if (indent < 0) indent = 0;
106     pj_thread_local_set(thread_indent_tls_id, (void*)(long)indent);
107 }
108
109 static int log_get_raw_indent(void)
110 {
111     return (long)pj_thread_local_get(thread_indent_tls_id);
112 }
113
114 #else
115 static void log_set_indent(int indent)
116 {
117     log_indent = indent;
118     if (log_indent < 0) log_indent = 0;
119 }
120
121 static int log_get_raw_indent(void)
122 {
123     return log_indent;
124 }
125 #endif  /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
126
127 static int log_get_indent(void)
128 {
129     int indent = log_get_raw_indent();
130     return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
131 }
132
133 PJ_DEF(void) pj_log_add_indent(int indent)
134 {
135     log_set_indent(log_get_raw_indent() + indent);
136 }
137
138 PJ_DEF(void) pj_log_push_indent(void)
139 {
140     pj_log_add_indent(PJ_LOG_INDENT_SIZE);
141 }
142
143 PJ_DEF(void) pj_log_pop_indent(void)
144 {
145     pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
146 }
147
148 pj_status_t pj_log_init(void)
149 {
150 #if PJ_HAS_THREADS
151     if (thread_suspended_tls_id == -1) {
152         pj_status_t status;
153         status = pj_thread_local_alloc(&thread_suspended_tls_id);
154         if (status != PJ_SUCCESS)
155             return status;
156
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;
162             return status;
163         }
164 #  endif
165         pj_atexit(&logging_shutdown);
166     }
167 #endif
168     g_last_thread = NULL;
169     return PJ_SUCCESS;
170 }
171
172 PJ_DEF(void) pj_log_set_decor(unsigned decor)
173 {
174     log_decor = decor;
175 }
176
177 PJ_DEF(unsigned) pj_log_get_decor(void)
178 {
179     return log_decor;
180 }
181
182 PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
183 {
184     switch (level) 
185     {
186         case 0: PJ_LOG_COLOR_0 = color; 
187             break;
188         case 1: PJ_LOG_COLOR_1 = color; 
189             break;
190         case 2: PJ_LOG_COLOR_2 = color; 
191             break;
192         case 3: PJ_LOG_COLOR_3 = color; 
193             break;
194         case 4: PJ_LOG_COLOR_4 = color; 
195             break;
196         case 5: PJ_LOG_COLOR_5 = color; 
197             break;
198         case 6: PJ_LOG_COLOR_6 = color; 
199             break;
200         /* Default terminal color */
201         case 77: PJ_LOG_COLOR_77 = color; 
202             break;
203         default:
204             /* Do nothing */
205             break;
206     }
207 }
208
209 PJ_DEF(pj_color_t) pj_log_get_color(int level)
210 {
211     switch (level) {
212         case 0:
213             return PJ_LOG_COLOR_0;
214         case 1:
215             return PJ_LOG_COLOR_1;
216         case 2:
217             return PJ_LOG_COLOR_2;
218         case 3:
219             return PJ_LOG_COLOR_3;
220         case 4:
221             return PJ_LOG_COLOR_4;
222         case 5:
223             return PJ_LOG_COLOR_5;
224         case 6:
225             return PJ_LOG_COLOR_6;
226         default:
227             /* Return default terminal color */
228             return PJ_LOG_COLOR_77;
229     }
230 }
231
232 PJ_DEF(void) pj_log_set_level(int level)
233 {
234     pj_log_max_level = level;
235 }
236
237 #if 1
238 PJ_DEF(int) pj_log_get_level(void)
239 {
240     return pj_log_max_level;
241 }
242 #endif
243
244 PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
245 {
246     log_writer = func;
247 }
248
249 PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
250 {
251     return log_writer;
252 }
253
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.
259  */
260 static void suspend_logging(int *saved_level)
261 {
262         /* Save the level regardless, just in case PJLIB is shutdown
263          * between suspend and resume.
264          */
265         *saved_level = pj_log_max_level;
266
267 #if PJ_HAS_THREADS
268     if (thread_suspended_tls_id != -1) 
269     {
270         pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE);
271     } 
272     else
273 #endif
274     {
275         pj_log_max_level = 0;
276     }
277 }
278
279 /* Resume logging facility for this thread */
280 static void resume_logging(int *saved_level)
281 {
282 #if PJ_HAS_THREADS
283     if (thread_suspended_tls_id != -1) 
284     {
285         pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
286     }
287     else
288 #endif
289     {
290         /* Only revert the level if application doesn't change the
291          * logging level between suspend and resume.
292          */
293         if (pj_log_max_level==0 && *saved_level)
294             pj_log_max_level = *saved_level;
295     }
296 }
297
298 /* Is logging facility suspended for this thread? */
299 static pj_bool_t is_logging_suspended(void)
300 {
301 #if PJ_HAS_THREADS
302     if (thread_suspended_tls_id != -1) 
303     {
304         return pj_thread_local_get(thread_suspended_tls_id) != NULL;
305     }
306     else
307 #endif
308     {
309         return pj_log_max_level == 0;
310     }
311 }
312
313 PJ_DEF(void) pj_log( const char *sender, int level, 
314                      const char *format, va_list marker)
315 {
316     pj_time_val now;
317     pj_parsed_time ptime;
318     char *pre;
319 #if PJ_LOG_USE_STACK_BUFFER
320     char log_buffer[PJ_LOG_MAX_SIZE];
321 #endif
322     int saved_level, len, print_len, indent;
323
324     PJ_CHECK_STACK();
325
326     if (level > pj_log_max_level)
327         return;
328
329     if (is_logging_suspended())
330         return;
331
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.
335      */
336     suspend_logging(&saved_level);
337
338     /* Get current date/time. */
339     pj_gettimeofday(&now);
340     pj_time_decode(&now, &ptime);
341
342     pre = log_buffer;
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]);
347         pre += 6;
348     }
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]);
353         pre += 3;
354     }
355     if (log_decor & PJ_LOG_HAS_YEAR) {
356         if (pre!=log_buffer) *pre++ = ' ';
357         pre += pj_utoa(ptime.year, pre);
358     }
359     if (log_decor & PJ_LOG_HAS_MONTH) {
360         *pre++ = '-';
361         pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
362     }
363     if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
364         *pre++ = '-';
365         pre += pj_utoa_pad(ptime.day, pre, 2, '0');
366     }
367     if (log_decor & PJ_LOG_HAS_TIME) {
368         if (pre!=log_buffer) *pre++ = ' ';
369         pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
370         *pre++ = ':';
371         pre += pj_utoa_pad(ptime.min, pre, 2, '0');
372         *pre++ = ':';
373         pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
374     }
375     if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
376         *pre++ = '.';
377         pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
378     }
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;
386             while (*sender)
387                 *pre++ = *sender++;
388         } else {
389             int i;
390             for (i=0; i<SENDER_WIDTH; ++i)
391                 *pre++ = *sender++;
392         }
393     }
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);
398         *pre++ = ' ';
399         if (thread_len <= THREAD_WIDTH) {
400             while (thread_len < THREAD_WIDTH)
401                 *pre++ = ' ', ++thread_len;
402             while (*thread_name)
403                 *pre++ = *thread_name++;
404         } else {
405             int i;
406             for (i=0; i<THREAD_WIDTH; ++i)
407                 *pre++ = *thread_name++;
408         }
409     }
410
411     if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
412         *pre++ = ' ';
413
414     if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
415         void *current_thread = (void*)pj_thread_this();
416         if (current_thread != g_last_thread) {
417             *pre++ = '!';
418             g_last_thread = current_thread;
419         } else {
420             *pre++ = ' ';
421         }
422     } else if (log_decor & PJ_LOG_HAS_SPACE) {
423         *pre++ = ' ';
424     }
425
426 #if PJ_LOG_ENABLE_INDENT
427     if (log_decor & PJ_LOG_HAS_INDENT) {
428         indent = log_get_indent();
429         if (indent > 0) {
430             pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
431             pre += indent;
432         }
433     }
434 #endif
435
436     len = pre - log_buffer;
437
438     /* Print the whole message to the string log_buffer. */
439     print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, 
440                                   marker);
441     if (print_len < 0) {
442         level = 1;
443         print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, 
444                                      "<logging error: msg too long>");
445     }
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';
450         }
451         if (log_decor & PJ_LOG_HAS_NEWLINE) {
452             log_buffer[len++] = '\n';
453         }
454         log_buffer[len] = '\0';
455     } else {
456         len = sizeof(log_buffer)-1;
457         if (log_decor & PJ_LOG_HAS_CR) {
458             log_buffer[sizeof(log_buffer)-3] = '\r';
459         }
460         if (log_decor & PJ_LOG_HAS_NEWLINE) {
461             log_buffer[sizeof(log_buffer)-2] = '\n';
462         }
463         log_buffer[sizeof(log_buffer)-1] = '\0';
464     }
465
466     /* It should be safe to resume logging at this point. Application can
467      * recursively call the logging function inside the callback.
468      */
469     resume_logging(&saved_level);
470
471     if (log_writer)
472         (*log_writer)(level, log_buffer, len);
473 }
474
475 /*
476 PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
477 {
478     va_list arg;
479     va_start(arg, format);
480     pj_log(obj, 0, format, arg);
481     va_end(arg);
482 }
483 */
484
485 PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
486 {
487     va_list arg;
488     va_start(arg, format);
489     pj_log(obj, 1, format, arg);
490     va_end(arg);
491 }
492 #endif  /* PJ_LOG_MAX_LEVEL >= 1 */
493
494 #if PJ_LOG_MAX_LEVEL >= 2
495 PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
496 {
497     va_list arg;
498     va_start(arg, format);
499     pj_log(obj, 2, format, arg);
500     va_end(arg);
501 }
502 #endif
503
504 #if PJ_LOG_MAX_LEVEL >= 3
505 PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
506 {
507     va_list arg;
508     va_start(arg, format);
509     pj_log(obj, 3, format, arg);
510     va_end(arg);
511 }
512 #endif
513
514 #if PJ_LOG_MAX_LEVEL >= 4
515 PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
516 {
517     va_list arg;
518     va_start(arg, format);
519     pj_log(obj, 4, format, arg);
520     va_end(arg);
521 }
522 #endif
523
524 #if PJ_LOG_MAX_LEVEL >= 5
525 PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
526 {
527     va_list arg;
528     va_start(arg, format);
529     pj_log(obj, 5, format, arg);
530     va_end(arg);
531 }
532 #endif
533
534 #if PJ_LOG_MAX_LEVEL >= 6
535 PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
536 {
537     va_list arg;
538     va_start(arg, format);
539     pj_log(obj, 6, format, arg);
540     va_end(arg);
541 }
542 #endif
543