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