]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: logs: add support for RFC5424 header format per logger
authorDragan Dosen <ddosen@haproxy.com>
Tue, 22 Sep 2015 14:05:32 +0000 (16:05 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 28 Sep 2015 12:01:27 +0000 (14:01 +0200)
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

include/proto/log.h
include/types/log.h
include/types/proxy.h
src/cfgparse.c
src/haproxy.c
src/log.c

index e8b57d867f3c66fee7a369aa1121da621c586081..fca7fdd2fd9d4b08f1bd23d37bda79f7a067dac3 100644 (file)
@@ -39,7 +39,12 @@ extern char *log_format;
 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;
 
 
@@ -108,6 +113,11 @@ void send_log(struct proxy *p, int level, const char *format, ...)
 
 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.
  */
@@ -141,7 +151,7 @@ char *lf_port(char *dst, struct sockaddr *sockaddr, size_t size, struct logforma
 /*
  * 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 */
index 0214ae675aaf31f8957ca92e19e183de6cb08092..2cfd31a8bce64abc2c04f9821ac7b5945f0974dd 100644 (file)
 /* 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 logformat_node {
 struct logsrv {
        struct list list;
        struct sockaddr_storage addr;
+       int format;
        int facility;
        int level;
        int minlvl;
index 1400126deac7fdd0f0dbd2309fb1ed1b040d0a07..9793a29f5d7600c993fb2f759c3cea64b99228f0 100644 (file)
@@ -346,7 +346,8 @@ struct proxy {
        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_*) */
index fdbb462f6e63e4ead44f2d80a3715758a3ce0ffe..af2d7771fa32a6bd73564c1509193e200ed27ab6 100644 (file)
@@ -1572,9 +1572,23 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
                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 @@ stats_error_parsing:
                        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 @@ int check_config_validity()
                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 @@ int check_config_validity()
 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);
-
-               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;
-               }
+               list_for_each_entry(tmplogsrv, &curproxy->logsrvs, list) {
+                       char *hdr;
+                       struct chunk *htp;
+                       char *htp_fmt;
+                       char *host = global.log_send_hostname;
+
+                       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;
+
+                       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;
 
-               logheader[0] = 0;
+                       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)) {
index 1edb823d763f2c05d227b760d494c9069cd394dc..9539dfa30965e76697790fe88a2548d52cae0abd 100644 (file)
@@ -1329,6 +1329,7 @@ void deinit(void)
                        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);
index ad25174d224fff2528d98eb6c9f0834d8f721440..8fe23485698bdea51b4f605b0dc7a4dd018bdbbd 100644 (file)
--- a/src/log.c
+++ b/src/log.c
 #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 @@ const char *log_facilities[NB_LOG_FACILITIES] = {
        "local4", "local5", "local6", "local7"
 };
 
-
 const char *log_levels[NB_LOG_LEVELS] = {
        "emerg", "alert", "crit", "err",
        "warning", "notice", "info", "debug"
@@ -151,11 +155,20 @@ char clf_http_log_format[] = "%{+Q}o %{-Q}ci - - [%T] %r %ST %B \"\" \"\" %cp %m
 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.
  */
@@ -617,6 +630,20 @@ void qfprintf(FILE *out, const char *fmt, ...)
        }
 }
 
+/*
+ * 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.
  */
@@ -631,7 +658,6 @@ int get_log_level(const char *lev)
        return level;
 }
 
-
 /*
  * returns log facility for <fac> or -1 if not found.
  */
@@ -739,12 +765,12 @@ char *lf_port(char *dst, struct sockaddr *sockaddr, size_t size, struct logforma
        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 @@ char *lf_host_tag_pid(char *dst, const char *hostname, const char *log_tag, int
        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 @@ char *update_log_hdr()
        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 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
        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 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
        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 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
                        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 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
                 * 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 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
                }
 
                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 @@ send:
 
                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;