[PATCH v3 3/3] syslogd: Add a configurable option to log timestamps in ISO 8601 format

Grant Erickson gerickson at nuovations.com
Fri Apr 4 16:58:11 UTC 2025


This adds a configurable option 'FEATURE_SYSLOGD_ISO8601FMT' enabling
a command line option ('-I', with semantics similar to 'date') that
directs syslogd to log timestamps in ISO 8601 format.

Precision in seconds (the default), milliseconds, and microseconds are
supported. Without the 'FEATURE_SYSLOGD_UTC' configuration and its
'-u' option, outputs are of the form:

  YYYY-MM-DDThh:mm:ss±hhmm (seconds)
  YYYY-MM-DDThh:mm:ss.sss±hhmm (milliseconds)
  YYYY-MM-DDThh:mm:ss.uuuuuu±hhmm (microseconds)

with the 'FEATURE_SYSLOGD_UTC' configuration and its '-u' option,
outputs are of the form:

  YYYY-MM-DDThh:mm:ssZ (seconds)
  YYYY-MM-DDThh:mm:ss.sssZ (milliseconds)
  YYYY-MM-DDThh:mm:ss.uuuuuuZ (microseconds)

Signed-off-by: Grant Erickson <gerickson at nuovations.com>
---
 sysklogd/syslogd.c | 157 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 118 insertions(+), 39 deletions(-)

diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 0894693c4580..e9404eaf003f 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -44,6 +44,15 @@
 //config:	syslogd to log timestamps in Coordinated Universal Time
 //config:	(UTC) rather than in local time.
 //config:
+//config:config FEATURE_SYSLOGD_ISO8601FMT
+//config:	bool "Log timestamps in ISO 8601 format"
+//config:	default n
+//config:	depends on SYSLOGD
+//config:	help
+//config:	This enables a command line option ('-I') that directs
+//config:	syslogd to log timestamps in ISO 8601 format with second,
+//config:	millisecond, or microsecond resolution.
+//config:
 //config:config FEATURE_REMOTE_LOG
 //config:	bool "Remote Log support"
 //config:	default y
@@ -159,6 +168,10 @@
 //usage:	IF_FEATURE_SYSLOGD_UTC(
 //usage:     "\n	-u		Log timestamps in Coordinated Universal Time (UTC)"
 //usage:	)
+//usage:	IF_FEATURE_SYSLOGD_ISO8601FMT(
+//usage:     "\n	-I[SPEC]	Log timestamps in ISO 8601 format"
+//usage:     "\n			SPEC=seconds (default), ms, or us"
+//usage:	)
 //usage:     "\n	-t		Strip client-generated timestamps"
 //usage:	IF_FEATURE_SYSLOGD_DUP(
 //usage:     "\n	-D		Drop duplicates"
