diff --git a/doc/configuration.txt b/doc/configuration.txt index 3b76b01e2..fc1684355 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -565,7 +565,7 @@ group Similar to "gid" but uses the GID of group name from /etc/group. See also "gid" and "user". -log
[max level [min level]] +log
[len ] [max level [min level]] Adds a global syslog server. Up to two global servers can be defined. They will receive logs for startups and exits, as well as all logs from proxies configured with "log global". @@ -590,6 +590,18 @@ log
[max level [min level]] optionally enclosing them with braces ('{}'), similarly to what is done in Bourne shell. + is an optional maximum line length. Log lines larger than this value + will be truncated before being sent. The reason is that syslog + servers act differently on log line length. All servers support the + default value of 1024, but some servers simply drop larger lines + while others do log them. If a server supports long lines, it may + make sense to set this value here in order to avoid truncating long + lines. Similarly, if a server drops long lines, it is preferable to + truncate them before sending them. Accepted values are 80 to 65535 + inclusive. The default value of 1024 is generally fine for all + standard usages. Some specific cases of long captures or + JSON-formated logs may require larger values. + must be one of the 24 standard syslog facilities : kern user mail daemon auth syslog lpr news @@ -3358,7 +3370,7 @@ ignore-persist { if | unless } log global -log
[ []] +log
[len ] [ []] no log Enable per-instance logging of events and traffic. May be used in sections : defaults | frontend | listen | backend @@ -3398,6 +3410,18 @@ no log sign ('$') and optionally enclosing them with braces ('{}'), similarly to what is done in Bourne shell. + is an optional maximum line length. Log lines larger than this + value will be truncated before being sent. The reason is that + syslog servers act differently on log line length. All servers + support the default value of 1024, but some servers simply drop + larger lines while others do log them. If a server supports long + lines, it may make sense to set this value here in order to avoid + truncating long lines. Similarly, if a server drops long lines, + it is preferable to truncate them before sending them. Accepted + values are 80 to 65535 inclusive. The default value of 1024 is + generally fine for all standard usages. Some specific cases of + long captures or JSON-formated logs may require larger values. + must be one of the 24 standard syslog facilities : kern user mail daemon auth syslog lpr news diff --git a/include/proto/log.h b/include/proto/log.h index e3c1a75eb..54c51d16c 100644 --- a/include/proto/log.h +++ b/include/proto/log.h @@ -39,6 +39,7 @@ extern char *log_format; extern char default_tcp_log_format[]; extern char default_http_log_format[]; extern char clf_http_log_format[]; +extern char *logline; int build_logline(struct session *s, char *dst, size_t maxsize, struct list *list_format); diff --git a/include/types/global.h b/include/types/global.h index e351a6e36..14a6bf2e1 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -111,6 +111,7 @@ struct global { int last_checks; int spread_checks; int max_spread_checks; + int max_syslog_len; char *chroot; char *pidfile; char *node, *desc; /* node name & description */ diff --git a/include/types/log.h b/include/types/log.h index b3288bd69..c7e47ea4a 100644 --- a/include/types/log.h +++ b/include/types/log.h @@ -152,6 +152,7 @@ struct logsrv { int facility; int level; int minlvl; + int maxlen; }; #endif /* _TYPES_LOG_H */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 6a331d1c4..16cf717b2 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1257,6 +1257,8 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) struct sockaddr_storage *sk; int port1, port2; struct logsrv *logsrv; + int arg = 0; + int len = 0; if (*(args[1]) == 0 || *(args[2]) == 0) { Alert("parsing [%s:%d] : '%s' expects
and as arguments.\n", file, linenum, args[0]); @@ -1266,28 +1268,50 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) logsrv = calloc(1, sizeof(struct logsrv)); - logsrv->facility = get_log_facility(args[2]); + /* just after the address, a length may be specified */ + if (strcmp(args[arg+2], "len") == 0) { + len = atoi(args[arg+3]); + if (len < 80 || len > 65535) { + Alert("parsing [%s:%d] : invalid log length '%s', must be between 80 and 65535.\n", + file, linenum, args[arg+3]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + logsrv->maxlen = len; + + /* skip these two args */ + arg += 2; + } + else + logsrv->maxlen = MAX_SYSLOG_LEN; + + if (logsrv->maxlen > global.max_syslog_len) { + global.max_syslog_len = logsrv->maxlen; + logline = realloc(logline, global.max_syslog_len + 1); + } + + logsrv->facility = get_log_facility(args[arg+2]); if (logsrv->facility < 0) { - Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[2]); + Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[arg+2]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->facility = 0; } logsrv->level = 7; /* max syslog level = debug */ - if (*(args[3])) { - logsrv->level = get_log_level(args[3]); + if (*(args[arg+3])) { + logsrv->level = get_log_level(args[arg+3]); if (logsrv->level < 0) { - Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[3]); + Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[arg+3]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->level = 0; } } logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */ - if (*(args[4])) { - logsrv->minlvl = get_log_level(args[4]); + if (*(args[arg+4])) { + logsrv->minlvl = get_log_level(args[arg+4]); if (logsrv->minlvl < 0) { - Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[4]); + Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[arg+4]); err_code |= ERR_ALERT | ERR_FATAL; logsrv->minlvl = 0; } @@ -4843,22 +4867,46 @@ stats_error_parsing: else if (*(args[1]) && *(args[2])) { struct sockaddr_storage *sk; int port1, port2; + int arg = 0; + int len = 0; logsrv = calloc(1, sizeof(struct logsrv)); - logsrv->facility = get_log_facility(args[2]); + /* just after the address, a length may be specified */ + if (strcmp(args[arg+2], "len") == 0) { + len = atoi(args[arg+3]); + if (len < 80 || len > 65535) { + Alert("parsing [%s:%d] : invalid log length '%s', must be between 80 and 65535.\n", + file, linenum, args[arg+3]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + logsrv->maxlen = len; + + /* skip these two args */ + arg += 2; + } + else + logsrv->maxlen = MAX_SYSLOG_LEN; + + if (logsrv->maxlen > global.max_syslog_len) { + global.max_syslog_len = logsrv->maxlen; + logline = realloc(logline, global.max_syslog_len + 1); + } + + logsrv->facility = get_log_facility(args[arg+2]); if (logsrv->facility < 0) { - Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[2]); + Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[arg+2]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } logsrv->level = 7; /* max syslog level = debug */ - if (*(args[3])) { - logsrv->level = get_log_level(args[3]); + if (*(args[arg+3])) { + logsrv->level = get_log_level(args[arg+3]); if (logsrv->level < 0) { - Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[3]); + Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[arg+3]); err_code |= ERR_ALERT | ERR_FATAL; goto out; @@ -4866,10 +4914,10 @@ stats_error_parsing: } logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */ - if (*(args[4])) { - logsrv->minlvl = get_log_level(args[4]); + if (*(args[arg+4])) { + logsrv->minlvl = get_log_level(args[arg+4]); if (logsrv->minlvl < 0) { - Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[4]); + Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[arg+4]); err_code |= ERR_ALERT | ERR_FATAL; goto out; diff --git a/src/log.c b/src/log.c index 114ab7b61..3e3acb4cb 100644 --- a/src/log.c +++ b/src/log.c @@ -146,7 +146,7 @@ char *log_format = NULL; /* This is a global syslog line, common to all outgoing messages. It begins * with the syslog tag and the date that are updated by update_log_hdr(). */ -static char logline[MAX_SYSLOG_LEN]; +char *logline = NULL; struct logformat_var_args { char *name; @@ -736,7 +736,7 @@ static char *update_log_hdr() tvsec = date.tv_sec; get_localtime(tvsec, &tm); - hdr_len = snprintf(logline, MAX_SYSLOG_LEN, + hdr_len = snprintf(logline, global.max_syslog_len, "<<<<>%s %2d %02d:%02d:%02d %s%s[%d]: ", monthname[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, @@ -746,8 +746,8 @@ static char *update_log_hdr() * 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 > MAX_SYSLOG_LEN) - hdr_len = MAX_SYSLOG_LEN; + if (hdr_len < 0 || hdr_len > global.max_syslog_len) + hdr_len = global.max_syslog_len; dataptr = logline + hdr_len; } @@ -772,9 +772,9 @@ void send_log(struct proxy *p, int level, const char *format, ...) data_len = dataptr - logline; va_start(argp, format); - data_len += vsnprintf(dataptr, logline + sizeof(logline) - dataptr, format, argp); - if (data_len < 0 || data_len > MAX_SYSLOG_LEN) - data_len = MAX_SYSLOG_LEN; + data_len += vsnprintf(dataptr, logline + global.max_syslog_len - dataptr, format, argp); + if (data_len < 0 || data_len > global.max_syslog_len) + data_len = global.max_syslog_len; va_end(argp); __send_log(p, level, logline, data_len); @@ -811,8 +811,6 @@ void __send_log(struct proxy *p, int level, char *message, size_t size) if (!logsrvs) return; - message[size - 1] = '\n'; - /* Send log messages to syslog server. */ nblogger = 0; list_for_each_entry(tmp, logsrvs, list) { @@ -820,6 +818,8 @@ void __send_log(struct proxy *p, int level, char *message, size_t size) int *plogfd = logsrv->addr.ss_family == AF_UNIX ? &logfdunix : &logfdinet; int sent; + int max; + char backup; nblogger++; @@ -858,9 +858,23 @@ void __send_log(struct proxy *p, int level, char *message, size_t size) } while (fac_level && log_ptr > dataptr); *log_ptr = '<'; - sent = sendto(*plogfd, log_ptr, size - (log_ptr - dataptr), + max = size - (log_ptr - dataptr); + if (max > logsrv->maxlen) + max = logsrv->maxlen; + + /* insert a \n at the end of the message, but save what was + * there first because we could have different max lengths + * for different log targets. + */ + backup = log_ptr[max - 1]; + log_ptr[max - 1] = '\n'; + + sent = sendto(*plogfd, log_ptr, max, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&logsrv->addr, get_addr_len(&logsrv->addr)); + + log_ptr[max - 1] = backup; + if (sent < 0) { Alert("sendto logger #%d failed: %s (errno=%d)\n", nblogger, strerror(errno), errno); @@ -1604,7 +1618,7 @@ void sess_log(struct session *s) tmplog = update_log_hdr(); size = tmplog - logline; - size += build_logline(s, tmplog, sizeof(logline) - size, &s->fe->logformat); + size += build_logline(s, tmplog, global.max_syslog_len - size, &s->fe->logformat); if (size > 0) { __send_log(s->fe, level, logline, size + 1); s->logs.logwait = 0;