xref: /dpdk/lib/log/log_timestamp.c (revision 62ae1149f2bdaed3482abb08f2e255f1ac4746e7)
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