lsquic_logger.c revision 3b55e6ae
1/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * LSQUIC Logger implementation.
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdarg.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#ifndef WIN32
14#include <sys/time.h>
15#endif
16#include <time.h>
17
18#define LSQUIC_LOGGER_MODULE LSQLM_LOGGER /* Quis custodiet ipsos custodes? */
19#include "lsquic_logger.h"
20#include "lsquic.h"
21
22static enum lsquic_logger_timestamp_style g_llts = LLTS_NONE;
23
24static int
25null_vprintf (void *ctx, const char *fmt, va_list ap)
26{
27    return 0;
28}
29
30
31static int
32file_vprintf (void *ctx, const char *fmt, va_list ap)
33{
34    return vfprintf((FILE *) ctx, fmt, ap);
35}
36
37
38static const struct lsquic_logger_if file_logger_if = {
39    .vprintf    = file_vprintf,
40};
41
42static const struct lsquic_logger_if null_logger_if = {
43    .vprintf    = null_vprintf,
44};
45
46static void *logger_ctx = NULL;
47static const struct lsquic_logger_if *logger_if = &null_logger_if;
48
49enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
50    [LSQLM_NOMODULE]    = LSQ_LOG_WARN,
51    [LSQLM_LOGGER]      = LSQ_LOG_WARN,
52    [LSQLM_EVENT]       = LSQ_LOG_WARN,
53    [LSQLM_ENGINE]      = LSQ_LOG_WARN,
54    [LSQLM_CONN]        = LSQ_LOG_WARN,
55    [LSQLM_RECHIST]     = LSQ_LOG_WARN,
56    [LSQLM_STREAM]      = LSQ_LOG_WARN,
57    [LSQLM_PARSE]       = LSQ_LOG_WARN,
58    [LSQLM_CFCW]        = LSQ_LOG_WARN,
59    [LSQLM_SFCW]        = LSQ_LOG_WARN,
60    [LSQLM_SENDCTL]     = LSQ_LOG_WARN,
61    [LSQLM_ALARMSET]    = LSQ_LOG_WARN,
62    [LSQLM_CRYPTO]      = LSQ_LOG_WARN,
63    [LSQLM_HANDSHAKE]   = LSQ_LOG_WARN,
64    [LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
65    [LSQLM_CUBIC]       = LSQ_LOG_WARN,
66    [LSQLM_HEADERS]     = LSQ_LOG_WARN,
67    [LSQLM_FRAME_READER]= LSQ_LOG_WARN,
68    [LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
69    [LSQLM_CONN_HASH]   = LSQ_LOG_WARN,
70    [LSQLM_ENG_HIST]    = LSQ_LOG_WARN,
71    [LSQLM_SPI]         = LSQ_LOG_WARN,
72    [LSQLM_DI]          = LSQ_LOG_WARN,
73    [LSQLM_PACER]       = LSQ_LOG_WARN,
74    [LSQLM_MIN_HEAP]    = LSQ_LOG_WARN,
75    [LSQLM_HTTP1X]      = LSQ_LOG_WARN,
76};
77
78const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
79    [LSQLM_NOMODULE]    = "",
80    [LSQLM_LOGGER]      = "logger",
81    [LSQLM_EVENT]       = "event",
82    [LSQLM_ENGINE]      = "engine",
83    [LSQLM_CONN]        = "conn",
84    [LSQLM_RECHIST]     = "rechist",
85    [LSQLM_STREAM]      = "stream",
86    [LSQLM_PARSE]       = "parse",
87    [LSQLM_CFCW]        = "cfcw",
88    [LSQLM_SFCW]        = "sfcw",
89    [LSQLM_SENDCTL]     = "sendctl",
90    [LSQLM_ALARMSET]    = "alarmset",
91    [LSQLM_CRYPTO]      = "crypto",
92    [LSQLM_HANDSHAKE]   = "handshake",
93    [LSQLM_HSK_ADAPTER] = "hsk-adapter",
94    [LSQLM_CUBIC]       = "cubic",
95    [LSQLM_HEADERS]     = "headers",
96    [LSQLM_FRAME_READER]= "frame-reader",
97    [LSQLM_FRAME_WRITER]= "frame-writer",
98    [LSQLM_CONN_HASH]   = "conn-hash",
99    [LSQLM_ENG_HIST]    = "eng-hist",
100    [LSQLM_SPI]         = "spi",
101    [LSQLM_DI]          = "di",
102    [LSQLM_PACER]       = "pacer",
103    [LSQLM_MIN_HEAP]    = "min-heap",
104    [LSQLM_HTTP1X]      = "http1x",
105};
106
107const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
108    [LSQ_LOG_ALERT]   =  "ALERT",
109    [LSQ_LOG_CRIT]    =  "CRIT",
110    [LSQ_LOG_DEBUG]   =  "DEBUG",
111    [LSQ_LOG_EMERG]   =  "EMERG",
112    [LSQ_LOG_ERROR]   =  "ERROR",
113    [LSQ_LOG_INFO]    =  "INFO",
114    [LSQ_LOG_NOTICE]  =  "NOTICE",
115    [LSQ_LOG_WARN]    =  "WARN",
116};
117
118
119static void
120lsquic_printf (const char *fmt, ...)
121{
122    va_list ap;
123    va_start(ap, fmt);
124    logger_if->vprintf(logger_ctx, fmt, ap);
125    va_end(ap);
126}
127
128
129#ifdef WIN32
130#define DELTA_EPOCH_IN_TICKS  116444736000000000Ui64
131struct timezone
132{
133    time_t tz_minuteswest;         /* minutes W of Greenwich */
134    time_t tz_dsttime;             /* type of dst correction */
135};
136
137static int
138gettimeofday (struct timeval *tv, struct timezone *tz)
139{
140    FILETIME ft;
141    uint64_t tmpres;
142    static int tzflag;
143
144    if (NULL != tv)
145    {
146        GetSystemTimeAsFileTime(&ft);
147
148        tmpres = ((uint64_t) ft.dwHighDateTime << 32)
149               | (ft.dwLowDateTime);
150
151        tmpres -= DELTA_EPOCH_IN_TICKS;
152        tv->tv_sec = tmpres / 10000000;
153        tv->tv_usec = tmpres % 1000000;
154    }
155
156    if (NULL != tz)
157    {
158        if (!tzflag)
159        {
160            _tzset();
161            tzflag++;
162        }
163        tz->tz_minuteswest = _timezone / 60;
164        tz->tz_dsttime = _daylight;
165    }
166
167    return 0;
168}
169
170
171#endif
172
173
174static void
175print_timestamp (void)
176{
177    struct tm tm;
178    struct timeval tv;
179    gettimeofday(&tv, NULL);
180#ifdef WIN32
181    {
182        time_t t = tv.tv_sec;
183#ifndef NDEBUG
184        errno_t e =
185#endif
186	localtime_s(&tm, &t);
187	assert(!e);
188    }
189#else
190    localtime_r(&tv.tv_sec, &tm);
191#endif
192    if (g_llts == LLTS_YYYYMMDD_HHMMSSUS)
193        lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%06d ",
194            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
195            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec));
196    else if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
197        lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ",
198            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
199            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
200    else if (g_llts == LLTS_HHMMSSMS)
201        lsquic_printf("%02d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min,
202                                    tm.tm_sec, (int) (tv.tv_usec / 1000));
203    else if (g_llts == LLTS_HHMMSSUS)
204        lsquic_printf("%02d:%02d:%02d.%06d ", tm.tm_hour, tm.tm_min,
205                                    tm.tm_sec, (int) tv.tv_usec);
206    else if (g_llts == LLTS_CHROMELIKE)
207        lsquic_printf("%02d%02d/%02d%02d%02d.%06d ", tm.tm_mon + 1,
208            tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
209}
210
211
212void
213lsquic_logger_log3 (enum lsq_log_level log_level,
214                    enum lsquic_logger_module module,
215                    uint64_t conn_id, uint32_t stream_id, const char *fmt, ...)
216{
217    const int saved_errno = errno;
218
219    if (g_llts != LLTS_NONE)
220        print_timestamp();
221
222    lsquic_printf("[%s] [QUIC:%"PRIu64"-%"PRIu32"] %s: ",
223        lsq_loglevel2str[log_level], conn_id, stream_id, lsqlm_to_str[module]);
224    va_list ap;
225    va_start(ap, fmt);
226    logger_if->vprintf(logger_ctx, fmt, ap);
227    va_end(ap);
228    lsquic_printf("\n");
229    errno = saved_errno;
230}
231
232
233void
234lsquic_logger_log2 (enum lsq_log_level log_level,
235                    enum lsquic_logger_module module,
236                    uint64_t conn_id, const char *fmt, ...)
237{
238    const int saved_errno = errno;
239
240    if (g_llts != LLTS_NONE)
241        print_timestamp();
242
243    lsquic_printf("[%s] [QUIC:%"PRIu64"] %s: ",
244        lsq_loglevel2str[log_level], conn_id, lsqlm_to_str[module]);
245    va_list ap;
246    va_start(ap, fmt);
247    logger_if->vprintf(logger_ctx, fmt, ap);
248    va_end(ap);
249    lsquic_printf("\n");
250    errno = saved_errno;
251}
252
253
254void
255lsquic_logger_log1 (enum lsq_log_level log_level,
256                    enum lsquic_logger_module module,
257                    const char *fmt, ...)
258{
259    const int saved_errno = errno;
260
261    if (g_llts != LLTS_NONE)
262        print_timestamp();
263
264    lsquic_printf("[%s] %s: ", lsq_loglevel2str[log_level],
265                                                lsqlm_to_str[module]);
266    va_list ap;
267    va_start(ap, fmt);
268    logger_if->vprintf(logger_ctx, fmt, ap);
269    va_end(ap);
270    lsquic_printf("\n");
271    errno = saved_errno;
272}
273
274
275void
276lsquic_logger_log0 (enum lsq_log_level log_level, const char *fmt, ...)
277{
278    const int saved_errno = errno;
279
280    if (g_llts != LLTS_NONE)
281        print_timestamp();
282
283    lsquic_printf("[%s] ", lsq_loglevel2str[log_level]);
284    va_list ap;
285    va_start(ap, fmt);
286    logger_if->vprintf(logger_ctx, fmt, ap);
287    va_end(ap);
288    lsquic_printf("\n");
289    errno = saved_errno;
290}
291
292
293void
294lsquic_logger_init (const struct lsquic_logger_if *lif, void *lctx,
295                    unsigned llts)
296{
297    logger_if  = lif;
298    logger_ctx = lctx;
299    if (llts < N_LLTS)
300        g_llts = llts;
301    LSQ_DEBUG("%s called", __func__);
302}
303
304
305enum lsquic_logger_module
306lsquic_str_to_logger_module (const char *str)
307{
308    enum lsquic_logger_module i;
309    for (i = 0; i < sizeof(lsqlm_to_str) / sizeof(lsqlm_to_str[0]); ++i)
310        if (0 == strcasecmp(lsqlm_to_str[i], str))
311            return i;
312    return -1;
313}
314
315
316enum lsq_log_level
317lsquic_str_to_log_level (const char *str)
318{
319    if (0 == strcasecmp(str, "emerg"))
320        return LSQ_LOG_EMERG;
321    if (0 == strcasecmp(str, "alert"))
322        return LSQ_LOG_ALERT;
323    if (0 == strcasecmp(str, "crit"))
324        return LSQ_LOG_CRIT;
325    if (0 == strcasecmp(str, "error"))
326        return LSQ_LOG_ERROR;
327    if (0 == strcasecmp(str, "warn"))
328        return LSQ_LOG_WARN;
329    if (0 == strcasecmp(str, "notice"))
330        return LSQ_LOG_NOTICE;
331    if (0 == strcasecmp(str, "info"))
332        return LSQ_LOG_INFO;
333    if (0 == strcasecmp(str, "debug"))
334        return LSQ_LOG_DEBUG;
335    return -1;
336}
337
338
339void
340lsquic_log_to_fstream (FILE *file, unsigned llts)
341{
342    lsquic_logger_init(&file_logger_if, file, llts);
343}
344
345
346int
347lsquic_logger_lopt (const char *optarg_orig)
348{
349    char *const optarg = strdup(optarg_orig);
350    char *mod_str;
351    int i;
352    for (i = 0; (mod_str = strtok(i ? NULL : optarg, ",")); ++i) {
353        char *level_str = strchr(mod_str, '=');
354        if (!level_str) {
355            fprintf(stderr, "Invalid module specification `%s'\n", mod_str);
356            break;
357        }
358        *level_str = '\0';
359        ++level_str;
360        enum lsquic_logger_module mod = lsquic_str_to_logger_module(mod_str);
361        if (-1 == (int) mod) {
362            fprintf(stderr, "`%s' is not a valid module name\n", mod_str);
363            break;
364        }
365        enum lsq_log_level level = lsquic_str_to_log_level(level_str);
366        if (-1 == (int) level) {
367            fprintf(stderr, "`%s' is not a valid level\n", level_str);
368            break;
369        }
370        lsq_log_levels[mod] = level;
371        LSQ_INFO("set %s to %s", mod_str, level_str);
372    }
373    free(optarg);
374    return mod_str == NULL ? 0 : -1;
375}
376
377
378int
379lsquic_set_log_level (const char *level_str)
380{
381    enum lsq_log_level level;
382    unsigned i;
383
384    level = lsquic_str_to_log_level(level_str);
385    if ((int) level >= 0)
386    {
387        for (i = 0; i < sizeof(lsq_log_levels) / sizeof(lsq_log_levels[0]); ++i)
388            lsq_log_levels[i] = level;
389        return 0;
390    }
391    else
392        return -1;
393}
394