blob: 5cb8b040cafe52648c092ac7ddabebc4d81f3f79 [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
Benny Prijonob1a3e732009-08-05 10:58:02 +000033static long thread_suspended_tls_id = -1;
Benny Prijono9033e312005-11-21 02:08:39 +000034static pj_log_func *log_writer = &pj_log_write;
35static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
Benny Prijonod6e362a2008-07-19 17:53:47 +000036 PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
Benny Prijonof940be42009-07-21 12:20:17 +000037 PJ_LOG_HAS_SPACE
Benny Prijonod6e362a2008-07-19 17:53:47 +000038#if defined(PJ_WIN32) && PJ_WIN32!=0
39 | PJ_LOG_HAS_COLOR
40#endif
41 ;
42
43static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
44static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
45static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT |
46 PJ_TERM_COLOR_R |
47 PJ_TERM_COLOR_G;
48static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT |
49 PJ_TERM_COLOR_R |
50 PJ_TERM_COLOR_G |
51 PJ_TERM_COLOR_B;
52static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R |
53 PJ_TERM_COLOR_G |
54 PJ_TERM_COLOR_B;
55static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R |
56 PJ_TERM_COLOR_G |
57 PJ_TERM_COLOR_B;
58static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R |
59 PJ_TERM_COLOR_G |
60 PJ_TERM_COLOR_B;
61/* Default terminal color */
62static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R |
63 PJ_TERM_COLOR_G |
64 PJ_TERM_COLOR_B;
Benny Prijono9033e312005-11-21 02:08:39 +000065
66#if PJ_LOG_USE_STACK_BUFFER==0
67static char log_buffer[PJ_LOG_MAX_SIZE];
68#endif
69
Benny Prijonob1a3e732009-08-05 10:58:02 +000070static void logging_shutdown(void)
71{
72#if PJ_HAS_THREADS
73 if (thread_suspended_tls_id != -1) {
74 pj_thread_local_free(thread_suspended_tls_id);
75 thread_suspended_tls_id = -1;
76 }
77#endif
78}
79
80pj_status_t pj_log_init(void)
81{
82#if PJ_HAS_THREADS
83 if (thread_suspended_tls_id == -1) {
84 pj_thread_local_alloc(&thread_suspended_tls_id);
85 pj_atexit(&logging_shutdown);
86 }
87#endif
88 return PJ_SUCCESS;
89}
90
Benny Prijono9033e312005-11-21 02:08:39 +000091PJ_DEF(void) pj_log_set_decor(unsigned decor)
92{
93 log_decor = decor;
94}
95
96PJ_DEF(unsigned) pj_log_get_decor(void)
97{
98 return log_decor;
99}
100
Benny Prijonod6e362a2008-07-19 17:53:47 +0000101PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
102{
103 switch (level)
104 {
105 case 0: PJ_LOG_COLOR_0 = color;
106 break;
107 case 1: PJ_LOG_COLOR_1 = color;
108 break;
109 case 2: PJ_LOG_COLOR_2 = color;
110 break;
111 case 3: PJ_LOG_COLOR_3 = color;
112 break;
113 case 4: PJ_LOG_COLOR_4 = color;
114 break;
115 case 5: PJ_LOG_COLOR_5 = color;
116 break;
117 case 6: PJ_LOG_COLOR_6 = color;
118 break;
119 /* Default terminal color */
120 case 77: PJ_LOG_COLOR_77 = color;
121 break;
122 default:
123 /* Do nothing */
124 break;
125 }
126}
127
128PJ_DEF(pj_color_t) pj_log_get_color(int level)
129{
130 switch (level) {
131 case 0:
132 return PJ_LOG_COLOR_0;
133 case 1:
134 return PJ_LOG_COLOR_1;
135 case 2:
136 return PJ_LOG_COLOR_2;
137 case 3:
138 return PJ_LOG_COLOR_3;
139 case 4:
140 return PJ_LOG_COLOR_4;
141 case 5:
142 return PJ_LOG_COLOR_5;
143 case 6:
144 return PJ_LOG_COLOR_6;
145 default:
146 /* Return default terminal color */
147 return PJ_LOG_COLOR_77;
148 }
149}
150
Benny Prijono9033e312005-11-21 02:08:39 +0000151PJ_DEF(void) pj_log_set_level(int level)
152{
Benny Prijono505e82e2007-03-05 21:08:01 +0000153 pj_log_max_level = level;
Benny Prijono9033e312005-11-21 02:08:39 +0000154}
155
Benny Prijono8ab968f2007-07-20 08:08:30 +0000156#if 1
Benny Prijono9033e312005-11-21 02:08:39 +0000157PJ_DEF(int) pj_log_get_level(void)
158{
Benny Prijono505e82e2007-03-05 21:08:01 +0000159 return pj_log_max_level;
Benny Prijono9033e312005-11-21 02:08:39 +0000160}
Benny Prijono505e82e2007-03-05 21:08:01 +0000161#endif
Benny Prijono9033e312005-11-21 02:08:39 +0000162
163PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
164{
165 log_writer = func;
166}
167
168PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
169{
170 return log_writer;
171}
172
Benny Prijonob1a3e732009-08-05 10:58:02 +0000173/* Temporarily suspend logging facility for this thread.
174 * If thread local storage/variable is not used or not initialized, then
175 * we can only suspend the logging globally across all threads. This may
176 * happen e.g. when log function is called before PJLIB is fully initialized
177 * or after PJLIB is shutdown.
178 */
179static void suspend_logging(int *saved_level)
180{
181#if PJ_HAS_THREADS
182 if (thread_suspended_tls_id != -1)
183 {
184 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE);
185 }
186 else
187#endif
188 {
189 pj_log_max_level = 0;
190 }
191 /* Save the level regardless, just in case PJLIB is shutdown
192 * between suspend and resume.
193 */
194 *saved_level = pj_log_max_level;
195}
196
197/* Resume logging facility for this thread */
198static void resume_logging(int *saved_level)
199{
200#if PJ_HAS_THREADS
201 if (thread_suspended_tls_id != -1)
202 {
203 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
204 }
205 else
206#endif
207 {
208 /* Only revert the level if application doesn't change the
209 * logging level between suspend and resume.
210 */
211 if (pj_log_max_level==0 && *saved_level)
212 pj_log_max_level = *saved_level;
213 }
214}
215
216/* Is logging facility suspended for this thread? */
217static pj_bool_t is_logging_suspended(void)
218{
219#if PJ_HAS_THREADS
220 if (thread_suspended_tls_id != -1)
221 {
222 return pj_thread_local_get(thread_suspended_tls_id) != NULL;
223 }
224 else
225#endif
226 {
227 return pj_log_max_level != 0;
228 }
229}
230
Benny Prijono9033e312005-11-21 02:08:39 +0000231PJ_DEF(void) pj_log( const char *sender, int level,
232 const char *format, va_list marker)
233{
234 pj_time_val now;
235 pj_parsed_time ptime;
236 char *pre;
237#if PJ_LOG_USE_STACK_BUFFER
238 char log_buffer[PJ_LOG_MAX_SIZE];
239#endif
Benny Prijonob1a3e732009-08-05 10:58:02 +0000240 int saved_level, len, print_len;
Benny Prijono9033e312005-11-21 02:08:39 +0000241
242 PJ_CHECK_STACK();
243
Benny Prijono505e82e2007-03-05 21:08:01 +0000244 if (level > pj_log_max_level)
Benny Prijono9033e312005-11-21 02:08:39 +0000245 return;
246
Benny Prijonob1a3e732009-08-05 10:58:02 +0000247 if (is_logging_suspended())
248 return;
249
250 /* Temporarily disable logging for this thread. Some of PJLIB APIs that
251 * this function calls below will recursively call the logging function
252 * back, hence it will cause infinite recursive calls if we allow that.
253 */
254 suspend_logging(&saved_level);
255
Benny Prijono9033e312005-11-21 02:08:39 +0000256 /* Get current date/time. */
257 pj_gettimeofday(&now);
258 pj_time_decode(&now, &ptime);
259
260 pre = log_buffer;
Benny Prijono901a2c32008-07-28 21:15:04 +0000261 if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
262 static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:",
263 " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
264 pj_ansi_strcpy(pre, ltexts[level]);
265 pre += 6;
266 }
Benny Prijono9033e312005-11-21 02:08:39 +0000267 if (log_decor & PJ_LOG_HAS_DAY_NAME) {
268 static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
269 "Thu", "Fri", "Sat"};
Benny Prijonoed811d72006-03-10 12:57:12 +0000270 pj_ansi_strcpy(pre, wdays[ptime.wday]);
Benny Prijono9033e312005-11-21 02:08:39 +0000271 pre += 3;
272 }
273 if (log_decor & PJ_LOG_HAS_YEAR) {
274 *pre++ = ' ';
275 pre += pj_utoa(ptime.year, pre);
276 }
277 if (log_decor & PJ_LOG_HAS_MONTH) {
278 *pre++ = '-';
Benny Prijono9cb09a22007-08-30 09:35:10 +0000279 pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
Benny Prijono9033e312005-11-21 02:08:39 +0000280 }
281 if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
Benny Prijonod6e362a2008-07-19 17:53:47 +0000282 *pre++ = '-';
Benny Prijono9033e312005-11-21 02:08:39 +0000283 pre += pj_utoa_pad(ptime.day, pre, 2, '0');
284 }
285 if (log_decor & PJ_LOG_HAS_TIME) {
286 *pre++ = ' ';
287 pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
288 *pre++ = ':';
289 pre += pj_utoa_pad(ptime.min, pre, 2, '0');
290 *pre++ = ':';
291 pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
292 }
293 if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
294 *pre++ = '.';
295 pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
296 }
297 if (log_decor & PJ_LOG_HAS_SENDER) {
Benny Prijono0016d0c2006-08-11 12:42:06 +0000298 enum { SENDER_WIDTH = 14 };
Benny Prijono9033e312005-11-21 02:08:39 +0000299 int sender_len = strlen(sender);
300 *pre++ = ' ';
301 if (sender_len <= SENDER_WIDTH) {
302 while (sender_len < SENDER_WIDTH)
303 *pre++ = ' ', ++sender_len;
304 while (*sender)
305 *pre++ = *sender++;
306 } else {
307 int i;
308 for (i=0; i<SENDER_WIDTH; ++i)
309 *pre++ = *sender++;
310 }
311 }
Benny Prijonof940be42009-07-21 12:20:17 +0000312 if (log_decor & PJ_LOG_HAS_THREAD_ID) {
313 enum { THREAD_WIDTH = 12 };
314 const char *thread_name = pj_thread_get_name(pj_thread_this());
315 int thread_len = strlen(thread_name);
316 *pre++ = ' ';
317 if (thread_len <= THREAD_WIDTH) {
318 while (thread_len < THREAD_WIDTH)
319 *pre++ = ' ', ++thread_len;
320 while (*thread_name)
321 *pre++ = *thread_name++;
322 } else {
323 int i;
324 for (i=0; i<THREAD_WIDTH; ++i)
325 *pre++ = *thread_name++;
326 }
327 }
Benny Prijono9033e312005-11-21 02:08:39 +0000328
329 if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
330 *pre++ = ' ';
331
Benny Prijonod6e362a2008-07-19 17:53:47 +0000332 if (log_decor & PJ_LOG_HAS_SPACE) {
333 *pre++ = ' ';
334 }
335
Benny Prijono9033e312005-11-21 02:08:39 +0000336 len = pre - log_buffer;
337
338 /* Print the whole message to the string log_buffer. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000339 print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
340 marker);
Benny Prijonoccf95622006-02-07 18:48:01 +0000341 if (print_len < 0) {
Benny Prijono6e996dd2006-06-19 12:08:27 +0000342 level = 1;
Benny Prijonoed811d72006-03-10 12:57:12 +0000343 print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
344 "<logging error: msg too long>");
Benny Prijonoccf95622006-02-07 18:48:01 +0000345 }
346 len = len + print_len;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000347 if (len > 0 && len < (int)sizeof(log_buffer)-2) {
Benny Prijono9cf138e2006-01-19 03:58:29 +0000348 if (log_decor & PJ_LOG_HAS_CR) {
349 log_buffer[len++] = '\r';
350 }
Benny Prijono9033e312005-11-21 02:08:39 +0000351 if (log_decor & PJ_LOG_HAS_NEWLINE) {
352 log_buffer[len++] = '\n';
353 }
Benny Prijono0f3173d2006-10-13 13:48:56 +0000354 log_buffer[len] = '\0';
Benny Prijono9033e312005-11-21 02:08:39 +0000355 } else {
356 len = sizeof(log_buffer)-1;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000357 if (log_decor & PJ_LOG_HAS_CR) {
358 log_buffer[sizeof(log_buffer)-3] = '\r';
359 }
Benny Prijono9033e312005-11-21 02:08:39 +0000360 if (log_decor & PJ_LOG_HAS_NEWLINE) {
361 log_buffer[sizeof(log_buffer)-2] = '\n';
362 }
363 log_buffer[sizeof(log_buffer)-1] = '\0';
364 }
365
Benny Prijonob1a3e732009-08-05 10:58:02 +0000366 /* It should be safe to resume logging at this point. Application can
367 * recursively call the logging function inside the callback.
368 */
369 resume_logging(&saved_level);
370
Benny Prijono9033e312005-11-21 02:08:39 +0000371 if (log_writer)
372 (*log_writer)(level, log_buffer, len);
373}
374
Benny Prijono8ab968f2007-07-20 08:08:30 +0000375/*
Benny Prijono9033e312005-11-21 02:08:39 +0000376PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
377{
378 va_list arg;
379 va_start(arg, format);
380 pj_log(obj, 0, format, arg);
381 va_end(arg);
382}
Benny Prijono8ab968f2007-07-20 08:08:30 +0000383*/
Benny Prijono9033e312005-11-21 02:08:39 +0000384
385PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
386{
387 va_list arg;
388 va_start(arg, format);
389 pj_log(obj, 1, format, arg);
390 va_end(arg);
391}
392#endif /* PJ_LOG_MAX_LEVEL >= 1 */
393
394#if PJ_LOG_MAX_LEVEL >= 2
395PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
396{
397 va_list arg;
398 va_start(arg, format);
399 pj_log(obj, 2, format, arg);
400 va_end(arg);
401}
402#endif
403
404#if PJ_LOG_MAX_LEVEL >= 3
405PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
406{
407 va_list arg;
408 va_start(arg, format);
409 pj_log(obj, 3, format, arg);
410 va_end(arg);
411}
412#endif
413
414#if PJ_LOG_MAX_LEVEL >= 4
415PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
416{
417 va_list arg;
418 va_start(arg, format);
419 pj_log(obj, 4, format, arg);
420 va_end(arg);
421}
422#endif
423
424#if PJ_LOG_MAX_LEVEL >= 5
425PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
426{
427 va_list arg;
428 va_start(arg, format);
429 pj_log(obj, 5, format, arg);
430 va_end(arg);
431}
432#endif
433
434#if PJ_LOG_MAX_LEVEL >= 6
435PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
436{
437 va_list arg;
438 va_start(arg, format);
439 pj_log(obj, 6, format, arg);
440 va_end(arg);
441}
442#endif
443