On Mon, Sep 09, 2013 at 06:05:42PM -0700, Paul Ingram wrote:
> To make debugging easier.

It's reasonable enough.

I didn't really like the %. format specification for this.  It is
inflexible--what if someone wants only tenths of a second or wants
microseconds?  It is also somewhat hard to parse since it uses %
(naturally enough though).  Anyway I thought about it for a minute and
concluded that one can reasonably just replace '#' in the strftime()
formatted output with fractional seconds, so that "%S.###" outputs
milliseconds, "%S.#" outputs tenths, etc.  Also this allows for
formatting properly for cultures that prefer to use a comma for the
decimal point.

I made this change.

Some indentation was weird (I think you indented with tabs instead of
spaces and we must have different tab stops).  I fixed it.

NEWS and manpages needed an update.  I did it.

I need a sign-off.

Here's what I folded in, followed by a revised patch.  Comments?

diff --git a/NEWS b/NEWS
index 09c98eb..4dd9568 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 Post-v2.0.0
 ---------------------
+    - Log files now report times with millisecond resolution.  (Previous
+      versions only reported whole seconds.)
 
 
 v2.0.0 - xx xxx xxxx
diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c
index 66ffc8f..5137d9f 100644
--- a/lib/dynamic-string.c
+++ b/lib/dynamic-string.c
@@ -188,7 +188,7 @@ ds_put_printable(struct ds *ds, const char *s, size_t n)
  * The current time is either localtime or UTC based on 'utc'. */
 void
 ds_put_strftime_msec(struct ds *ds, const char *template, long long int when,
-                    bool utc)
+                     bool utc)
 {
     struct tm_msec tm;
     if (utc) {
@@ -200,7 +200,7 @@ ds_put_strftime_msec(struct ds *ds, const char *template, 
long long int when,
     for (;;) {
         size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0;
         size_t used = strftime_msec(&ds->string[ds->length], avail, template,
-                                   &tm);
+                                    &tm);
         if (used) {
             ds->length += used;
             return;
diff --git a/lib/table.c b/lib/table.c
index c49487c..4628170 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -221,7 +221,7 @@ table_print_table_line__(struct ds *line)
 static char *
 table_format_timestamp__(void)
 {
-    return xastrftime_msec("%Y-%m-%d %H:%M:%S.%.", time_wall_msec(), true);
+    return xastrftime_msec("%Y-%m-%d %H:%M:%S.###", time_wall_msec(), true);
 }
 
 static void
diff --git a/lib/timeval.c b/lib/timeval.c
index 6af0bac..1e5eeb2 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -70,9 +70,6 @@ static struct rusage *get_recent_rusage(void);
 static void refresh_rusage(void);
 static void timespec_add(struct timespec *sum,
                          const struct timespec *a, const struct timespec *b);
-static void format_msec(char *buf, size_t buf_len, const char *fmt, int msec);
-static char *buf_copy(char *dst, const char *dst_end,
-                     const char *src, const char *src_end);
 
 static void
 init_clock(struct clock *c, clockid_t id)
@@ -499,77 +496,44 @@ timeval_dummy_register(void)
 }
 
 
-/* Millisecond resolution timestamps. */
+/* strftime() with an extension for high-resolution timestamps.  Any '#'s in
+ * 'format' will be replaced by subseconds, e.g. use "%S.###" to obtain results
+ * like "01.123".  */
 size_t
 strftime_msec(char *s, size_t max, const char *format,
               const struct tm_msec *tm)
 {
-    char buf[256];
+    size_t n;
 
-    format_msec(buf, sizeof buf, format, tm->msec);
+    n = strftime(s, max, format, &tm->tm);
+    if (n) {
+        char decimals[4];
+        char *d, *p;
 
-    return strftime(s, max, buf, &tm->tm);
+        sprintf(decimals, "%03d", tm->msec);
+        d = decimals;
+        for (p = strchr(s, '#'); p; p = strchr(p, '#')) {
+            *p++ = *d ? *d++ : '0';
+        }
+    }
+
+    return n;
 }
 
-struct tm_msec*
-localtime_msec(long long int now, struct tm_msec *result) {
+struct tm_msec *
+localtime_msec(long long int now, struct tm_msec *result)
+{
   time_t now_sec = now / 1000;
-  localtime_r(now_sec, &result->tm);
+  localtime_r(&now_sec, &result->tm);
   result->msec = now % 1000;
   return result;
 }
 
-struct tm_msec*
-gmtime_msec(long long int now, struct tm_msec *result) {
+struct tm_msec *
+gmtime_msec(long long int now, struct tm_msec *result)
+{
   time_t now_sec = now / 1000;
   gmtime_r(&now_sec, &result->tm);
   result->msec = now % 1000;
   return result;
 }
-
-void
-format_msec(char *buf, size_t buf_len, const char *fmt, int msec)
-{
-    char *buf_ptr = buf;
-    const char *buf_end = buf + buf_len;
-    const char *fmt_ptr = fmt;
-    const char *add_ptr = fmt_ptr;
-
-    bool percent = false;
-
-    char msec_buf[4];
-    const char *msec_end = msec_buf;
-    ovs_assert(sprintf(msec_buf, "%03i", MIN(msec, 999)) == 3);
-    msec_end += strlen(msec_buf);
-
-    while (*fmt_ptr) {
-        if (percent) {
-           if (*fmt_ptr == '.') {
-             buf_ptr = buf_copy(buf_ptr, buf_end, add_ptr, fmt_ptr-1);
-             buf_ptr = buf_copy(buf_ptr, buf_end, msec_buf, msec_end);
-
-             add_ptr = fmt_ptr+1;
-           }
-           percent = false;
-       }
-       else if (*fmt_ptr == '%') {
-         percent = true;
-       }
-       fmt_ptr++;
-    }
-
-    buf_ptr = buf_copy(buf_ptr, buf_end, add_ptr, fmt_ptr);
-
-    ovs_assert(buf_ptr <= buf_end);
-
-    *(buf_ptr < buf_end ? buf_ptr : buf_ptr-1) = '\0';
-}
-
-static char *
-buf_copy(char *dst, const char *dst_end, const char *src, const char *src_end)
-{
-  int len = MIN(dst_end-dst, src_end-src);
-  memcpy(dst, src, len);
-  dst += len;
-  return dst;
-}
diff --git a/lib/timeval.h b/lib/timeval.h
index dfe5a98..99b3af0 100644
--- a/lib/timeval.h
+++ b/lib/timeval.h
@@ -58,10 +58,10 @@ int time_poll(struct pollfd *, int n_pollfds, long long int 
timeout_when,
 long long int timespec_to_msec(const struct timespec *);
 long long int timeval_to_msec(const struct timeval *);
 
-struct tm_msec* localtime_msec(long long int now, struct tm_msec *result);
-struct tm_msec* gmtime_msec(long long int now, struct tm_msec *result);
+struct tm_msec *localtime_msec(long long int now, struct tm_msec *result);
+struct tm_msec *gmtime_msec(long long int now, struct tm_msec *result);
 size_t strftime_msec(char *s, size_t max, const char *format,
-                    const struct tm_msec *tm);
+                     const struct tm_msec *);
 void xgettimeofday(struct timeval *);
 void xclock_gettime(clock_t, struct timespec *);
 
diff --git a/lib/vlog.c b/lib/vlog.c
index 2b97da1..37806b8d 100644
--- a/lib/vlog.c
+++ b/lib/vlog.c
@@ -746,11 +746,11 @@ format_log_message(const struct vlog_module *module, enum 
vlog_level level,
             ds_put_cstr(s, vlog_get_module_name(module));
             break;
         case 'd':
-            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.%.", tmp, sizeof tmp);
+            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
             ds_put_strftime_msec(s, tmp, time_wall_msec(), false);
             break;
         case 'D':
-            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.%.", tmp, sizeof tmp);
+            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
             ds_put_strftime_msec(s, tmp, time_wall_msec(), true);
             break;
         case 'm':
diff --git a/lib/vlog.h b/lib/vlog.h
index 9699379..d7d63bf 100644
--- a/lib/vlog.h
+++ b/lib/vlog.h
@@ -64,7 +64,7 @@ enum vlog_level vlog_get_level_val(const char *name);
 #define VLOG_FACILITIES                                                 \
     VLOG_FACILITY(SYSLOG, "ovs|%05N|%c%T|%p|%m")                            \
     VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")    \
-    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%S.%.Z}|%05N|%c%T|%p|%m")
+    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m")
 enum vlog_facility {
 #define VLOG_FACILITY(NAME, PATTERN) VLF_##NAME,
     VLOG_FACILITIES
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
index 7cd0485..8670127 100644
--- a/ovsdb/ovsdb-tool.c
+++ b/ovsdb/ovsdb-tool.c
@@ -519,7 +519,8 @@ do_show_log(int argc, char *argv[])
             date = shash_find_data(json_object(json), "_date");
             if (date && date->type == JSON_INTEGER) {
                 time_t t = json_integer(date);
-                char *s = xastrftime(" %Y-%m-%d %H:%M:%S", t, true);
+                char *s = xastrftime_msec(" %Y-%m-%d %H:%M:%S",
+                                          t * 1000LL, true);
                 fputs(s, stdout);
                 free(s);
             }
diff --git a/utilities/ovs-appctl.8.in b/utilities/ovs-appctl.8.in
index df56309..1cf888d 100644
--- a/utilities/ovs-appctl.8.in
+++ b/utilities/ovs-appctl.8.in
@@ -158,13 +158,20 @@ The current date and time in ISO 8601 format 
(YYYY\-MM\-DD HH:MM:SS).
 .IP \fB%d{\fIformat\fB}\fR
 The current date and time in the specified \fIformat\fR, which takes
 the same format as the \fItemplate\fR argument to \fBstrftime\fR(3).
+As an extension, any \fB#\fR characters in \fIformat\fR will be
+replaced by fractional seconds, e.g. use \fB%H:%M:%S.###\fR for the
+time to the nearest millisecond.  Sub-second times are only
+approximate and currently decimal places after the third will always
+be reported as zero.
 .
 .IP \fB%D\fR
 The current UTC date and time in ISO 8601 format (YYYY\-MM\-DD HH:MM:SS).
 .
 .IP \fB%D{\fIformat\fB}\fR
-The current UTC date and time in the specified \fIformat\fR, which takes
-the same format as the \fItemplate\fR argument to \fBstrftime\fR(3).
+The current UTC date and time in the specified \fIformat\fR, which
+takes the same format as the \fItemplate\fR argument to
+\fBstrftime\fR(3).  Supports the same extension for sub-second
+resolution as \fB%d{\fR...\fB}\fR.
 .
 .IP \fB%m\fR
 The message being logged.
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 86efb04..a5a5c02 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1396,7 +1396,7 @@ monitor_vconn(struct vconn *vconn, bool 
reply_to_echo_requests)
             run(retval, "vconn_recv");
 
             if (timestamp) {
-                char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.%.: ",
+                char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ",
                                           time_wall_msec(), true);
                 fputs(s, stderr);
                 free(s);

--8<--------------------------cut here-------------------------->8--

From: Paul Ingram <ping...@nicira.com>
Date: Mon, 9 Sep 2013 18:05:42 -0700
Subject: [PATCH] Report timestamps in millisecond resolution in log messages.

To make debugging easier.

Signed-off-by: Ben Pfaff <b...@nicira.com>
---
 NEWS                      |    2 ++
 lib/dynamic-string.c      |   21 ++++++++++++---------
 lib/dynamic-string.h      |    7 +++----
 lib/table.c               |    2 +-
 lib/timeval.c             |   43 +++++++++++++++++++++++++++++++++++++++++++
 lib/timeval.h             |    9 +++++++++
 lib/vlog.c                |   16 ++++++++--------
 lib/vlog.h                |    2 +-
 ovsdb/ovsdb-tool.c        |    3 ++-
 python/ovs/vlog.py        |    3 ++-
 tests/vlog.at             |    2 +-
 utilities/ovs-appctl.8.in |   11 +++++++++--
 utilities/ovs-ofctl.c     |    3 ++-
 13 files changed, 95 insertions(+), 29 deletions(-)

diff --git a/NEWS b/NEWS
index 09c98eb..4dd9568 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 Post-v2.0.0
 ---------------------
+    - Log files now report times with millisecond resolution.  (Previous
+      versions only reported whole seconds.)
 
 
 v2.0.0 - xx xxx xxxx
diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c
index 9b3e7ba..5137d9f 100644
--- a/lib/dynamic-string.c
+++ b/lib/dynamic-string.c
@@ -183,21 +183,24 @@ ds_put_printable(struct ds *ds, const char *s, size_t n)
     }
 }
 
-/* Writes time 'when' to 'string' based on 'template', in local time or UTC
- * based on 'utc'. */
+/* Writes the current time with optional millisecond resolution to 'string'
+ * based on 'template'.
+ * The current time is either localtime or UTC based on 'utc'. */
 void
-ds_put_strftime(struct ds *ds, const char *template, time_t when, bool utc)
+ds_put_strftime_msec(struct ds *ds, const char *template, long long int when,
+                     bool utc)
 {
-    struct tm tm;
+    struct tm_msec tm;
     if (utc) {
-        gmtime_r(&when, &tm);
+        gmtime_msec(when, &tm);
     } else {
-        localtime_r(&when, &tm);
+        localtime_msec(when, &tm);
     }
 
     for (;;) {
         size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0;
-        size_t used = strftime(&ds->string[ds->length], avail, template, &tm);
+        size_t used = strftime_msec(&ds->string[ds->length], avail, template,
+                                    &tm);
         if (used) {
             ds->length += used;
             return;
@@ -209,12 +212,12 @@ ds_put_strftime(struct ds *ds, const char *template, 
time_t when, bool utc)
 /* Returns a malloc()'d string for time 'when' based on 'template', in local
  * time or UTC based on 'utc'. */
 char *
-xastrftime(const char *template, time_t when, bool utc)
+xastrftime_msec(const char *template, long long int when, bool utc)
 {
     struct ds s;
 
     ds_init(&s);
-    ds_put_strftime(&s, template, when, utc);
+    ds_put_strftime_msec(&s, template, when, utc);
     return s.string;
 }
 
diff --git a/lib/dynamic-string.h b/lib/dynamic-string.h
index c069586..2272343 100644
--- a/lib/dynamic-string.h
+++ b/lib/dynamic-string.h
@@ -61,10 +61,9 @@ int ds_get_line(struct ds *, FILE *);
 int ds_get_preprocessed_line(struct ds *, FILE *, int *line_number);
 int ds_get_test_line(struct ds *, FILE *);
 
-void ds_put_strftime(struct ds *, const char *template, time_t when, bool utc)
-    STRFTIME_FORMAT(2);
-char *xastrftime(const char *template, time_t when, bool utc)
-    STRFTIME_FORMAT(1);
+void ds_put_strftime_msec(struct ds *, const char *template, long long int 
when,
+                         bool utc);
+char *xastrftime_msec(const char *template, long long int when, bool utc);
 
 char *ds_cstr(struct ds *);
 const char *ds_cstr_ro(const struct ds *);
diff --git a/lib/table.c b/lib/table.c
index 21f4905..4628170 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -221,7 +221,7 @@ table_print_table_line__(struct ds *line)
 static char *
 table_format_timestamp__(void)
 {
-    return xastrftime("%Y-%m-%d %H:%M:%S", time_wall(), true);
+    return xastrftime_msec("%Y-%m-%d %H:%M:%S.###", time_wall_msec(), true);
 }
 
 static void
diff --git a/lib/timeval.c b/lib/timeval.c
index 3262397..1e5eeb2 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -494,3 +494,46 @@ timeval_dummy_register(void)
     unixctl_command_register("time/warp", "MSECS", 1, 1,
                              timeval_warp_cb, NULL);
 }
+
+
+/* strftime() with an extension for high-resolution timestamps.  Any '#'s in
+ * 'format' will be replaced by subseconds, e.g. use "%S.###" to obtain results
+ * like "01.123".  */
+size_t
+strftime_msec(char *s, size_t max, const char *format,
+              const struct tm_msec *tm)
+{
+    size_t n;
+
+    n = strftime(s, max, format, &tm->tm);
+    if (n) {
+        char decimals[4];
+        char *d, *p;
+
+        sprintf(decimals, "%03d", tm->msec);
+        d = decimals;
+        for (p = strchr(s, '#'); p; p = strchr(p, '#')) {
+            *p++ = *d ? *d++ : '0';
+        }
+    }
+
+    return n;
+}
+
+struct tm_msec *
+localtime_msec(long long int now, struct tm_msec *result)
+{
+  time_t now_sec = now / 1000;
+  localtime_r(&now_sec, &result->tm);
+  result->msec = now % 1000;
+  return result;
+}
+
+struct tm_msec *
+gmtime_msec(long long int now, struct tm_msec *result)
+{
+  time_t now_sec = now / 1000;
+  gmtime_r(&now_sec, &result->tm);
+  result->msec = now % 1000;
+  return result;
+}
diff --git a/lib/timeval.h b/lib/timeval.h
index d0962ee..99b3af0 100644
--- a/lib/timeval.h
+++ b/lib/timeval.h
@@ -40,6 +40,11 @@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
 #define TIME_MAX TYPE_MAXIMUM(time_t)
 #define TIME_MIN TYPE_MINIMUM(time_t)
 
+struct tm_msec {
+  struct tm tm;
+  int msec;
+};
+
 time_t time_now(void);
 time_t time_wall(void);
 long long int time_msec(void);
@@ -53,6 +58,10 @@ int time_poll(struct pollfd *, int n_pollfds, long long int 
timeout_when,
 long long int timespec_to_msec(const struct timespec *);
 long long int timeval_to_msec(const struct timeval *);
 
+struct tm_msec *localtime_msec(long long int now, struct tm_msec *result);
+struct tm_msec *gmtime_msec(long long int now, struct tm_msec *result);
+size_t strftime_msec(char *s, size_t max, const char *format,
+                     const struct tm_msec *);
 void xgettimeofday(struct timeval *);
 void xclock_gettime(clock_t, struct timespec *);
 
diff --git a/lib/vlog.c b/lib/vlog.c
index a267112..37806b8d 100644
--- a/lib/vlog.c
+++ b/lib/vlog.c
@@ -583,7 +583,7 @@ static void
 vlog_init__(void)
 {
     static char *program_name_copy;
-    time_t now;
+    long long int now;
 
     /* openlog() is allowed to keep the pointer passed in, without making a
      * copy.  The daemonize code sometimes frees and replaces 'program_name',
@@ -593,10 +593,10 @@ vlog_init__(void)
     program_name_copy = program_name ? xstrdup(program_name) : NULL;
     openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
 
-    now = time_wall();
+    now = time_wall_msec();
     if (now < 0) {
-        char *s = xastrftime("%a, %d %b %Y %H:%M:%S", now, true);
-        VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
+        char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true);
+        VLOG_ERR("current time is negative: %s (%lld)", s, now);
         free(s);
     }
 
@@ -746,12 +746,12 @@ format_log_message(const struct vlog_module *module, enum 
vlog_level level,
             ds_put_cstr(s, vlog_get_module_name(module));
             break;
         case 'd':
-            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp);
-            ds_put_strftime(s, tmp, time_wall(), false);
+            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
+            ds_put_strftime_msec(s, tmp, time_wall_msec(), false);
             break;
         case 'D':
-            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp);
-            ds_put_strftime(s, tmp, time_wall(), true);
+            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
+            ds_put_strftime_msec(s, tmp, time_wall_msec(), true);
             break;
         case 'm':
             /* Format user-supplied log message and trim trailing new-lines. */
diff --git a/lib/vlog.h b/lib/vlog.h
index d2c6679..d7d63bf 100644
--- a/lib/vlog.h
+++ b/lib/vlog.h
@@ -64,7 +64,7 @@ enum vlog_level vlog_get_level_val(const char *name);
 #define VLOG_FACILITIES                                                 \
     VLOG_FACILITY(SYSLOG, "ovs|%05N|%c%T|%p|%m")                            \
     VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")    \
-    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")
+    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m")
 enum vlog_facility {
 #define VLOG_FACILITY(NAME, PATTERN) VLF_##NAME,
     VLOG_FACILITIES
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
index 7cd0485..8670127 100644
--- a/ovsdb/ovsdb-tool.c
+++ b/ovsdb/ovsdb-tool.c
@@ -519,7 +519,8 @@ do_show_log(int argc, char *argv[])
             date = shash_find_data(json_object(json), "_date");
             if (date && date->type == JSON_INTEGER) {
                 time_t t = json_integer(date);
-                char *s = xastrftime(" %Y-%m-%d %H:%M:%S", t, true);
+                char *s = xastrftime_msec(" %Y-%m-%d %H:%M:%S",
+                                          t * 1000LL, true);
                 fputs(s, stdout);
                 free(s);
             }
diff --git a/python/ovs/vlog.py b/python/ovs/vlog.py
index 25ae4ea..14679d9 100644
--- a/python/ovs/vlog.py
+++ b/python/ovs/vlog.py
@@ -60,7 +60,8 @@ class Vlog:
         if not Vlog.__inited:
             return
 
-        now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
+        dt = datetime.datetime.utcnow();
+        now = dt.strftime("%Y-%m-%dT%H:%M:%S.%%iZ") % (dt.microsecond/1000)
         syslog_message = ("%s|%s|%s|%s"
                            % (Vlog.__msg_num, self.name, level, message))
 
diff --git a/tests/vlog.at b/tests/vlog.at
index 957d872..35336ce 100644
--- a/tests/vlog.at
+++ b/tests/vlog.at
@@ -9,7 +9,7 @@ AT_CHECK([$PYTHON $srcdir/test-vlog.py --log-file log_file \
 
 AT_CHECK([diff log_file stderr_log])
 
-AT_CHECK([sed -e 's/.*-.*-.*T..:..:..Z|//' \
+AT_CHECK([sed -e 's/.*-.*-.*T..:..:..\....Z|//' \
 -e 's/File ".*", line [[0-9]][[0-9]]*,/File <name>, line <number>,/' \
 stderr_log], [0], [dnl
 0|module_0|EMER|emergency
diff --git a/utilities/ovs-appctl.8.in b/utilities/ovs-appctl.8.in
index df56309..1cf888d 100644
--- a/utilities/ovs-appctl.8.in
+++ b/utilities/ovs-appctl.8.in
@@ -158,13 +158,20 @@ The current date and time in ISO 8601 format 
(YYYY\-MM\-DD HH:MM:SS).
 .IP \fB%d{\fIformat\fB}\fR
 The current date and time in the specified \fIformat\fR, which takes
 the same format as the \fItemplate\fR argument to \fBstrftime\fR(3).
+As an extension, any \fB#\fR characters in \fIformat\fR will be
+replaced by fractional seconds, e.g. use \fB%H:%M:%S.###\fR for the
+time to the nearest millisecond.  Sub-second times are only
+approximate and currently decimal places after the third will always
+be reported as zero.
 .
 .IP \fB%D\fR
 The current UTC date and time in ISO 8601 format (YYYY\-MM\-DD HH:MM:SS).
 .
 .IP \fB%D{\fIformat\fB}\fR
-The current UTC date and time in the specified \fIformat\fR, which takes
-the same format as the \fItemplate\fR argument to \fBstrftime\fR(3).
+The current UTC date and time in the specified \fIformat\fR, which
+takes the same format as the \fItemplate\fR argument to
+\fBstrftime\fR(3).  Supports the same extension for sub-second
+resolution as \fB%d{\fR...\fB}\fR.
 .
 .IP \fB%m\fR
 The message being logged.
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 95bf1bf..a5a5c02 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1396,7 +1396,8 @@ monitor_vconn(struct vconn *vconn, bool 
reply_to_echo_requests)
             run(retval, "vconn_recv");
 
             if (timestamp) {
-                char *s = xastrftime("%Y-%m-%d %H:%M:%S: ", time_wall(), true);
+                char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ",
+                                          time_wall_msec(), true);
                 fputs(s, stderr);
                 free(s);
             }
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to