From: Dragan Dosen Date: Tue, 22 Sep 2015 14:05:32 +0000 (+0200) Subject: MEDIUM: logs: add support for RFC5424 header format per logger X-Git-Tag: v1.6-dev6~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1322d09a6f8fb2adb6309f9bcad6bd19e67f607d;p=thirdparty%2Fhaproxy.git 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 e8b57d867f..fca7fdd2fd 100644 --- a/include/proto/log.h +++ b/include/proto/log.h @@ -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 or -1 if not found. + */ +int get_log_format(const char *fmt); + /* * returns log level for 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 */ diff --git a/include/types/log.h b/include/types/log.h index 0214ae675a..2cfd31a8bc 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 logformat_node { 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 1400126dea..9793a29f5d 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -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_*) */ diff --git a/src/cfgparse.c b/src/cfgparse.c index fdbb462f6e..af2d7771fa 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -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)) { diff --git a/src/haproxy.c b/src/haproxy.c index 1edb823d76..9539dfa309 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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); diff --git a/src/log.c b/src/log.c index ad25174d22..8fe2348569 100644 --- a/src/log.c +++ b/src/log.c @@ -41,6 +41,11 @@ #include #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 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 or -1 if not found. */ @@ -631,7 +658,6 @@ int get_log_level(const char *lev) return level; } - /* * returns log facility for 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;