Coverage Report

Created: 2025-08-29 06:52

/src/pjsip/pjlib/src/pj/log.c
Line
Count
Source (jump to first uncovered line)
1
/* 
2
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
3
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
18
 */
19
#include <pj/types.h>
20
#include <pj/log.h>
21
#include <pj/string.h>
22
#include <pj/os.h>
23
#include <pj/compat/stdarg.h>
24
25
#if PJ_LOG_MAX_LEVEL >= 1
26
27
#if 0
28
PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL;
29
#else
30
static int pj_log_max_level = PJ_LOG_MAX_LEVEL;
31
#endif
32
33
static void *g_last_thread;
34
35
#if PJ_HAS_THREADS
36
static long thread_suspended_tls_id = -1;
37
#  if PJ_LOG_ENABLE_INDENT
38
static long thread_indent_tls_id = -1;
39
#  endif
40
#endif
41
42
#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS
43
static int log_indent;
44
#endif
45
46
static pj_log_func *log_writer = &pj_log_write;
47
static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
48
                            PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
49
                            PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC |
50
                            PJ_LOG_HAS_INDENT
51
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
52
    (defined(PJ_WIN64) && PJ_WIN64!=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
1
#define LOG_MAX_INDENT          80
85
86
#if PJ_HAS_THREADS
87
static void logging_shutdown(void)
88
0
{
89
0
    if (thread_suspended_tls_id != -1) {
90
0
        pj_thread_local_free(thread_suspended_tls_id);
91
0
        thread_suspended_tls_id = -1;
92
0
    }
93
0
#  if PJ_LOG_ENABLE_INDENT
94
0
    if (thread_indent_tls_id != -1) {
95
0
        pj_thread_local_free(thread_indent_tls_id);
96
0
        thread_indent_tls_id = -1;
97
0
    }
98
0
#  endif
99
0
}
100
#endif  /* PJ_HAS_THREADS */
101
102
#if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
103
PJ_DEF(void) pj_log_set_indent(int indent)
104
0
{
105
0
    if (indent < 0) indent = 0;
106
0
    pj_thread_local_set(thread_indent_tls_id, (void*)(pj_ssize_t)indent);
107
0
}
108
109
static int log_get_raw_indent(void)
110
1
{
111
1
    return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id);
112
1
}
113
114
#else
115
PJ_DEF(void) pj_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
PJ_DEF(int) pj_log_get_indent(void)
128
1
{
129
1
    int indent = log_get_raw_indent();
130
1
    return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
131
1
}
132
133
PJ_DEF(void) pj_log_add_indent(int indent)
134
0
{
135
0
    pj_log_set_indent(log_get_raw_indent() + indent);
136
0
}
137
138
PJ_DEF(void) pj_log_push_indent(void)
139
0
{
140
0
    pj_log_add_indent(PJ_LOG_INDENT_SIZE);
141
0
}
142
143
PJ_DEF(void) pj_log_pop_indent(void)
144
0
{
145
0
    pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
146
0
}
147
148
pj_status_t pj_log_init(void)
149
1
{
150
1
#if PJ_HAS_THREADS
151
1
    if (thread_suspended_tls_id == -1) {
152
1
        pj_status_t status;
153
1
        status = pj_thread_local_alloc(&thread_suspended_tls_id);
154
1
        if (status != PJ_SUCCESS) {
155
0
            thread_suspended_tls_id = -1;
156
0
            return status;
157
0
        }
158
159
1
#  if PJ_LOG_ENABLE_INDENT
160
1
        status = pj_thread_local_alloc(&thread_indent_tls_id);
161
1
        if (status != PJ_SUCCESS) {
162
0
            pj_thread_local_free(thread_suspended_tls_id);
163
0
            thread_suspended_tls_id = -1;
164
0
            thread_indent_tls_id = -1;
165
0
            return status;
166
0
        }
167
1
#  endif
168
1
        pj_atexit(&logging_shutdown);
169
1
    }
170
1
#endif
171
1
    g_last_thread = NULL;
172
173
    /* Normalize log decor, e.g: unset thread flags when threading is
174
     * disabled.
175
     */
176
1
    pj_log_set_decor(pj_log_get_decor());
177
178
1
    return PJ_SUCCESS;
179
1
}
180
181
PJ_DEF(void) pj_log_set_decor(unsigned decor)
182
1
{
183
1
    log_decor = decor;
184
185
#if !PJ_HAS_THREADS
186
    /* Unset thread related flags */
187
    if (log_decor & PJ_LOG_HAS_THREAD_ID) {
188
       log_decor &= ~(PJ_LOG_HAS_THREAD_ID);
189
    }
190
    if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
191
       log_decor &= ~(PJ_LOG_HAS_THREAD_SWC);
192
    }