@@ -272,6 +285,9 @@ IF_FEATURE_SYSLOGD_CFG( \
 IF_FEATURE_KMSG_SYSLOG( \
 	int kmsgfd; \
 	int primask; \
+) \
+IF_FEATURE_SYSLOGD_ISO8601FMT( \
+	int iso8601fmt; \
 )
 
 struct init_globals {
@@ -298,7 +314,7 @@ struct globals {
 	/* ...then sprintf into printbuf, adding timestamp (15 or 19 chars),
 	 * host (64), fac.prio (20) to the message */
 	/* (growth by: 15 + 64 + 20 + delims = ~110) */
-	char printbuf[MAX_READ*2 + 128];
+	char printbuf[MAX_READ*2 + 144];
 };
 
 static const struct init_globals init_data = {
@@ -337,15 +353,16 @@ enum {
 	OPTBIT_loglevel, // -l
 	OPTBIT_small, // -S
 	OPTBIT_timestamp, // -t
-	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize   ,)	// -s
-	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt  ,)	// -b
-	IF_FEATURE_REMOTE_LOG(    OPTBIT_remotelog  ,)	// -R
-	IF_FEATURE_REMOTE_LOG(    OPTBIT_locallog   ,)	// -L
-	IF_FEATURE_IPC_SYSLOG(    OPTBIT_circularlog,)	// -C
-	IF_FEATURE_SYSLOGD_DUP(   OPTBIT_dup        ,)	// -D
-	IF_FEATURE_SYSLOGD_CFG(   OPTBIT_cfg        ,)	// -f
-	IF_FEATURE_KMSG_SYSLOG(   OPTBIT_kmsg       ,)	// -K
-	IF_FEATURE_SYSLOGD_UTC(   OPTBIT_utc        ,)	// -u
+	IF_FEATURE_ROTATE_LOGFILE(    OPTBIT_filesize   ,)	// -s
+	IF_FEATURE_ROTATE_LOGFILE(    OPTBIT_rotatecnt  ,)	// -b
+	IF_FEATURE_REMOTE_LOG(        OPTBIT_remotelog  ,)	// -R
+	IF_FEATURE_REMOTE_LOG(        OPTBIT_locallog   ,)	// -L
+	IF_FEATURE_IPC_SYSLOG(        OPTBIT_circularlog,)	// -C
+	IF_FEATURE_SYSLOGD_DUP(       OPTBIT_dup        ,)	// -D
+	IF_FEATURE_SYSLOGD_CFG(       OPTBIT_cfg        ,)	// -f
+	IF_FEATURE_KMSG_SYSLOG(       OPTBIT_kmsg       ,)	// -K
+	IF_FEATURE_SYSLOGD_UTC(       OPTBIT_utc        ,)	// -u
+	IF_FEATURE_SYSLOGD_ISO8601FMT(OPTBIT_iso8601fmt ,)	// -I
 
 	OPT_mark        = 1 << OPTBIT_mark    ,
 	OPT_nofork      = 1 << OPTBIT_nofork  ,
@@ -353,38 +370,41 @@ enum {
 	OPT_loglevel    = 1 << OPTBIT_loglevel,
 	OPT_small       = 1 << OPTBIT_small   ,
 	OPT_timestamp   = 1 << OPTBIT_timestamp,
-	OPT_filesize    = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize   )) + 0,
-	OPT_rotatecnt   = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt  )) + 0,
-	OPT_remotelog   = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remotelog  )) + 0,
-	OPT_locallog    = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_locallog   )) + 0,
-	OPT_circularlog = IF_FEATURE_IPC_SYSLOG(    (1 << OPTBIT_circularlog)) + 0,
-	OPT_dup         = IF_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
-	OPT_cfg         = IF_FEATURE_SYSLOGD_CFG(   (1 << OPTBIT_cfg        )) + 0,
-	OPT_kmsg        = IF_FEATURE_KMSG_SYSLOG(   (1 << OPTBIT_kmsg       )) + 0,
-	OPT_utc         = IF_FEATURE_SYSLOGD_UTC(   (1 << OPTBIT_utc        )) + 0,
+	OPT_filesize    = IF_FEATURE_ROTATE_LOGFILE(    (1 << OPTBIT_filesize   )) + 0,
+	OPT_rotatecnt   = IF_FEATURE_ROTATE_LOGFILE(    (1 << OPTBIT_rotatecnt  )) + 0,
+	OPT_remotelog   = IF_FEATURE_REMOTE_LOG(        (1 << OPTBIT_remotelog  )) + 0,
+	OPT_locallog    = IF_FEATURE_REMOTE_LOG(        (1 << OPTBIT_locallog   )) + 0,
+	OPT_circularlog = IF_FEATURE_IPC_SYSLOG(        (1 << OPTBIT_circularlog)) + 0,
+	OPT_dup         = IF_FEATURE_SYSLOGD_DUP(       (1 << OPTBIT_dup        )) + 0,
+	OPT_cfg         = IF_FEATURE_SYSLOGD_CFG(       (1 << OPTBIT_cfg        )) + 0,
+	OPT_kmsg        = IF_FEATURE_KMSG_SYSLOG(       (1 << OPTBIT_kmsg       )) + 0,
+	OPT_utc         = IF_FEATURE_SYSLOGD_UTC(       (1 << OPTBIT_utc        )) + 0,
+	OPT_iso8601fmt  = IF_FEATURE_SYSLOGD_ISO8601FMT((1 << OPTBIT_iso8601fmt )) + 0,
 };
 #define OPTION_STR "m:nO:l:St" \
