MEDIUM: logs: add support for RFC5424 header format per logger
The function __send_log() iterates over senders and passes the header as
the first vector to sendmsg(), thus it can send a logger-specific header
in each message.
A new logger arguments "format rfc5424" should be used in order to enable
RFC5424 header format. For example:
log 10.2.3.4:1234 len 2048 format rfc5424 local2 info
diff --git a/include/proto/log.h b/include/proto/log.h
index e8b57d8..fca7fdd 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -39,7 +39,12 @@
extern char default_tcp_log_format[];
extern char default_http_log_format[];
extern char clf_http_log_format[];
+
+extern char default_host_tag_pid_log_format[];
+extern char rfc5424_host_tag_pid_log_format[];
+
extern char *logheader;
+extern char *logheader_rfc5424;
extern char *logline;
@@ -109,6 +114,11 @@
void __send_log(struct proxy *p, int level, char *message, size_t size);
/*
+ * returns log format for <fmt> or -1 if not found.
+ */
+int get_log_format(const char *fmt);
+
+/*
* returns log level for <lev> or -1 if not found.
*/
int get_log_level(const char *lev);
@@ -141,7 +151,7 @@
/*
* Write hostname, log_tag and pid to the log string
*/
-char *lf_host_tag_pid(char *dst, const char *hostname, const char *log_tag, int pid, size_t size);
+char *lf_host_tag_pid(char *dst, const char *format, const char *hostname, const char *log_tag, int pid, size_t size);
#endif /* _PROTO_LOG_H */
diff --git a/include/types/log.h b/include/types/log.h
index 0214ae6..2cfd31a 100644
--- a/include/types/log.h
+++ b/include/types/log.h
@@ -37,6 +37,13 @@
/* The array containing the names of the log levels. */
extern const char *log_levels[];
+/* enum for log format */
+enum {
+ LOG_FORMAT_RFC3164 = 0,
+ LOG_FORMAT_RFC5424,
+ LOG_FORMATS, /* number of supported log formats, must always be last */
+};
+
/* lists of fields that can be logged */
enum {
@@ -158,6 +165,7 @@
struct logsrv {
struct list list;
struct sockaddr_storage addr;
+ int format;
int facility;
int level;
int minlvl;
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 1400126..9793a29 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -346,7 +346,8 @@
struct list logsrvs;
struct list logformat; /* log_format linked list */
char *log_tag; /* override default syslog tag */
- struct chunk log_htp; /* a syslog header part that contains hostname, log_tag and pid */
+ struct chunk log_htp; /* a syslog header part that contains hostname, log_tag and pid (RFC3164 format) */
+ struct chunk log_htp_rfc5424; /* a syslog header part that contains hostname, log_tag and pid (RFC5424 format) */
char *header_unique_id; /* unique-id header */
struct list format_unique_id; /* unique-id format */
int to_log; /* things to be logged (LW_*) */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index fdbb462..af2d777 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1572,9 +1572,23 @@
if (logsrv->maxlen > global.max_syslog_len) {
global.max_syslog_len = logsrv->maxlen;
logheader = realloc(logheader, global.max_syslog_len + 1);
+ logheader_rfc5424 = realloc(logheader_rfc5424, global.max_syslog_len + 1);
logline = realloc(logline, global.max_syslog_len + 1);
}
+ /* after the length, a format may be specified */
+ if (strcmp(args[arg+2], "format") == 0) {
+ logsrv->format = get_log_format(args[arg+3]);
+ if (logsrv->format < 0) {
+ Alert("parsing [%s:%d] : unknown log format '%s'\n", file, linenum, args[arg+3]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ /* skip these two args */
+ arg += 2;
+ }
+
if (alertif_too_many_args_idx(3, arg + 1, file, linenum, args, &err_code))
goto out;
@@ -5855,9 +5869,23 @@
if (logsrv->maxlen > global.max_syslog_len) {
global.max_syslog_len = logsrv->maxlen;
logheader = realloc(logheader, global.max_syslog_len + 1);
+ logheader_rfc5424 = realloc(logheader_rfc5424, global.max_syslog_len + 1);
logline = realloc(logline, global.max_syslog_len + 1);
}
+ /* after the length, a format may be specified */
+ if (strcmp(args[arg+2], "format") == 0) {
+ logsrv->format = get_log_format(args[arg+3]);
+ if (logsrv->format < 0) {
+ Alert("parsing [%s:%d] : unknown log format '%s'\n", file, linenum, args[arg+3]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ /* skip these two args */
+ arg += 2;
+ }
+
if (alertif_too_many_args_idx(3, arg + 1, file, linenum, args, &err_code))
goto out;
@@ -7217,6 +7245,7 @@
struct sticking_rule *mrule;
struct act_rule *trule;
struct act_rule *hrqrule;
+ struct logsrv *tmplogsrv;
unsigned int next_id;
int nbproc;
@@ -7819,25 +7848,62 @@
out_uri_auth_compat:
/* write a syslog header string that contains hostname, log_tag and pid */
- curproxy->log_htp.str = lf_host_tag_pid(logheader,
- global.log_send_hostname ? global.log_send_hostname : "",
- curproxy->log_tag ? curproxy->log_tag : global.log_tag, pid,
- global.max_syslog_len);
+ list_for_each_entry(tmplogsrv, &curproxy->logsrvs, list) {
+ char *hdr;
+ struct chunk *htp;
+ char *htp_fmt;
+ char *host = global.log_send_hostname;
- if ((curproxy->log_htp.str == NULL) ||
- (curproxy->log_htp.len = curproxy->log_htp.str - logheader) >= global.max_syslog_len) {
- Alert("Proxy '%s': cannot write a syslog header string that contains "
- "hostname, log_tag and pid.\n",
- curproxy->id);
- cfgerr++;
- }
- else {
- curproxy->log_htp.str = (char *)malloc(curproxy->log_htp.len);
- memcpy(curproxy->log_htp.str, logheader, curproxy->log_htp.len);
- curproxy->log_htp.size = 0;
- }
+ switch (tmplogsrv->format) {
+ case LOG_FORMAT_RFC3164:
+ hdr = logheader;
+ htp = &curproxy->log_htp;
+ htp_fmt = default_host_tag_pid_log_format;
+ host = host ? host : "";
+ break;
- logheader[0] = 0;
+ case LOG_FORMAT_RFC5424:
+ hdr = logheader_rfc5424;
+ htp = &curproxy->log_htp_rfc5424;
+ htp_fmt = rfc5424_host_tag_pid_log_format;
+ break;
+
+ default:
+ continue; /* must never happen */
+ }
+
+ if (htp->str)
+ continue;
+
+ if (!host) {
+ int len = strlen(hostname);
+ host = malloc(len + 2);
+ snprintf(host, len + 2, "%s ", hostname);
+ }
+
+ htp->str = lf_host_tag_pid(hdr, htp_fmt, host,
+ curproxy->log_tag ? curproxy->log_tag : global.log_tag,
+ pid, global.max_syslog_len);
+
+ if ((host != global.log_send_hostname) && strlen(host))
+ free(host);
+
+ if ((htp->str == NULL) ||
+ ((htp->len = htp->str - hdr) >= global.max_syslog_len)) {
+ Alert("Proxy '%s': cannot write a syslog header string that contains "
+ "hostname, log_tag and pid.\n",
+ curproxy->id);
+ cfgerr++;
+ goto out_host_tag_pid;
+ }
+
+ htp->str = (char *)malloc(htp->len);
+ memcpy(htp->str, hdr, htp->len);
+ htp->size = 0;
+
+ hdr[0] = 0;
+ }
+out_host_tag_pid:
/* compile the log format */
if (!(curproxy->cap & PR_CAP_FE)) {
diff --git a/src/haproxy.c b/src/haproxy.c
index 1edb823..9539dfa 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1329,6 +1329,7 @@
free(log);
}
chunk_destroy(&p->log_htp);
+ chunk_destroy(&p->log_htp_rfc5424);
list_for_each_entry_safe(lf, lfb, &p->logformat, list) {
LIST_DEL(&lf->list);
diff --git a/src/log.c b/src/log.c
index ad25174..8fe2348 100644
--- a/src/log.c
+++ b/src/log.c
@@ -41,6 +41,11 @@
#include <proto/ssl_sock.h>
#endif
+const char *log_formats[LOG_FORMATS] = {
+ [LOG_FORMAT_RFC3164] = "rfc3164",
+ [LOG_FORMAT_RFC5424] = "rfc5424"
+};
+
const char *log_facilities[NB_LOG_FACILITIES] = {
"kern", "user", "mail", "daemon",
"auth", "syslog", "lpr", "news",
@@ -50,7 +55,6 @@
"local4", "local5", "local6", "local7"
};
-
const char *log_levels[NB_LOG_LEVELS] = {
"emerg", "alert", "crit", "err",
"warning", "notice", "info", "debug"
@@ -151,11 +155,20 @@
char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq";
char *log_format = NULL;
-/* This is a global syslog header, common to all outgoing messages. It
- * begins with time-based part and is updated by update_log_hdr().
+char default_host_tag_pid_log_format[] = "%s%s[%d]: ";
+char rfc5424_host_tag_pid_log_format[] = "%s%s %d - - ";
+
+/* This is a global syslog header, common to all outgoing messages in
+ * RFC3164 format. It begins with time-based part and is updated by
+ * update_log_hdr().
*/
char *logheader = NULL;
+/* This is a global syslog header for messages in RFC5424 format. It is
+ * updated by update_log_hdr_rfc5424().
+ */
+char *logheader_rfc5424 = NULL;
+
/* This is a global syslog message buffer, common to all outgoing
* messages. It contains only the data part.
*/
@@ -618,6 +631,20 @@
}
/*
+ * returns log format for <fmt> or -1 if not found.
+ */
+int get_log_format(const char *fmt)
+{
+ int format;
+
+ format = LOG_FORMATS - 1;
+ while (format >= 0 && strcmp(log_formats[format], fmt))
+ format--;
+
+ return format;
+}
+
+/*
* returns log level for <lev> or -1 if not found.
*/
int get_log_level(const char *lev)
@@ -631,7 +658,6 @@
return level;
}
-
/*
* returns log facility for <fac> or -1 if not found.
*/
@@ -739,12 +765,12 @@
return ret;
}
-char *lf_host_tag_pid(char *dst, const char *hostname, const char *log_tag, int pid, size_t size)
+char *lf_host_tag_pid(char *dst, const char *format, const char *hostname, const char *log_tag, int pid, size_t size)
{
char *ret = dst;
int iret;
- iret = snprintf(dst, size, "%s%s[%d]: ", hostname, log_tag, pid);
+ iret = snprintf(dst, size, format, hostname, log_tag, pid);
if (iret < 0 || iret > size)
return NULL;
ret += iret;
@@ -752,10 +778,11 @@
return ret;
}
-/* Re-generate the syslog header at the beginning of logheader once a second and
- * return the pointer to the first character after the header.
+/* Re-generate time-based part of the syslog header in RFC3164 format at
+ * the beginning of logheader once a second and return the pointer to the
+ * first character after it.
*/
-char *update_log_hdr()
+static char *update_log_hdr()
{
static long tvsec;
static char *dataptr = NULL; /* backup of last end of header, NULL first time */
@@ -787,6 +814,43 @@
return dataptr;
}
+/* Re-generate time-based part of the syslog header in RFC5424 format at
+ * the beginning of logheader_rfc5424 once a second and return the pointer
+ * to the first character after it.
+ */
+static char *update_log_hdr_rfc5424()
+{
+ static long tvsec;
+ static char *dataptr = NULL; /* backup of last end of header, NULL first time */
+
+ if (unlikely(date.tv_sec != tvsec || dataptr == NULL)) {
+ /* this string is rebuild only once a second */
+ struct tm tm;
+ int hdr_len;
+
+ tvsec = date.tv_sec;
+ get_localtime(tvsec, &tm);
+
+ hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
+ "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%s ",
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ localtimezone);
+ /* WARNING: depending upon implementations, snprintf may return
+ * either -1 or the number of bytes that would be needed to store
+ * the total message. In both cases, we must adjust it.
+ */
+ if (hdr_len < 0 || hdr_len > global.max_syslog_len)
+ hdr_len = global.max_syslog_len;
+
+ dataptr = logheader_rfc5424 + hdr_len;
+ }
+
+ dataptr[0] = 0; // ensure we get rid of any previous attempt
+
+ return dataptr;
+}
+
/*
* This function sends the syslog message using a printf format string. It
* expects an LF-terminated message.
@@ -828,8 +892,9 @@
struct logsrv *tmp = NULL;
int nblogger;
char *log_ptr;
- char *hdr_ptr;
+ char *hdr, *hdr_ptr;
size_t hdr_size;
+ struct chunk *htp;
dataptr = message;
@@ -846,9 +911,6 @@
if (!logsrvs)
return;
- hdr_ptr = update_log_hdr();
- hdr_size = hdr_ptr - logheader;
-
/* Send log messages to syslog server. */
nblogger = 0;
list_for_each_entry(tmp, logsrvs, list) {
@@ -883,6 +945,25 @@
shutdown(*plogfd, SHUT_RD);
}
+ switch (logsrv->format) {
+ case LOG_FORMAT_RFC3164:
+ hdr = logheader;
+ hdr_ptr = update_log_hdr();
+ htp = &p->log_htp;
+ break;
+
+ case LOG_FORMAT_RFC5424:
+ hdr = logheader_rfc5424;
+ hdr_ptr = update_log_hdr_rfc5424();
+ htp = &p->log_htp_rfc5424;
+ break;
+
+ default:
+ continue; /* must never happen */
+ }
+
+ hdr_size = hdr_ptr - hdr;
+
/* For each target, we may have a different facility.
* We can also have a different log level for each message.
* This induces variations in the message header length.
@@ -891,17 +972,17 @@
* and we change the pointer to the header accordingly.
*/
fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
- hdr_ptr = logheader + 3; /* last digit of the log level */
+ hdr_ptr = hdr + 3; /* last digit of the log level */
do {
*hdr_ptr = '0' + fac_level % 10;
fac_level /= 10;
hdr_ptr--;
- } while (fac_level && hdr_ptr > logheader);
+ } while (fac_level && hdr_ptr > hdr);
*hdr_ptr = '<';
log_ptr = dataptr;
- hdr_max = hdr_size - (hdr_ptr - logheader);
+ hdr_max = hdr_size - (hdr_ptr - hdr);
if (unlikely(hdr_size >= logsrv->maxlen)) {
hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
@@ -909,7 +990,7 @@
}
maxlen = logsrv->maxlen - hdr_max;
- htp_max = p->log_htp.len;
+ htp_max = htp->len;
if (unlikely(htp_max >= maxlen)) {
htp_max = maxlen - 1;
@@ -930,7 +1011,7 @@
iovec[0].iov_base = hdr_ptr;
iovec[0].iov_len = hdr_max;
- iovec[1].iov_base = p->log_htp.str;
+ iovec[1].iov_base = htp->str;
iovec[1].iov_len = htp_max;
iovec[2].iov_base = dataptr;
iovec[2].iov_len = max;