193
#endif
194
1
}
195
196
PJ_DEF(unsigned) pj_log_get_decor(void)
197
2
{
198
2
    return log_decor;
199
2
}
200
201
PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
202
0
{
203
0
    switch (level) 
204
0
    {
205
0
        case 0: PJ_LOG_COLOR_0 = color; 
206
0
            break;
207
0
        case 1: PJ_LOG_COLOR_1 = color; 
208
0
            break;
209
0
        case 2: PJ_LOG_COLOR_2 = color; 
210
0
            break;
211
0
        case 3: PJ_LOG_COLOR_3 = color; 
212
0
            break;
213
0
        case 4: PJ_LOG_COLOR_4 = color; 
214
0
            break;
215
0
        case 5: PJ_LOG_COLOR_5 = color; 
216
0
            break;
217
0
        case 6: PJ_LOG_COLOR_6 = color; 
218
0
            break;
219
        /* Default terminal color */
220
0
        case 77: PJ_LOG_COLOR_77 = color; 
221
0
            break;
222
0
        default:
223
            /* Do nothing */
224
0
            break;
225
0
    }
226
0
}
227
228
PJ_DEF(pj_color_t) pj_log_get_color(int level)
229
0
{
230
0
    switch (level) {
231
0
        case 0:
232
0
            return PJ_LOG_COLOR_0;
233
0
        case 1:
234
0
            return PJ_LOG_COLOR_1;
235
0
        case 2:
236
0
            return PJ_LOG_COLOR_2;
237
0
        case 3:
238
0
            return PJ_LOG_COLOR_3;
239
0
        case 4:
240
0
            return PJ_LOG_COLOR_4;
241
0
        case 5:
242
0
            return PJ_LOG_COLOR_5;
243
0
        case 6:
244
0
            return PJ_LOG_COLOR_6;
245
0
        default:
246
            /* Return default terminal color */
247
0
            return PJ_LOG_COLOR_77;
248
0
    }
249
0
}
250
251
PJ_DEF(void) pj_log_set_level(int level)
252
2.11k
{
253
2.11k
    pj_log_max_level = level;
254
2.11k
}
255
256
#if 1
257
PJ_DEF(int) pj_log_get_level(void)
258
106k
{
259
106k
    return pj_log_max_level;
260
106k
}
261
#endif
262
263
PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
264
0
{
265
0
    log_writer = func;
266
0
}
267
268
PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
269
0
{
270
0
    return log_writer;
271
0
}
272
273
/* Temporarily suspend logging facility for this thread.
274
 * If thread local storage/variable is not used or not initialized, then
275
 * we can only suspend the logging globally across all threads. This may
276
 * happen e.g. when log function is called before PJLIB is fully initialized
277
 * or after PJLIB is shutdown.
278
 */
279
static void suspend_logging(int *saved_level)
280
1
{
281
    /* Save the level regardless, just in case PJLIB is shutdown
282
     * between suspend and resume.
283
     */
284
1
    *saved_level = pj_log_max_level;
285
286
1
#if PJ_HAS_THREADS
287
1
    if (thread_suspended_tls_id != -1) 
288
1
    {
289
1
        static pj_ssize_t suspend_log = PJ_TRUE;
290
1
        pj_thread_local_set(thread_suspended_tls_id, 
291
1
                            (void*)&suspend_log);
292
1
    } 
293
0
    else
294
0
#endif
295
0
    {
296
0
        pj_log_max_level = 0;
297
0
    }
298
1
}
299
300
/* Resume logging facility for this thread */
301
static void resume_logging(int *saved_level)
302
1
{
303
1
#if PJ_HAS_THREADS
304
1
    if (thread_suspended_tls_id != -1) 
305
1
    {
306
1
        static pj_ssize_t suspend_log = PJ_FALSE;
307
1
        pj_thread_local_set(thread_suspended_tls_id, 
308
1
                            (void*)&suspend_log);
309
1
    }
310
0
    else
311
0
#endif
312
0
    {
313
        /* Only revert the level if application doesn't change the
314
         * logging level between suspend and resume.
315
         */
316
0
        if (pj_log_max_level==0 && *saved_level)
317
0
            pj_log_max_level = *saved_level;
318
0
    }
319
1
}
320
321
/* Is logging facility suspended for this thread? */
322
static pj_bool_t is_logging_suspended(void)
323
1
{
324
1
#if PJ_HAS_THREADS
325
1
    if (thread_suspended_tls_id != -1) 
326
1
    {
327
1
        pj_ssize_t *ils;
328
1
        ils = (pj_ssize_t *)pj_thread_local_get(thread_suspended_tls_id);
329
1
        return (ils? (*ils == PJ_TRUE): PJ_FALSE);
330
1
    }
331
0
    else
332
0
#endif
333
0
    {
334
0
        return pj_log_max_level == 0;
335
0
    }
336
1
}
337
338
PJ_DEF(void) pj_log( const char *sender, int level, 
339
                     const char *format, va_list marker)
