[Please use the attached patch, my version of Thunderbird replaces tabs by spaces]
GMT offset used in local time formats was computed at startup, but was not updated when DST status changed while running.
For example these two RFC5424 syslog traces where emitted 5 seconds apart, just before and after DST changed:
<14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 2098 - - Connect ...
<14>1 2016-03-27T03:00:03+01:00 bunch-VirtualBox haproxy 2098 - - Connect ...
It looked like they were emitted more than 1 hour apart, unlike with the fix:
<14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 3381 - - Connect ...
<14>1 2016-03-27T03:00:03+02:00 bunch-VirtualBox haproxy 3381 - - Connect ...
This patch should be backported to 1.6 and partially to 1.5 (no fix needed in log.c).
---
include/common/standard.h | 9 ++++++---
src/haproxy.c | 5 -----
src/log.c | 4 +++-
src/standard.c | 34 +++++++++++++++++++++++++++++++++-
4 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/include/common/standard.h b/include/common/standard.h
index 9abdb06..353d0b0 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -860,9 +860,6 @@ char *human_time(int t, short hz_div);
extern const char *monthname[];
-/* numeric timezone (that is, the hour and minute offset from UTC) */
-char localtimezone[6];
-
/* date2str_log: write a date in the format :
* sprintf(str, "%02d/%s/%04d:%02d:%02d:%02d.%03d",
* tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
@@ -873,6 +870,12 @@ char localtimezone[6];
*/
char *date2str_log(char *dest, struct tm *tm, struct timeval *date, size_t size);
+/* Return the GMT offset for a specific local time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(struct tm *tm);
+
/* gmt2str_log: write a date in the format :
* "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
* return a pointer to the last char written (\0) or
diff --git a/src/haproxy.c b/src/haproxy.c
index 4d38d27..aada213 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -562,7 +562,6 @@ void init(int argc, char **argv)
struct wordlist *wl;
char *progname;
char *change_dir = NULL;
- struct tm curtime;
struct proxy *px;
chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
@@ -593,10 +592,6 @@ void init(int argc, char **argv)
srandom(now_ms - getpid());
- /* Get the numeric timezone. */
- get_localtime(start_date.tv_sec, &curtime);
- strftime(localtimezone, 6, "%z", &curtime);
-
init_log();
signal_init();
if (init_acl() != 0)
diff --git a/src/log.c b/src/log.c
index 3e25bc7..ab38353 100644
--- a/src/log.c
+++ b/src/log.c
@@ -970,6 +970,7 @@ static char *update_log_hdr_rfc5424(const time_t time)
{
static long tvsec;
static char *dataptr = NULL; /* backup of last end of header, NULL first time */
+ const char *gmt_offset;
if (unlikely(time != tvsec || dataptr == NULL)) {
/* this string is rebuild only once a second */
@@ -978,12 +979,13 @@ static char *update_log_hdr_rfc5424(const time_t time)
tvsec = time;
get_localtime(tvsec, &tm);
+ gmt_offset = get_gmt_offset(&tm);
hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
"<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
- localtimezone, localtimezone+3,
+ gmt_offset, gmt_offset+3,
global.log_send_hostname ? global.log_send_hostname : hostname);
/* WARNING: depending upon implementations, snprintf may return
* either -1 or the number of bytes that would be needed to store
diff --git a/src/standard.c b/src/standard.c
index 30883d7..52ddaa2 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -2550,6 +2550,35 @@ char *date2str_log(char *dst, struct tm *tm, struct timeval *date, size_t size)
return dst;
}
+/* Return the GMT offset for a specific local time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(struct tm *tm)
+{
+ /* Cache offsets from GMT (depending on whether DST is active or not) */
+ static char gmt_offsets[2][5+1] = { "", "" };
+
+ int old_isdst = tm->tm_isdst;
+ char *gmt_offset;
+
+ /* Pretend DST not active if its status is unknown, or strftime() will return an empty string for "%z" */
+ if (tm->tm_isdst < 0) {
+ tm->tm_isdst = 0;
+ }
+
+ /* Fetch the offset and initialize it if needed */
+ gmt_offset = gmt_offsets[tm->tm_isdst & 0x01];
+ if (unlikely(!*gmt_offset)) {
+ strftime(gmt_offset, 5+1, "%z", tm);
+ }
+
+ /* Restore previous DST flag */
+ tm->tm_isdst = old_isdst;
+
+ return gmt_offset;
+}
+
/* gmt2str_log: write a date in the format :
* "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
* return a pointer to the last char written (\0) or
@@ -2590,9 +2619,12 @@ char *gmt2str_log(char *dst, struct tm *tm, size_t size)
*/
char *localdate2str_log(char *dst, struct tm *tm, size_t size)
{
+ const char *gmt_offset;
if (size < 27) /* the size is fixed: 26 chars + \0 */
return NULL;
+ gmt_offset = get_gmt_offset(tm);
+
dst = utoa_pad((unsigned int)tm->tm_mday, dst, 3); // day
*dst++ = '/';
memcpy(dst, monthname[tm->tm_mon], 3); // month
@@ -2606,7 +2638,7 @@ char *localdate2str_log(char *dst, struct tm *tm, size_t size)
*dst++ = ':';
dst = utoa_pad((unsigned int)tm->tm_sec, dst, 3); // secondes
*dst++ = ' ';
- memcpy(dst, localtimezone, 5); // timezone
+ memcpy(dst, gmt_offset, 5); // Offset from local time to GMT
dst += 5;
*dst = '\0';
--
2.5.0
From 530b5340ab0e45d86a99e21f080a05e9a71beebd Mon Sep 17 00:00:00 2001
From: Benoit GARNIER <chezbunch+haproxy@gmail.com>
Date: Sun, 27 Mar 2016 11:08:03 +0200
Subject: [PATCH] BUG/MINOR: log: GMT offset not updated when entering/leaving DST
GMT offset used in local time formats was computed at startup, but was not updated when DST status changed while running.
For example these two RFC5424 syslog traces where emitted 5 seconds apart, just before and after DST changed:
<14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 2098 - - Connect ...
<14>1 2016-03-27T03:00:03+01:00 bunch-VirtualBox haproxy 2098 - - Connect ...
It looked like they were emitted more than 1 hour apart, unlike with the fix:
<14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 3381 - - Connect ...
<14>1 2016-03-27T03:00:03+02:00 bunch-VirtualBox haproxy 3381 - - Connect ...
This patch should be backported to 1.6 and partially to 1.5 (no fix needed in log.c).
---
include/common/standard.h | 9 ++++++---
src/haproxy.c | 5 -----
src/log.c | 4 +++-
src/standard.c | 34 +++++++++++++++++++++++++++++++++-
4 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/include/common/standard.h b/include/common/standard.h
index 9abdb06..353d0b0 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -860,9 +860,6 @@ char *human_time(int t, short hz_div);
extern const char *monthname[];
-/* numeric timezone (that is, the hour and minute offset from UTC) */
-char localtimezone[6];
-
/* date2str_log: write a date in the format :
* sprintf(str, "%02d/%s/%04d:%02d:%02d:%02d.%03d",
* tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
@@ -873,6 +870,12 @@ char localtimezone[6];
*/
char *date2str_log(char *dest, struct tm *tm, struct timeval *date, size_t size);
+/* Return the GMT offset for a specific local time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(struct tm *tm);
+
/* gmt2str_log: write a date in the format :
* "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
* return a pointer to the last char written (\0) or
diff --git a/src/haproxy.c b/src/haproxy.c
index 4d38d27..aada213 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -562,7 +562,6 @@ void init(int argc, char **argv)
struct wordlist *wl;
char *progname;
char *change_dir = NULL;
- struct tm curtime;
struct proxy *px;
chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
@@ -593,10 +592,6 @@ void init(int argc, char **argv)
srandom(now_ms - getpid());
- /* Get the numeric timezone. */
- get_localtime(start_date.tv_sec, &curtime);
- strftime(localtimezone, 6, "%z", &curtime);
-
init_log();
signal_init();
if (init_acl() != 0)
diff --git a/src/log.c b/src/log.c
index 3e25bc7..ab38353 100644
--- a/src/log.c
+++ b/src/log.c
@@ -970,6 +970,7 @@ static char *update_log_hdr_rfc5424(const time_t time)
{
static long tvsec;
static char *dataptr = NULL; /* backup of last end of header, NULL first time */
+ const char *gmt_offset;
if (unlikely(time != tvsec || dataptr == NULL)) {
/* this string is rebuild only once a second */
@@ -978,12 +979,13 @@ static char *update_log_hdr_rfc5424(const time_t time)
tvsec = time;
get_localtime(tvsec, &tm);
+ gmt_offset = get_gmt_offset(&tm);
hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
"<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
- localtimezone, localtimezone+3,
+ gmt_offset, gmt_offset+3,
global.log_send_hostname ? global.log_send_hostname : hostname);
/* WARNING: depending upon implementations, snprintf may return
* either -1 or the number of bytes that would be needed to store
diff --git a/src/standard.c b/src/standard.c
index 30883d7..52ddaa2 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -2550,6 +2550,35 @@ char *date2str_log(char *dst, struct tm *tm, struct timeval *date, size_t size)
return dst;
}
+/* Return the GMT offset for a specific local time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(struct tm *tm)
+{
+ /* Cache offsets from GMT (depending on whether DST is active or not) */
+ static char gmt_offsets[2][5+1] = { "", "" };
+
+ int old_isdst = tm->tm_isdst;
+ char *gmt_offset;
+
+ /* Pretend DST not active if its status is unknown, or strftime() will return an empty string for "%z" */
+ if (tm->tm_isdst < 0) {
+ tm->tm_isdst = 0;
+ }
+
+ /* Fetch the offset and initialize it if needed */
+ gmt_offset = gmt_offsets[tm->tm_isdst & 0x01];
+ if (unlikely(!*gmt_offset)) {
+ strftime(gmt_offset, 5+1, "%z", tm);
+ }
+
+ /* Restore previous DST flag */
+ tm->tm_isdst = old_isdst;
+
+ return gmt_offset;
+}
+
/* gmt2str_log: write a date in the format :
* "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
* return a pointer to the last char written (\0) or
@@ -2590,9 +2619,12 @@ char *gmt2str_log(char *dst, struct tm *tm, size_t size)
*/
char *localdate2str_log(char *dst, struct tm *tm, size_t size)
{
+ const char *gmt_offset;
if (size < 27) /* the size is fixed: 26 chars + \0 */
return NULL;
+ gmt_offset = get_gmt_offset(tm);
+
dst = utoa_pad((unsigned int)tm->tm_mday, dst, 3); // day
*dst++ = '/';
memcpy(dst, monthname[tm->tm_mon], 3); // month
@@ -2606,7 +2638,7 @@ char *localdate2str_log(char *dst, struct tm *tm, size_t size)
*dst++ = ':';
dst = utoa_pad((unsigned int)tm->tm_sec, dst, 3); // secondes
*dst++ = ' ';
- memcpy(dst, localtimezone, 5); // timezone
+ memcpy(dst, gmt_offset, 5); // Offset from local time to GMT
dst += 5;
*dst = '\0';
--
2.5.0