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