1*62ae1149SStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause */ 2*62ae1149SStephen Hemminger 3*62ae1149SStephen Hemminger #include <limits.h> 4*62ae1149SStephen Hemminger #include <stdarg.h> 5*62ae1149SStephen Hemminger #include <stdbool.h> 6*62ae1149SStephen Hemminger #include <stdint.h> 7*62ae1149SStephen Hemminger #include <stdio.h> 8*62ae1149SStephen Hemminger #include <stdlib.h> 9*62ae1149SStephen Hemminger #include <string.h> 10*62ae1149SStephen Hemminger #include <time.h> 11*62ae1149SStephen Hemminger 12*62ae1149SStephen Hemminger #include <rte_common.h> 13*62ae1149SStephen Hemminger #include <rte_stdatomic.h> 14*62ae1149SStephen Hemminger #include <rte_time.h> 15*62ae1149SStephen Hemminger 16*62ae1149SStephen Hemminger #ifdef RTE_EXEC_ENV_WINDOWS 17*62ae1149SStephen Hemminger #include <rte_os_shim.h> 18*62ae1149SStephen Hemminger #endif 19*62ae1149SStephen Hemminger 20*62ae1149SStephen Hemminger #include "log_internal.h" 21*62ae1149SStephen Hemminger #include "log_private.h" 22*62ae1149SStephen Hemminger 23*62ae1149SStephen Hemminger #ifndef NS_PER_SEC 24*62ae1149SStephen Hemminger #define NS_PER_SEC 1E9 25*62ae1149SStephen Hemminger #endif 26*62ae1149SStephen Hemminger 27*62ae1149SStephen Hemminger static enum { 28*62ae1149SStephen Hemminger LOG_TIMESTAMP_NONE = 0, 29*62ae1149SStephen Hemminger LOG_TIMESTAMP_TIME, /* time since start */ 30*62ae1149SStephen Hemminger LOG_TIMESTAMP_DELTA, /* time since last message */ 31*62ae1149SStephen Hemminger LOG_TIMESTAMP_RELTIME, /* relative time since last message */ 32*62ae1149SStephen Hemminger LOG_TIMESTAMP_CTIME, /* Unix standard time format */ 33*62ae1149SStephen Hemminger LOG_TIMESTAMP_ISO, /* ISO8601 time format */ 34*62ae1149SStephen Hemminger } log_time_format; 35*62ae1149SStephen Hemminger 36*62ae1149SStephen Hemminger static struct { 37*62ae1149SStephen Hemminger struct timespec started; /* when log was initialized */ 38*62ae1149SStephen Hemminger RTE_ATOMIC(uint64_t) last_monotonic; 39*62ae1149SStephen Hemminger RTE_ATOMIC(uint64_t) last_realtime; 40*62ae1149SStephen Hemminger } log_time; 41*62ae1149SStephen Hemminger 42*62ae1149SStephen Hemminger /* Set the log timestamp format */ 43*62ae1149SStephen Hemminger int 44*62ae1149SStephen Hemminger eal_log_timestamp(const char *str) 45*62ae1149SStephen Hemminger { 46*62ae1149SStephen Hemminger if (str == NULL) 47*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_TIME; 48*62ae1149SStephen Hemminger else if (strcmp(str, "notime") == 0) 49*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_NONE; 50*62ae1149SStephen Hemminger else if (strcmp(str, "reltime") == 0) 51*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_RELTIME; 52*62ae1149SStephen Hemminger else if (strcmp(str, "delta") == 0) 53*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_DELTA; 54*62ae1149SStephen Hemminger else if (strcmp(str, "ctime") == 0) 55*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_CTIME; 56*62ae1149SStephen Hemminger else if (strcmp(str, "iso") == 0) 57*62ae1149SStephen Hemminger log_time_format = LOG_TIMESTAMP_ISO; 58*62ae1149SStephen Hemminger else 59*62ae1149SStephen Hemminger return -1; 60*62ae1149SStephen Hemminger 61*62ae1149SStephen Hemminger return 0; 62*62ae1149SStephen Hemminger } 63*62ae1149SStephen Hemminger 64*62ae1149SStephen Hemminger bool 65*62ae1149SStephen Hemminger log_timestamp_enabled(void) 66*62ae1149SStephen Hemminger { 67*62ae1149SStephen Hemminger return log_time_format != LOG_TIMESTAMP_NONE; 68*62ae1149SStephen Hemminger } 69*62ae1149SStephen Hemminger 70*62ae1149SStephen Hemminger /* Subtract two timespec values and handle wraparound */ 71*62ae1149SStephen Hemminger static struct timespec 72*62ae1149SStephen Hemminger timespec_sub(const struct timespec *t0, const struct timespec *t1) 73*62ae1149SStephen Hemminger { 74*62ae1149SStephen Hemminger struct timespec ts; 75*62ae1149SStephen Hemminger 76*62ae1149SStephen Hemminger ts.tv_sec = t0->tv_sec - t1->tv_sec; 77*62ae1149SStephen Hemminger ts.tv_nsec = t0->tv_nsec - t1->tv_nsec; 78*62ae1149SStephen Hemminger if (ts.tv_nsec < 0) { 79*62ae1149SStephen Hemminger ts.tv_sec--; 80*62ae1149SStephen Hemminger ts.tv_nsec += 1000000000L; 81*62ae1149SStephen Hemminger } 82*62ae1149SStephen Hemminger return ts; 83*62ae1149SStephen Hemminger } 84*62ae1149SStephen Hemminger 85*62ae1149SStephen Hemminger /* 86*62ae1149SStephen Hemminger * Format current timespec into ISO8601 format. 87*62ae1149SStephen Hemminger * Surprisingly, can't just use strftime() for this; 88*62ae1149SStephen Hemminger * since want microseconds and the timezone offset format differs. 89*62ae1149SStephen Hemminger */ 90*62ae1149SStephen Hemminger static ssize_t 91*62ae1149SStephen Hemminger format_iso8601(char *tsbuf, size_t tsbuflen, const struct timespec *now) 92*62ae1149SStephen Hemminger { 93*62ae1149SStephen Hemminger struct tm *tm, tbuf; 94*62ae1149SStephen Hemminger char dbuf[64]; /* "2024-05-01T22:11:00" */ 95*62ae1149SStephen Hemminger char zbuf[16] = { }; /* "+0800" */ 96*62ae1149SStephen Hemminger 97*62ae1149SStephen Hemminger tm = localtime_r(&now->tv_sec, &tbuf); 98*62ae1149SStephen Hemminger 99*62ae1149SStephen Hemminger /* make "2024-05-01T22:11:00,123456+0100" */ 100*62ae1149SStephen Hemminger if (strftime(dbuf, sizeof(dbuf), "%Y-%m-%dT%H:%M:%S", tm) == 0) 101*62ae1149SStephen Hemminger return 0; 102*62ae1149SStephen Hemminger 103*62ae1149SStephen Hemminger /* convert timezone to +hhmm */ 104*62ae1149SStephen Hemminger if (strftime(zbuf, sizeof(zbuf), "%z", tm) == 0) 105*62ae1149SStephen Hemminger return 0; 106*62ae1149SStephen Hemminger 107*62ae1149SStephen Hemminger /* the result for strftime is "+hhmm" but ISO wants "+hh:mm" */ 108*62ae1149SStephen Hemminger return snprintf(tsbuf, tsbuflen, "%s,%06lu%.3s:%.2s", 109*62ae1149SStephen Hemminger dbuf, now->tv_nsec / 1000u, 110*62ae1149SStephen Hemminger zbuf, zbuf + 3); 111*62ae1149SStephen Hemminger } 112*62ae1149SStephen Hemminger 113*62ae1149SStephen Hemminger /* 114*62ae1149SStephen Hemminger * Format a timestamp which shows time between messages. 115*62ae1149SStephen Hemminger */ 116*62ae1149SStephen Hemminger static ssize_t 117*62ae1149SStephen Hemminger format_delta(char *tsbuf, size_t tsbuflen, const struct timespec *now) 118*62ae1149SStephen Hemminger { 119*62ae1149SStephen Hemminger struct timespec delta; 120*62ae1149SStephen Hemminger uint64_t ns = rte_timespec_to_ns(now); 121*62ae1149SStephen Hemminger uint64_t previous; 122*62ae1149SStephen Hemminger 123*62ae1149SStephen Hemminger previous = rte_atomic_exchange_explicit(&log_time.last_monotonic, 124*62ae1149SStephen Hemminger ns, rte_memory_order_seq_cst); 125*62ae1149SStephen Hemminger delta = rte_ns_to_timespec(ns - previous); 126*62ae1149SStephen Hemminger 127*62ae1149SStephen Hemminger return snprintf(tsbuf, tsbuflen, "<%6lu.%06lu>", 128*62ae1149SStephen Hemminger (unsigned long)delta.tv_sec, 129*62ae1149SStephen Hemminger (unsigned long)delta.tv_nsec / 1000u); 130*62ae1149SStephen Hemminger } 131*62ae1149SStephen Hemminger 132*62ae1149SStephen Hemminger /* 133*62ae1149SStephen Hemminger * Make a timestamp where if the minute, hour or day has 134*62ae1149SStephen Hemminger * changed from the last message, then print abbreviated 135*62ae1149SStephen Hemminger * "Month day hour:minute" format. 136*62ae1149SStephen Hemminger * Otherwise print delta from last printed message as +sec.usec 137*62ae1149SStephen Hemminger */ 138*62ae1149SStephen Hemminger static ssize_t 139*62ae1149SStephen Hemminger format_reltime(char *tsbuf, size_t tsbuflen, const struct timespec *now) 140*62ae1149SStephen Hemminger { 141*62ae1149SStephen Hemminger struct tm *tm, *last_tm, tbuf1, tbuf2; 142*62ae1149SStephen Hemminger time_t last_sec; 143*62ae1149SStephen Hemminger uint64_t ns = rte_timespec_to_ns(now); 144*62ae1149SStephen Hemminger uint64_t previous; 145*62ae1149SStephen Hemminger 146*62ae1149SStephen Hemminger tm = localtime_r(&now->tv_sec, &tbuf1); 147*62ae1149SStephen Hemminger 148*62ae1149SStephen Hemminger previous = rte_atomic_exchange_explicit(&log_time.last_realtime, 149*62ae1149SStephen Hemminger ns, rte_memory_order_seq_cst); 150*62ae1149SStephen Hemminger last_sec = previous / NS_PER_SEC; 151*62ae1149SStephen Hemminger last_tm = localtime_r(&last_sec, &tbuf2); 152*62ae1149SStephen Hemminger if (tm->tm_min == last_tm->tm_min && 153*62ae1149SStephen Hemminger tm->tm_hour == last_tm->tm_hour && 154*62ae1149SStephen Hemminger tm->tm_yday == last_tm->tm_yday) { 155*62ae1149SStephen Hemminger struct timespec elapsed; 156*62ae1149SStephen Hemminger 157*62ae1149SStephen Hemminger elapsed = rte_ns_to_timespec(ns - previous); 158*62ae1149SStephen Hemminger 159*62ae1149SStephen Hemminger return snprintf(tsbuf, tsbuflen, "+%3lu.%06lu", 160*62ae1149SStephen Hemminger (unsigned long)elapsed.tv_sec, 161*62ae1149SStephen Hemminger (unsigned long)elapsed.tv_nsec / 1000u); 162*62ae1149SStephen Hemminger } else { 163*62ae1149SStephen Hemminger return strftime(tsbuf, tsbuflen, "%b%d %H:%M", tm); 164*62ae1149SStephen Hemminger } 165*62ae1149SStephen Hemminger } 166*62ae1149SStephen Hemminger 167*62ae1149SStephen Hemminger /* Format up a timestamp based on current format */ 168*62ae1149SStephen Hemminger ssize_t 169*62ae1149SStephen Hemminger log_timestamp(char *tsbuf, size_t tsbuflen) 170*62ae1149SStephen Hemminger { 171*62ae1149SStephen Hemminger struct timespec now, delta; 172*62ae1149SStephen Hemminger 173*62ae1149SStephen Hemminger switch (log_time_format) { 174*62ae1149SStephen Hemminger case LOG_TIMESTAMP_NONE: 175*62ae1149SStephen Hemminger return 0; 176*62ae1149SStephen Hemminger 177*62ae1149SStephen Hemminger case LOG_TIMESTAMP_TIME: 178*62ae1149SStephen Hemminger if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) 179*62ae1149SStephen Hemminger return 0; 180*62ae1149SStephen Hemminger 181*62ae1149SStephen Hemminger delta = timespec_sub(&now, &log_time.started); 182*62ae1149SStephen Hemminger 183*62ae1149SStephen Hemminger return snprintf(tsbuf, tsbuflen, "%6lu.%06lu", 184*62ae1149SStephen Hemminger (unsigned long)delta.tv_sec, 185*62ae1149SStephen Hemminger (unsigned long)delta.tv_nsec / 1000u); 186*62ae1149SStephen Hemminger 187*62ae1149SStephen Hemminger case LOG_TIMESTAMP_DELTA: 188*62ae1149SStephen Hemminger if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) 189*62ae1149SStephen Hemminger return 0; 190*62ae1149SStephen Hemminger 191*62ae1149SStephen Hemminger return format_delta(tsbuf, tsbuflen, &now); 192*62ae1149SStephen Hemminger 193*62ae1149SStephen Hemminger case LOG_TIMESTAMP_RELTIME: 194*62ae1149SStephen Hemminger if (clock_gettime(CLOCK_REALTIME, &now) < 0) 195*62ae1149SStephen Hemminger return 0; 196*62ae1149SStephen Hemminger 197*62ae1149SStephen Hemminger return format_reltime(tsbuf, tsbuflen, &now); 198*62ae1149SStephen Hemminger 199*62ae1149SStephen Hemminger case LOG_TIMESTAMP_CTIME: 200*62ae1149SStephen Hemminger if (clock_gettime(CLOCK_REALTIME, &now) < 0) 201*62ae1149SStephen Hemminger return 0; 202*62ae1149SStephen Hemminger 203*62ae1149SStephen Hemminger /* trncate to remove newline from ctime result */ 204*62ae1149SStephen Hemminger return snprintf(tsbuf, tsbuflen, "%.24s", ctime(&now.tv_sec)); 205*62ae1149SStephen Hemminger 206*62ae1149SStephen Hemminger case LOG_TIMESTAMP_ISO: 207*62ae1149SStephen Hemminger if (clock_gettime(CLOCK_REALTIME, &now) < 0) 208*62ae1149SStephen Hemminger return 0; 209*62ae1149SStephen Hemminger 210*62ae1149SStephen Hemminger return format_iso8601(tsbuf, tsbuflen, &now); 211*62ae1149SStephen Hemminger } 212*62ae1149SStephen Hemminger 213*62ae1149SStephen Hemminger return 0; 214*62ae1149SStephen Hemminger } 215*62ae1149SStephen Hemminger 216*62ae1149SStephen Hemminger /* print timestamp before message */ 217*62ae1149SStephen Hemminger int 218*62ae1149SStephen Hemminger log_print_with_timestamp(FILE *f, const char *format, va_list ap) 219*62ae1149SStephen Hemminger { 220*62ae1149SStephen Hemminger char tsbuf[128]; 221*62ae1149SStephen Hemminger char msgbuf[LINE_MAX]; 222*62ae1149SStephen Hemminger 223*62ae1149SStephen Hemminger if (log_timestamp(tsbuf, sizeof(tsbuf)) > 0) { 224*62ae1149SStephen Hemminger vsnprintf(msgbuf, sizeof(msgbuf), format, ap); 225*62ae1149SStephen Hemminger return fprintf(f, "[%s] %s", tsbuf, msgbuf); 226*62ae1149SStephen Hemminger } 227*62ae1149SStephen Hemminger 228*62ae1149SStephen Hemminger /* fall back when timestamp is unavailable */ 229*62ae1149SStephen Hemminger return vfprintf(f, format, ap); 230*62ae1149SStephen Hemminger } 231*62ae1149SStephen Hemminger 232*62ae1149SStephen Hemminger RTE_INIT_PRIO(log_timestamp_init, LOG) 233*62ae1149SStephen Hemminger { 234*62ae1149SStephen Hemminger struct timespec now; 235*62ae1149SStephen Hemminger 236*62ae1149SStephen Hemminger clock_gettime(CLOCK_MONOTONIC, &now); 237*62ae1149SStephen Hemminger log_time.started = now; 238*62ae1149SStephen Hemminger rte_atomic_store_explicit(&log_time.last_monotonic, rte_timespec_to_ns(&now), 239*62ae1149SStephen Hemminger rte_memory_order_seq_cst); 240*62ae1149SStephen Hemminger } 241