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