blob: 998ba6a15218b8b297eda191808c6c1191941b76 [file] [log] [blame]
Benny Prijono9033e312005-11-21 02:08:39 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono9033e312005-11-21 02:08:39 +00005 *
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>
Benny Prijono9033e312005-11-21 02:08:39 +000024#include <pj/compat/stdarg.h>
25
26#if PJ_LOG_MAX_LEVEL >= 1
27
Benny Prijono8ab968f2007-07-20 08:08:30 +000028#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
Nanang Izzuddind1f054e2010-08-06 07:18:08 +000033
34#if PJ_HAS_THREADS
Benny Prijonob1a3e732009-08-05 10:58:02 +000035static long thread_suspended_tls_id = -1;
Nanang Izzuddind1f054e2010-08-06 07:18:08 +000036#endif
37
Benny Prijono9033e312005-11-21 02:08:39 +000038static pj_log_func *log_writer = &pj_log_write;
39static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
Benny Prijonod6e362a2008-07-19 17:53:47 +000040 PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
Benny Prijonof940be42009-07-21 12:20:17 +000041 PJ_LOG_HAS_SPACE
Benny Prijonod6e362a2008-07-19 17:53:47 +000042#if defined(PJ_WIN32) && PJ_WIN32!=0
43 | PJ_LOG_HAS_COLOR
44#endif
45 ;
46
47static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
48static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
49static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT |
50 PJ_TERM_COLOR_R |
51 PJ_TERM_COLOR_G;
52static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT |
53 PJ_TERM_COLOR_R |
54 PJ_TERM_COLOR_G |
55 PJ_TERM_COLOR_B;
56static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R |
57 PJ_TERM_COLOR_G |
58 PJ_TERM_COLOR_B;
59static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R |
60 PJ_TERM_COLOR_G |
61 PJ_TERM_COLOR_B;
62static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R |
63 PJ_TERM_COLOR_G |
64 PJ_TERM_COLOR_B;
65/* Default terminal color */
66static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R |
67 PJ_TERM_COLOR_G |
68 PJ_TERM_COLOR_B;
Benny Prijono9033e312005-11-21 02:08:39 +000069
70#if PJ_LOG_USE_STACK_BUFFER==0
71static char log_buffer[PJ_LOG_MAX_SIZE];
72#endif
73
Nanang Izzuddind1f054e2010-08-06 07:18:08 +000074#if PJ_HAS_THREADS
Benny Prijonob1a3e732009-08-05 10:58:02 +000075static void logging_shutdown(void)
76{
Benny Prijonob1a3e732009-08-05 10:58:02 +000077 if (thread_suspended_tls_id != -1) {
78 pj_thread_local_free(thread_suspended_tls_id);
79 thread_suspended_tls_id = -1;
80 }
Benny Prijonob1a3e732009-08-05 10:58:02 +000081}
Nanang Izzuddind1f054e2010-08-06 07:18:08 +000082#endif
Benny Prijonob1a3e732009-08-05 10:58:02 +000083
84pj_status_t pj_log_init(void)
85{
86#if PJ_HAS_THREADS
87 if (thread_suspended_tls_id == -1) {
88 pj_thread_local_alloc(&thread_suspended_tls_id);
89 pj_atexit(&logging_shutdown);
90 }
91#endif
92 return PJ_SUCCESS;
93}
94
Benny Prijono9033e312005-11-21 02:08:39 +000095PJ_DEF(void) pj_log_set_decor(unsigned decor)
96{
97 log_decor = decor;
98}
99
100PJ_DEF(unsigned) pj_log_get_decor(void)
101{
102 return log_decor;
103}
104
Benny Prijonod6e362a2008-07-19 17:53:47 +0000105PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
106{
107 switch (level)
108 {
109 case 0: PJ_LOG_COLOR_0 = color;
110 break;
111 case 1: PJ_LOG_COLOR_1 = color;
112 break;
113 case 2: PJ_LOG_COLOR_2 = color;
114 break;
115 case 3: PJ_LOG_COLOR_3 = color;
116 break;
117 case 4: PJ_LOG_COLOR_4 = color;
118 break;
119 case 5: PJ_LOG_COLOR_5 = color;
120 break;
121 case 6: PJ_LOG_COLOR_6 = color;
122 break;
123 /* Default terminal color */
124 case 77: PJ_LOG_COLOR_77 = color;
125 break;
126 default:
127 /* Do nothing */
128 break;
129 }
130}
131
132PJ_DEF(pj_color_t) pj_log_get_color(int level)
133{
134 switch (level) {
135 case 0:
136 return PJ_LOG_COLOR_0;
137 case 1:
138 return PJ_LOG_COLOR_1;
139 case 2:
140 return PJ_LOG_COLOR_2;
141 case 3:
142 return PJ_LOG_COLOR_3;
143 case 4:
144 return PJ_LOG_COLOR_4;
145 case 5:
146 return PJ_LOG_COLOR_5;
147 case 6:
148 return PJ_LOG_COLOR_6;
149 default:
150 /* Return default terminal color */
151 return PJ_LOG_COLOR_77;
152 }
153}
154
Benny Prijono9033e312005-11-21 02:08:39 +0000155PJ_DEF(void) pj_log_set_level(int level)
156{
Benny Prijono505e82e2007-03-05 21:08:01 +0000157 pj_log_max_level = level;
Benny Prijono9033e312005-11-21 02:08:39 +0000158}
159
Benny Prijono8ab968f2007-07-20 08:08:30 +0000160#if 1
Benny Prijono9033e312005-11-21 02:08:39 +0000161PJ_DEF(int) pj_log_get_level(void)
162{
Benny Prijono505e82e2007-03-05 21:08:01 +0000163 return pj_log_max_level;
Benny Prijono9033e312005-11-21 02:08:39 +0000164}
Benny Prijono505e82e2007-03-05 21:08:01 +0000165#endif
Benny Prijono9033e312005-11-21 02:08:39 +0000166
167PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
168{
169 log_writer = func;
170}
171
172PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
173{
174 return log_writer;
175}
176
Benny Prijonob1a3e732009-08-05 10:58:02 +0000177/* Temporarily suspend logging facility for this thread.
178 * If thread local storage/variable is not used or not initialized, then
179 * we can only suspend the logging globally across all threads. This may
180 * happen e.g. when log function is called before PJLIB is fully initialized
181 * or after PJLIB is shutdown.
182 */
183static void suspend_logging(int *saved_level)
184{
Nanang Izzuddine5fe4202009-08-12 17:50:52 +0000185 /* Save the level regardless, just in case PJLIB is shutdown
186 * between suspend and resume.
187 */
188 *saved_level = pj_log_max_level;
189
Benny Prijonob1a3e732009-08-05 10:58:02 +0000190#if PJ_HAS_THREADS
191 if (thread_suspended_tls_id != -1)
192 {
193 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE);
194 }
195 else
196#endif
197 {
198 pj_log_max_level = 0;
199 }
Benny Prijonob1a3e732009-08-05 10:58:02 +0000200}
201
202/* Resume logging facility for this thread */
203static void resume_logging(int *saved_level)
204{
205#if PJ_HAS_THREADS
206 if (thread_suspended_tls_id != -1)
207 {
208 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
209 }
210 else
211#endif
212 {
213 /* Only revert the level if application doesn't change the
214 * logging level between suspend and resume.
215 */
216 if (pj_log_max_level==0 && *saved_level)
217 pj_log_max_level = *saved_level;
218 }
219}
220
221/* Is logging facility suspended for this thread? */
222static pj_bool_t is_logging_suspended(void)
223{
224#if PJ_HAS_THREADS
225 if (thread_suspended_tls_id != -1)
226 {
227 return pj_thread_local_get(thread_suspended_tls_id) != NULL;
228 }
229 else
230#endif
231 {
Nanang Izzuddine5fe4202009-08-12 17:50:52 +0000232 return pj_log_max_level == 0;
Benny Prijonob1a3e732009-08-05 10:58:02 +0000233 }
234}
235
Benny Prijono9033e312005-11-21 02:08:39 +0000236PJ_DEF(void) pj_log( const char *sender, int level,
237 const char *format, va_list marker)
238{
239 pj_time_val now;
240 pj_parsed_time ptime;
241 char *pre;
242#if PJ_LOG_USE_STACK_BUFFER
243 char log_buffer[PJ_LOG_MAX_SIZE];
244#endif
Benny Prijonob1a3e732009-08-05 10:58:02 +0000245 int saved_level, len, print_len;
Benny Prijono9033e312005-11-21 02:08:39 +0000246
247 PJ_CHECK_STACK();
248
Benny Prijono505e82e2007-03-05 21:08:01 +0000249 if (level > pj_log_max_level)
Benny Prijono9033e312005-11-21 02:08:39 +0000250 return;
251
Benny Prijonob1a3e732009-08-05 10:58:02 +0000252 if (is_logging_suspended())
253 return;
254
255 /* Temporarily disable logging for this thread. Some of PJLIB APIs that
256 * this function calls below will recursively call the logging function
257 * back, hence it will cause infinite recursive calls if we allow that.
258 */
259 suspend_logging(&saved_level);
260
Benny Prijono9033e312005-11-21 02:08:39 +0000261 /* Get current date/time. */
262 pj_gettimeofday(&now);
263 pj_time_decode(&now, &ptime);
264
265 pre = log_buffer;
Benny Prijono901a2c32008-07-28 21:15:04 +0000266 if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
267 static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:",
268 " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
269 pj_ansi_strcpy(pre, ltexts[level]);
270 pre += 6;
271 }
Benny Prijono9033e312005-11-21 02:08:39 +0000272 if (log_decor & PJ_LOG_HAS_DAY_NAME) {
273 static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
274 "Thu", "Fri", "Sat"};
Benny Prijonoed811d72006-03-10 12:57:12 +0000275 pj_ansi_strcpy(pre, wdays[ptime.wday]);
Benny Prijono9033e312005-11-21 02:08:39 +0000276 pre += 3;
277 }
278 if (log_decor & PJ_LOG_HAS_YEAR) {
279 *pre++ = ' ';
280 pre += pj_utoa(ptime.year, pre);
281 }
282 if (log_decor & PJ_LOG_HAS_MONTH) {
283 *pre++ = '-';
Benny Prijono9cb09a22007-08-30 09:35:10 +0000284 pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
Benny Prijono9033e312005-11-21 02:08:39 +0000285 }
286 if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
Benny Prijonod6e362a2008-07-19 17:53:47 +0000287 *pre++ = '-';
Benny Prijono9033e312005-11-21 02:08:39 +0000288 pre += pj_utoa_pad(ptime.day, pre, 2, '0');
289 }
290 if (log_decor & PJ_LOG_HAS_TIME) {
291 *pre++ = ' ';
292 pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
293 *pre++ = ':';
294 pre += pj_utoa_pad(ptime.min, pre, 2, '0');
295 *pre++ = ':';
296 pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
297 }
298 if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
299 *pre++ = '.';
300 pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
301 }
302 if (log_decor & PJ_LOG_HAS_SENDER) {
Benny Prijono0016d0c2006-08-11 12:42:06 +0000303 enum { SENDER_WIDTH = 14 };
Benny Prijono9033e312005-11-21 02:08:39 +0000304 int sender_len = strlen(sender);
305 *pre++ = ' ';
306 if (sender_len <= SENDER_WIDTH) {
307 while (sender_len < SENDER_WIDTH)
308 *pre++ = ' ', ++sender_len;
309 while (*sender)
310 *pre++ = *sender++;
311 } else {
312 int i;
313 for (i=0; i<SENDER_WIDTH; ++i)
314 *pre++ = *sender++;
315 }
316 }
Benny Prijonof940be42009-07-21 12:20:17 +0000317 if (log_decor & PJ_LOG_HAS_THREAD_ID) {
318 enum { THREAD_WIDTH = 12 };
319 const char *thread_name = pj_thread_get_name(pj_thread_this());
320 int thread_len = strlen(thread_name);
321 *pre++ = ' ';
322 if (thread_len <= THREAD_WIDTH) {
323 while (thread_len < THREAD_WIDTH)
324 *pre++ = ' ', ++thread_len;
325 while (*thread_name)
326 *pre++ = *thread_name++;
327 } else {
328 int i;
329 for (i=0; i<THREAD_WIDTH; ++i)
330 *pre++ = *thread_name++;
331 }
332 }
Benny Prijono9033e312005-11-21 02:08:39 +0000333
334 if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
335 *pre++ = ' ';
336
Benny Prijonod6e362a2008-07-19 17:53:47 +0000337 if (log_decor & PJ_LOG_HAS_SPACE) {
338 *pre++ = ' ';
339 }
340
Benny Prijono9033e312005-11-21 02:08:39 +0000341 len = pre - log_buffer;
342
343 /* Print the whole message to the string log_buffer. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000344 print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
345 marker);
Benny Prijonoccf95622006-02-07 18:48:01 +0000346 if (print_len < 0) {
Benny Prijono6e996dd2006-06-19 12:08:27 +0000347 level = 1;
Benny Prijonoed811d72006-03-10 12:57:12 +0000348 print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
349 "<logging error: msg too long>");
Benny Prijonoccf95622006-02-07 18:48:01 +0000350 }
351 len = len + print_len;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000352 if (len > 0 && len < (int)sizeof(log_buffer)-2) {
Benny Prijono9cf138e2006-01-19 03:58:29 +0000353 if (log_decor & PJ_LOG_HAS_CR) {
354 log_buffer[len++] = '\r';
355 }
Benny Prijono9033e312005-11-21 02:08:39 +0000356 if (log_decor & PJ_LOG_HAS_NEWLINE) {
357 log_buffer[len++] = '\n';
358 }
Benny Prijono0f3173d2006-10-13 13:48:56 +0000359 log_buffer[len] = '\0';
Benny Prijono9033e312005-11-21 02:08:39 +0000360 } else {
361 len = sizeof(log_buffer)-1;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000362 if (log_decor & PJ_LOG_HAS_CR) {
363 log_buffer[sizeof(log_buffer)-3] = '\r';
364 }
Benny Prijono9033e312005-11-21 02:08:39 +0000365 if (log_decor & PJ_LOG_HAS_NEWLINE) {
366 log_buffer[sizeof(log_buffer)-2] = '\n';
367 }
368 log_buffer[sizeof(log_buffer)-1] = '\0';
369 }
370
Benny Prijonob1a3e732009-08-05 10:58:02 +0000371 /* It should be safe to resume logging at this point. Application can
372 * recursively call the logging function inside the callback.
373 */
374 resume_logging(&saved_level);
375
Benny Prijono9033e312005-11-21 02:08:39 +0000376 if (log_writer)
377 (*log_writer)(level, log_buffer, len);
378}
379
Benny Prijono8ab968f2007-07-20 08:08:30 +0000380/*
Benny Prijono9033e312005-11-21 02:08:39 +0000381PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
382{
383 va_list arg;
384 va_start(arg, format);
385 pj_log(obj, 0, format, arg);
386 va_end(arg);
387}
Benny Prijono8ab968f2007-07-20 08:08:30 +0000388*/
Benny Prijono9033e312005-11-21 02:08:39 +0000389
390PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
391{
392 va_list arg;
393 va_start(arg, format);
394 pj_log(obj, 1, format, arg);
395 va_end(arg);
396}
397#endif /* PJ_LOG_MAX_LEVEL >= 1 */
398
399#if PJ_LOG_MAX_LEVEL >= 2
400PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
401{
402 va_list arg;
403 va_start(arg, format);
404 pj_log(obj, 2, format, arg);
405 va_end(arg);
406}
407#endif
408
409#if PJ_LOG_MAX_LEVEL >= 3
410PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
411{
412 va_list arg;
413 va_start(arg, format);
414 pj_log(obj, 3, format, arg);
415 va_end(arg);
416}
417#endif
418
419#if PJ_LOG_MAX_LEVEL >= 4
420PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
421{
422 va_list arg;
423 va_start(arg, format);
424 pj_log(obj, 4, format, arg);
425 va_end(arg);
426}
427#endif
428
429#if PJ_LOG_MAX_LEVEL >= 5
430PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
431{
432 va_list arg;
433 va_start(arg, format);
434 pj_log(obj, 5, format, arg);
435 va_end(arg);
436}
437#endif
438
439#if PJ_LOG_MAX_LEVEL >= 6
440PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
441{
442 va_list arg;
443 va_start(arg, format);
444 pj_log(obj, 6, format, arg);
445 va_end(arg);
446}
447#endif
448