340
12.6k
{
341
12.6k
    pj_time_val now;
342
12.6k
    pj_parsed_time ptime;
343
12.6k
    char *pre;
344
12.6k
#if PJ_LOG_USE_STACK_BUFFER
345
12.6k
    char log_buffer[PJ_LOG_MAX_SIZE];
346
12.6k
#endif
347
12.6k
    int saved_level, len, print_len;
348
349
12.6k
    PJ_CHECK_STACK();
350
351
12.6k
    if (level > pj_log_max_level)
352
12.6k
        return;
353
354
1
    if (is_logging_suspended())
355
0
        return;
356
357
    /* Temporarily disable logging for this thread. Some of PJLIB APIs that
358
     * this function calls below will recursively call the logging function 
359
     * back, hence it will cause infinite recursive calls if we allow that.
360
     */
361
1
    suspend_logging(&saved_level);
362
363
    /* Get current date/time. */
364
1
    pj_gettimeofday(&now);
365
1
    pj_time_decode(&now, &ptime);
366
367
1
    pre = log_buffer;
368
1
    if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
369
0
        static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:", 
370
0
                              " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
371
0
        pj_ansi_strxcpy(pre, ltexts[level], PJ_LOG_MAX_SIZE);
372
0
        pre += 6;
373
0
    }
374
1
    if (log_decor & PJ_LOG_HAS_DAY_NAME) {
375
0
        static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
376
0
                                       "Thu", "Fri", "Sat"};
377
0
        pj_ansi_strxcpy(pre, wdays[ptime.wday], PJ_LOG_MAX_SIZE-6);
378
0
        pre += 3;
379
0
    }
380
1
    if (log_decor & PJ_LOG_HAS_YEAR) {
381
0
        if (pre!=log_buffer) *pre++ = ' ';
382
0
        pre += pj_utoa(ptime.year, pre);
383
0
    }
384
1
    if (log_decor & PJ_LOG_HAS_MONTH) {
385
0
        *pre++ = '-';
386
0
        pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
387
0
    }
388
1
    if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
389
0
        *pre++ = '-';
390
0
        pre += pj_utoa_pad(ptime.day, pre, 2, '0');
391
0
    }
392
1
    if (log_decor & PJ_LOG_HAS_TIME) {
393
1
        if (pre!=log_buffer) *pre++ = ' ';
394
1
        pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
395
1
        *pre++ = ':';
396
1
        pre += pj_utoa_pad(ptime.min, pre, 2, '0');
397
1
        *pre++ = ':';
398
1
        pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
399
1
    }
400
1
    if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
401
1
        *pre++ = '.';
402
1
        pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
403
1
    }
404
1
    if (log_decor & PJ_LOG_HAS_SENDER) {
405
1
        enum { SENDER_WIDTH = PJ_LOG_SENDER_WIDTH };
406
1
        pj_size_t sender_len = strlen(sender);
407
1
        if (pre!=log_buffer) *pre++ = ' ';
408
1
        if (sender_len <= SENDER_WIDTH) {
409
9
            while (sender_len < SENDER_WIDTH)
410
8
                *pre++ = ' ', ++sender_len;
411
15
            while (*sender)
412
14
                *pre++ = *sender++;
413
1
        } else {
414
0
            int i;
415
0
            for (i=0; i<SENDER_WIDTH; ++i)
416
0
                *pre++ = *sender++;
417
0
        }
418
1
    }
419
1
    if (log_decor & PJ_LOG_HAS_THREAD_ID) {
420
0
        enum { THREAD_WIDTH = PJ_LOG_THREAD_WIDTH };
421
0
        const char *thread_name = pj_thread_get_name(pj_thread_this());
422
0
        pj_size_t thread_len = strlen(thread_name);
423
0
        *pre++ = ' ';
424
0
        if (thread_len <= THREAD_WIDTH) {
425
0
            while (thread_len < THREAD_WIDTH)
426
0
                *pre++ = ' ', ++thread_len;
427
0
            while (*thread_name)
428
0
                *pre++ = *thread_name++;
429
0
        } else {
430
0
            int i;
431
0
            for (i=0; i<THREAD_WIDTH; ++i)
432
0
                *pre++ = *thread_name++;
433
0
        }
434
0
    }