-	IF_FEATURE_ROTATE_LOGFILE("s:" ) \
-	IF_FEATURE_ROTATE_LOGFILE("b:" ) \
-	IF_FEATURE_REMOTE_LOG(    "R:*") \
-	IF_FEATURE_REMOTE_LOG(    "L"  ) \
-	IF_FEATURE_IPC_SYSLOG(    "C::") \
-	IF_FEATURE_SYSLOGD_DUP(   "D"  ) \
-	IF_FEATURE_SYSLOGD_CFG(   "f:" ) \
-	IF_FEATURE_KMSG_SYSLOG(   "K"  ) \
-	IF_FEATURE_SYSLOGD_UTC(   "u"  )
+	IF_FEATURE_ROTATE_LOGFILE(    "s:" ) \
+	IF_FEATURE_ROTATE_LOGFILE(    "b:" ) \
+	IF_FEATURE_REMOTE_LOG(        "R:*") \
+	IF_FEATURE_REMOTE_LOG(        "L"  ) \
+	IF_FEATURE_IPC_SYSLOG(        "C::") \
+	IF_FEATURE_SYSLOGD_DUP(       "D"  ) \
+	IF_FEATURE_SYSLOGD_CFG(       "f:" ) \
+	IF_FEATURE_KMSG_SYSLOG(       "K"  ) \
+	IF_FEATURE_SYSLOGD_UTC(       "u"  ) \
+	IF_FEATURE_SYSLOGD_ISO8601FMT("I::")
 #define OPTION_DECL *opt_m, *opt_l \
-	IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \
-	IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \
-	IF_FEATURE_IPC_SYSLOG(    ,*opt_C = NULL) \
-	IF_FEATURE_SYSLOGD_CFG(   ,*opt_f = NULL)
+	IF_FEATURE_ROTATE_LOGFILE(    ,*opt_s) \
+	IF_FEATURE_ROTATE_LOGFILE(    ,*opt_b) \
+	IF_FEATURE_IPC_SYSLOG(        ,*opt_C = NULL) \
+	IF_FEATURE_SYSLOGD_CFG(       ,*opt_f = NULL) \
+	IF_FEATURE_SYSLOGD_ISO8601FMT(,*opt_I = NULL)
 #define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \
-	IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \
-	IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \
-	IF_FEATURE_REMOTE_LOG(    ,&remoteAddrList) \
-	IF_FEATURE_IPC_SYSLOG(    ,&opt_C) \
-	IF_FEATURE_SYSLOGD_CFG(   ,&opt_f)
-
+	IF_FEATURE_ROTATE_LOGFILE(    ,&opt_s) \
+	IF_FEATURE_ROTATE_LOGFILE(    ,&opt_b) \
+	IF_FEATURE_REMOTE_LOG(        ,&remoteAddrList) \
+	IF_FEATURE_IPC_SYSLOG(        ,&opt_C) \
+	IF_FEATURE_SYSLOGD_CFG(       ,&opt_f) \
+	IF_FEATURE_SYSLOGD_ISO8601FMT(,&opt_I)
 
 #if ENABLE_FEATURE_SYSLOGD_CFG
 static const CODE* find_by_name(char *name, const CODE* c_set)
@@ -873,6 +893,21 @@ static struct tm * generate_time(struct timeval *tvp, struct tm *tmp)
 	return tm;
 }
 
