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).
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,
*/
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
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);
global.rlimit_memmax_all = HAPROXY_MEMMAX;
#endif
+ tzset();
tv_update_date(-1,-1);
start_date = now;
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)
{
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 */
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
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
*/
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
*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';