435
436
1
    if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
437
1
        *pre++ = ' ';
438
439
1
    if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
440
1
        void *current_thread = (void*)pj_thread_this();
441
1
        if (current_thread != g_last_thread) {
442
1
            *pre++ = '!';
443
1
            g_last_thread = current_thread;
444
1
        } else {
445
0
            *pre++ = ' ';
446
0
        }
447
1
    } else if (log_decor & PJ_LOG_HAS_SPACE) {
448
0
        *pre++ = ' ';
449
0
    }
450
451
1
#if PJ_LOG_ENABLE_INDENT
452
1
    if (log_decor & PJ_LOG_HAS_INDENT) {
453
1
        int indent = pj_log_get_indent();
454
1
        if (indent > 0) {
455
0
            pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
456
0
            pre += indent;
457
0
        }
458
1
    }
459
1
#endif
460
461
1
    len = (int)(pre - log_buffer);
462
463
    /* Print the whole message to the string log_buffer. */
464
1
    print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, 
465
1
                                  marker);
466
1
    if (print_len < 0) {
467
0
        level = 1;
468
0
        print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, 
469
0
                                     "<logging error: msg too long>");
470
0
    }
471
1
    if (print_len < 0 || print_len >= (int)(sizeof(log_buffer)-len)) {
472
0
        print_len = sizeof(log_buffer) - len - 1;
473
0
    }
474
1
    len = len + print_len;
475
1
    if (len >= 0 && len < (int)sizeof(log_buffer)-2) {
476
1
        if (log_decor & PJ_LOG_HAS_CR) {
477
0
            log_buffer[len++] = '\r';
478
0
        }
479
1
        if (log_decor & PJ_LOG_HAS_NEWLINE) {
480
1
            log_buffer[len++] = '\n';
481
1
        }
482
1
        log_buffer[len] = '\0';
483
1
    } else {
484
0
        len = sizeof(log_buffer)-1;
485
0
        if (log_decor & PJ_LOG_HAS_CR) {
486
0
            log_buffer[sizeof(log_buffer)-3] = '\r';
487
0
        }
488
0
        if (log_decor & PJ_LOG_HAS_NEWLINE) {
489
0
            log_buffer[sizeof(log_buffer)-2] = '\n';
490
0
        }
491
0
        log_buffer[sizeof(log_buffer)-1] = '\0';
492
0
    }
493
494
    /* It should be safe to resume logging at this point. Application can
495
     * recursively call the logging function inside the callback.
496
     */
497
1
    resume_logging(&saved_level);
498
499
1
    if (log_writer)
500
1
        (*log_writer)(level, log_buffer, len);
501
1
}
502
503
/*
504
PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
505
{
506
    va_list arg;
507
    va_start(arg, format);
508
    pj_log(obj, 0, format, arg);
509
    va_end(arg);
510
}
511
*/
512
513
PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
514
0
{
515
0
    va_list arg;
516
0
    va_start(arg, format);
517
0
    pj_log(obj, 1, format, arg);
518
0
    va_end(arg);
519
0
}
520
#endif  /* PJ_LOG_MAX_LEVEL >= 1 */
521
522
#if PJ_LOG_MAX_LEVEL >= 2
523
PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
524
0
{
525
0
    va_list arg;
526
0
    va_start(arg, format);
527
0
    pj_log(obj, 2, format, arg);
528
0
    va_end(arg);
529
0
}
530
#endif
531
532
#if PJ_LOG_MAX_LEVEL >= 3
533
PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
534
0
{
535
0
    va_list arg;
536
0
    va_start(arg, format);
537
0
    pj_log(obj, 3, format, arg);
538
0
    va_end(arg);
539
0
}
540
#endif
541
542
#if PJ_LOG_MAX_LEVEL >= 4
543
PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
544
1
{
545
1
    va_list arg;
546
1
    va_start(arg, format);
547
1
    pj_log(obj, 4, format, arg);
548
1
    va_end(arg);
549
1
}
550
#endif
551
552
#if PJ_LOG_MAX_LEVEL >= 5
553
PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
554
0
{
555
0
    va_list arg;
556
0
    va_start(arg, format);
557
0
    pj_log(obj, 5, format, arg);
558
0
    va_end(arg);
559
0
}
560
#endif
561
562
#if PJ_LOG_MAX_LEVEL >= 6
563
PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
564
{
565
    va_list arg;
566
    va_start(arg, format);
567
    pj_log(obj, 6, format, arg);
568
    va_end(arg);
569
}
570
#endif
571