+#if ENABLE_FEATURE_SYSLOGD_ISO8601FMT
+static void append_zone_string(char *timestamp, size_t max, const struct tm *tmp)
+{
+	if (timestamp && tmp) {
+#if ENABLE_FEATURE_SYSLOGD_UTC
+		if (option_mask32 & OPT_utc)
+	        snprintf(timestamp, max, "%c", 'Z');
+	    else
+	        strftime(timestamp, max, "%z", tmp);
+#else
+		strftime(timestamp, max, "%z", tmp);
+#endif
+	}
+}
+#endif
 
 /* len parameter is used only for "is there a timestamp?" check.
  * NB: some callers cheat and supply len==0 when they know
@@ -887,11 +922,15 @@ static void timestamp_and_log(int pri, char *msg, int len)
 		msg[15] == ' ');
 	const char *format="%h %e %T";
 	char *timestamp = NULL;
-	char timestampbuf[20];
+	char timestampbuf[32];
 	/* MMM DD hh:mm:ss default */
 	/* 0123456789012345                 - 16 */
 	/* MMM DD hh:mm:ss.mmm ENABLE_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS */
 	/* 01234567890123456789             - 20 */
+	/* YYYY-MM-DDThh:mm:ss.uuuuuuZ OPT_utc & OPT_iso8601fmt */
+	/* 0123456789012345678901234567     - 28 */
+	/* YYYY-MM-DDThh:mm:ss.uuuuuu+hhmm OPT_iso8601fmt */
+	/* 01234567890123456789012345678901 - 32 */
 	struct timeval tvnow;
 #if ENABLE_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS || ENABLE_FEATURE_SYSLOGD_UTC
 	struct tm parsed;
@@ -941,6 +980,33 @@ static void timestamp_and_log(int pri, char *msg, int len)
 		tmp = generate_time(&tvnow, &tmnow);
 	}
 
+#if ENABLE_FEATURE_SYSLOGD_ISO8601FMT
+	if (tmp && !timestamp && G.iso8601fmt >= 0) {
+	    format = "%FT%T";
+		/* -I[SPEC]: 0:seconds 1:ms 2:us */
+		n = strftime(timestampbuf + n, sizeof(timestampbuf) - n, format, tmp);
+
+	    if (G.iso8601fmt == 1 || G.iso8601fmt == 2) {
+	        const char *iso8601fracfmt;
+	        unsigned long divisor;
+
+	        if (G.iso8601fmt == 1) {
+	            iso8601fracfmt = ".%03lu";
+	            divisor = 1000u;
+	        } else {
+	            iso8601fracfmt = ".%06lu";
+	            divisor = 1u;
+	        }
+
+			n += snprintf(timestampbuf + n, sizeof(timestampbuf) - n, iso8601fracfmt, tvnow.tv_usec / divisor);
+		}
+
+		append_zone_string(timestampbuf + n, sizeof(timestampbuf) - n, tmp);
+
+		timestamp = timestampbuf;
+	}
+#endif /* ENABLE_FEATURE_SYSLOGD_ISO8601FMT */
+
 #if ENABLE_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS
 	if (tmp && !timestamp) {
 		n = strftime(timestampbuf + n, sizeof(timestampbuf) - n, format, tmp);
@@ -1138,6 +1204,19 @@ static int NOINLINE syslogd_init(char **argv)
 #if ENABLE_FEATURE_IPC_SYSLOG
 	if (opt_C) // -Cn
 		G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
+#endif
+#if ENABLE_FEATURE_SYSLOGD_ISO8601FMT
+	G.iso8601fmt = -1;
+	if (opts & OPT_iso8601fmt) {
+		G.iso8601fmt = 0; /* default is seconds */
+		if (opt_I) {
+			static const char iso8601fmts[] ALIGN1 =
+				"seconds\0""ms\0""us\0";
+			G.iso8601fmt = index_in_substrings(iso8601fmts, opt_I);
+			if (G.iso8601fmt < 0)
+				bb_show_usage();
+		}
+	}
 #endif
 	/* If they have not specified remote logging, then log locally */
 	if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
-- 
2.45.0



More information about the busybox mailing list