blob: 0db0d169d2450431b7925a3d16a9dc144605fa2a [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{
Nanang Izzuddine5fe4202009-08-12 17:50:52 +0000181 /* Save the level regardless, just in case PJLIB is shutdown
182 * between suspend and resume.
183 */
184 *saved_level = pj_log_max_level;
185
Benny Prijonob1a3e732009-08-05 10:58:02 +0000186#if PJ_HAS_THREADS
187 if (thread_suspended_tls_id != -1)
188 {
189 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_TRUE);
190 }
191 else
192#endif
193 {
194 pj_log_max_level = 0;
195 }
Benny Prijonob1a3e732009-08-05 10:58:02 +0000196}
197
198/* Resume logging facility for this thread */
199static void resume_logging(int *saved_level)
200{
201#if PJ_HAS_THREADS
202 if (thread_suspended_tls_id != -1)
203 {
204 pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
205 }
206 else
207#endif
208 {
209 /* Only revert the level if application doesn't change the
210 * logging level between suspend and resume.
211 */
212 if (pj_log_max_level==0 && *saved_level)
213 pj_log_max_level = *saved_level;
214 }
215}
216
217/* Is logging facility suspended for this thread? */
218static pj_bool_t is_logging_suspended(void)
219{
220#if PJ_HAS_THREADS
221 if (thread_suspended_tls_id != -1)
222 {
223 return pj_thread_local_get(thread_suspended_tls_id) != NULL;
224 }
225 else
226#endif
227 {
Nanang Izzuddine5fe4202009-08-12 17:50:52 +0000228 return pj_log_max_level == 0;
Benny Prijonob1a3e732009-08-05 10:58:02 +0000229 }
230}
231
Benny Prijono9033e312005-11-21 02:08:39 +0000232PJ_DEF(void) pj_log( const char *sender, int level,
233 const char *format, va_list marker)
234{
235 pj_time_val now;
236 pj_parsed_time ptime;
237 char *pre;
238#if PJ_LOG_USE_STACK_BUFFER
239 char log_buffer[PJ_LOG_MAX_SIZE];
240#endif
Benny Prijonob1a3e732009-08-05 10:58:02 +0000241 int saved_level, len, print_len;
Benny Prijono9033e312005-11-21 02:08:39 +0000242
243 PJ_CHECK_STACK();
244
Benny Prijono505e82e2007-03-05 21:08:01 +0000245 if (level > pj_log_max_level)
Benny Prijono9033e312005-11-21 02:08:39 +0000246 return;
247
Benny Prijonob1a3e732009-08-05 10:58:02 +0000248 if (is_logging_suspended())
249 return;
250
251 /* Temporarily disable logging for this thread. Some of PJLIB APIs that
252 * this function calls below will recursively call the logging function
253 * back, hence it will cause infinite recursive calls if we allow that.
254 */
255 suspend_logging(&saved_level);
256
Benny Prijono9033e312005-11-21 02:08:39 +0000257 /* Get current date/time. */
258 pj_gettimeofday(&now);
259 pj_time_decode(&now, &ptime);
260
261 pre = log_buffer;
Benny Prijono901a2c32008-07-28 21:15:04 +0000262 if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
263 static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:",
264 " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
265 pj_ansi_strcpy(pre, ltexts[level]);
266 pre += 6;
267 }
Benny Prijono9033e312005-11-21 02:08:39 +0000268 if (log_decor & PJ_LOG_HAS_DAY_NAME) {
269 static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
270 "Thu", "Fri", "Sat"};
Benny Prijonoed811d72006-03-10 12:57:12 +0000271 pj_ansi_strcpy(pre, wdays[ptime.wday]);
Benny Prijono9033e312005-11-21 02:08:39 +0000272 pre += 3;
273 }
274 if (log_decor & PJ_LOG_HAS_YEAR) {
275 *pre++ = ' ';
276 pre += pj_utoa(ptime.year, pre);
277 }
278 if (log_decor & PJ_LOG_HAS_MONTH) {
279 *pre++ = '-';
Benny Prijono9cb09a22007-08-30 09:35:10 +0000280 pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
Benny Prijono9033e312005-11-21 02:08:39 +0000281 }
282 if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
Benny Prijonod6e362a2008-07-19 17:53:47 +0000283 *pre++ = '-';
Benny Prijono9033e312005-11-21 02:08:39 +0000284 pre += pj_utoa_pad(ptime.day, pre, 2, '0');
285 }
286 if (log_decor & PJ_LOG_HAS_TIME) {
287 *pre++ = ' ';
288 pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
289 *pre++ = ':';
290 pre += pj_utoa_pad(ptime.min, pre, 2, '0');
291 *pre++ = ':';
292 pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
293 }
294 if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
295 *pre++ = '.';
296 pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
297 }
298 if (log_decor & PJ_LOG_HAS_SENDER) {
Benny Prijono0016d0c2006-08-11 12:42:06 +0000299 enum { SENDER_WIDTH = 14 };
Benny Prijono9033e312005-11-21 02:08:39 +0000300 int sender_len = strlen(sender);
301 *pre++ = ' ';
302 if (sender_len <= SENDER_WIDTH) {
303 while (sender_len < SENDER_WIDTH)
304 *pre++ = ' ', ++sender_len;
305 while (*sender)
306 *pre++ = *sender++;
307 } else {
308 int i;
309 for (i=0; i<SENDER_WIDTH; ++i)
310 *pre++ = *sender++;
311 }
312 }
Benny Prijonof940be42009-07-21 12:20:17 +0000313 if (log_decor & PJ_LOG_HAS_THREAD_ID) {
314 enum { THREAD_WIDTH = 12 };
315 const char *thread_name = pj_thread_get_name(pj_thread_this());
316 int thread_len = strlen(thread_name);
317 *pre++ = ' ';
318 if (thread_len <= THREAD_WIDTH) {
319 while (thread_len < THREAD_WIDTH)
320 *pre++ = ' ', ++thread_len;
321 while (*thread_name)
322 *pre++ = *thread_name++;
323 } else {
324 int i;
325 for (i=0; i<THREAD_WIDTH; ++i)
326 *pre++ = *thread_name++;
327 }
328 }
Benny Prijono9033e312005-11-21 02:08:39 +0000329
330 if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
331 *pre++ = ' ';
332
Benny Prijonod6e362a2008-07-19 17:53:47 +0000333 if (log_decor & PJ_LOG_HAS_SPACE) {
334 *pre++ = ' ';
335 }
336
Benny Prijono9033e312005-11-21 02:08:39 +0000337 len = pre - log_buffer;
338
339 /* Print the whole message to the string log_buffer. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000340 print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format,
341 marker);
Benny Prijonoccf95622006-02-07 18:48:01 +0000342 if (print_len < 0) {
Benny Prijono6e996dd2006-06-19 12:08:27 +0000343 level = 1;
Benny Prijonoed811d72006-03-10 12:57:12 +0000344 print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len,
345 "<logging error: msg too long>");
Benny Prijonoccf95622006-02-07 18:48:01 +0000346 }
347 len = len + print_len;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000348 if (len > 0 && len < (int)sizeof(log_buffer)-2) {
Benny Prijono9cf138e2006-01-19 03:58:29 +0000349 if (log_decor & PJ_LOG_HAS_CR) {
350 log_buffer[len++] = '\r';
351 }
Benny Prijono9033e312005-11-21 02:08:39 +0000352 if (log_decor & PJ_LOG_HAS_NEWLINE) {
353 log_buffer[len++] = '\n';
354 }
Benny Prijono0f3173d2006-10-13 13:48:56 +0000355 log_buffer[len] = '\0';
Benny Prijono9033e312005-11-21 02:08:39 +0000356 } else {
357 len = sizeof(log_buffer)-1;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000358 if (log_decor & PJ_LOG_HAS_CR) {
359 log_buffer[sizeof(log_buffer)-3] = '\r';
360 }
Benny Prijono9033e312005-11-21 02:08:39 +0000361 if (log_decor & PJ_LOG_HAS_NEWLINE) {
362 log_buffer[sizeof(log_buffer)-2] = '\n';
363 }
364 log_buffer[sizeof(log_buffer)-1] = '\0';
365 }
366
Benny Prijonob1a3e732009-08-05 10:58:02 +0000367 /* It should be safe to resume logging at this point. Application can
368 * recursively call the logging function inside the callback.
369 */
370 resume_logging(&saved_level);
371
Benny Prijono9033e312005-11-21 02:08:39 +0000372 if (log_writer)
373 (*log_writer)(level, log_buffer, len);
374}
375
Benny Prijono8ab968f2007-07-20 08:08:30 +0000376/*
Benny Prijono9033e312005-11-21 02:08:39 +0000377PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
378{
379 va_list arg;
380 va_start(arg, format);
381 pj_log(obj, 0, format, arg);
382 va_end(arg);
383}
Benny Prijono8ab968f2007-07-20 08:08:30 +0000384*/
Benny Prijono9033e312005-11-21 02:08:39 +0000385
386PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
387{
388 va_list arg;
389 va_start(arg, format);
390 pj_log(obj, 1, format, arg);
391 va_end(arg);
392}
393#endif /* PJ_LOG_MAX_LEVEL >= 1 */
394
395#if PJ_LOG_MAX_LEVEL >= 2
396PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
397{
398 va_list arg;
399 va_start(arg, format);
400 pj_log(obj, 2, format, arg);
401 va_end(arg);
402}
403#endif
404
405#if PJ_LOG_MAX_LEVEL >= 3
406PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
407{
408 va_list arg;
409 va_start(arg, format);
410 pj_log(obj, 3, format, arg);
411 va_end(arg);
412}
413#endif
414
415#if PJ_LOG_MAX_LEVEL >= 4
416PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
417{
418 va_list arg;
419 va_start(arg, format);
420 pj_log(obj, 4, format, arg);
421 va_end(arg);
422}
423#endif
424
425#if PJ_LOG_MAX_LEVEL >= 5
426PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
427{
428 va_list arg;
429 va_start(arg, format);
430 pj_log(obj, 5, format, arg);
431 va_end(arg);
432}
433#endif
434
435#if PJ_LOG_MAX_LEVEL >= 6
436PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
437{
438 va_list arg;
439 va_start(arg, format);
440 pj_log(obj, 6, format, arg);
441 va_end(arg);
442}
443#endif
444