MEDIUM: log: support a user-configurable max log line length

With all the goodies supported by logformat, people find that the limit
of 1024 chars for log lines is too short. Some servers do not support
larger lines and can simply drop them, so changing the default value is
not always the best choice.

This patch takes a different approach. Log line length is specified per
log server on the "log" line, with a value between 80 and 65535. That
way it's possibly to satisfy all needs, even with some fat local servers
and small remote ones.
This commit is contained in:
Willy Tarreau 2014-06-27 18:10:07 +02:00
parent 4e957907aa
commit 18324f574f
6 changed files with 118 additions and 29 deletions

View File

@ -565,7 +565,7 @@ group <group name>
Similar to "gid" but uses the GID of group name <group name> from /etc/group. Similar to "gid" but uses the GID of group name <group name> from /etc/group.
See also "gid" and "user". See also "gid" and "user".
log <address> <facility> [max level [min level]] log <address> [len <length>] <facility> [max level [min level]]
Adds a global syslog server. Up to two global servers can be defined. They 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 will receive logs for startups and exits, as well as all logs from proxies
configured with "log global". configured with "log global".
@ -590,6 +590,18 @@ log <address> <facility> [max level [min level]]
optionally enclosing them with braces ('{}'), similarly to what is done optionally enclosing them with braces ('{}'), similarly to what is done
in Bourne shell. in Bourne shell.
<length> 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.
<facility> must be one of the 24 standard syslog facilities : <facility> must be one of the 24 standard syslog facilities :
kern user mail daemon auth syslog lpr news kern user mail daemon auth syslog lpr news
@ -3358,7 +3370,7 @@ ignore-persist { if | unless } <condition>
log global log global
log <address> <facility> [<level> [<minlevel>]] log <address> [len <length>] <facility> [<level> [<minlevel>]]
no log no log
Enable per-instance logging of events and traffic. Enable per-instance logging of events and traffic.
May be used in sections : defaults | frontend | listen | backend May be used in sections : defaults | frontend | listen | backend
@ -3398,6 +3410,18 @@ no log
sign ('$') and optionally enclosing them with braces ('{}'), sign ('$') and optionally enclosing them with braces ('{}'),
similarly to what is done in Bourne shell. similarly to what is done in Bourne shell.
<length> 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.
<facility> must be one of the 24 standard syslog facilities : <facility> must be one of the 24 standard syslog facilities :
kern user mail daemon auth syslog lpr news kern user mail daemon auth syslog lpr news

View File

@ -39,6 +39,7 @@ extern char *log_format;
extern char default_tcp_log_format[]; extern char default_tcp_log_format[];
extern char default_http_log_format[]; extern char default_http_log_format[];
extern char clf_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); int build_logline(struct session *s, char *dst, size_t maxsize, struct list *list_format);

View File

@ -111,6 +111,7 @@ struct global {
int last_checks; int last_checks;
int spread_checks; int spread_checks;
int max_spread_checks; int max_spread_checks;
int max_syslog_len;
char *chroot; char *chroot;
char *pidfile; char *pidfile;
char *node, *desc; /* node name & description */ char *node, *desc; /* node name & description */

View File

@ -152,6 +152,7 @@ struct logsrv {
int facility; int facility;
int level; int level;
int minlvl; int minlvl;
int maxlen;
}; };
#endif /* _TYPES_LOG_H */ #endif /* _TYPES_LOG_H */

View File

@ -1257,6 +1257,8 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
struct sockaddr_storage *sk; struct sockaddr_storage *sk;
int port1, port2; int port1, port2;
struct logsrv *logsrv; struct logsrv *logsrv;
int arg = 0;
int len = 0;
if (*(args[1]) == 0 || *(args[2]) == 0) { if (*(args[1]) == 0 || *(args[2]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <address> and <facility> as arguments.\n", file, linenum, args[0]); Alert("parsing [%s:%d] : '%s' expects <address> and <facility> 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 = 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) { 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; err_code |= ERR_ALERT | ERR_FATAL;
logsrv->facility = 0; logsrv->facility = 0;
} }
logsrv->level = 7; /* max syslog level = debug */ logsrv->level = 7; /* max syslog level = debug */
if (*(args[3])) { if (*(args[arg+3])) {
logsrv->level = get_log_level(args[3]); logsrv->level = get_log_level(args[arg+3]);
if (logsrv->level < 0) { 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; err_code |= ERR_ALERT | ERR_FATAL;
logsrv->level = 0; logsrv->level = 0;
} }
} }
logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */ logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */
if (*(args[4])) { if (*(args[arg+4])) {
logsrv->minlvl = get_log_level(args[4]); logsrv->minlvl = get_log_level(args[arg+4]);
if (logsrv->minlvl < 0) { 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; err_code |= ERR_ALERT | ERR_FATAL;
logsrv->minlvl = 0; logsrv->minlvl = 0;
} }
@ -4843,22 +4867,46 @@ stats_error_parsing:
else if (*(args[1]) && *(args[2])) { else if (*(args[1]) && *(args[2])) {
struct sockaddr_storage *sk; struct sockaddr_storage *sk;
int port1, port2; int port1, port2;
int arg = 0;
int len = 0;
logsrv = calloc(1, sizeof(struct logsrv)); 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) { 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; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
logsrv->level = 7; /* max syslog level = debug */ logsrv->level = 7; /* max syslog level = debug */
if (*(args[3])) { if (*(args[arg+3])) {
logsrv->level = get_log_level(args[3]); logsrv->level = get_log_level(args[arg+3]);
if (logsrv->level < 0) { 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; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
@ -4866,10 +4914,10 @@ stats_error_parsing:
} }
logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */ logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */
if (*(args[4])) { if (*(args[arg+4])) {
logsrv->minlvl = get_log_level(args[4]); logsrv->minlvl = get_log_level(args[arg+4]);
if (logsrv->minlvl < 0) { 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; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;

View File

@ -146,7 +146,7 @@ char *log_format = NULL;
/* This is a global syslog line, common to all outgoing messages. It begins /* 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(). * 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 { struct logformat_var_args {
char *name; char *name;
@ -736,7 +736,7 @@ static char *update_log_hdr()
tvsec = date.tv_sec; tvsec = date.tv_sec;
get_localtime(tvsec, &tm); 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]: ", "<<<<>%s %2d %02d:%02d:%02d %s%s[%d]: ",
monthname[tm.tm_mon], monthname[tm.tm_mon],
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 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 * either -1 or the number of bytes that would be needed to store
* the total message. In both cases, we must adjust it. * the total message. In both cases, we must adjust it.
*/ */
if (hdr_len < 0 || hdr_len > MAX_SYSLOG_LEN) if (hdr_len < 0 || hdr_len > global.max_syslog_len)
hdr_len = MAX_SYSLOG_LEN; hdr_len = global.max_syslog_len;
dataptr = logline + hdr_len; dataptr = logline + hdr_len;
} }
@ -772,9 +772,9 @@ void send_log(struct proxy *p, int level, const char *format, ...)
data_len = dataptr - logline; data_len = dataptr - logline;
va_start(argp, format); va_start(argp, format);
data_len += vsnprintf(dataptr, logline + sizeof(logline) - dataptr, format, argp); data_len += vsnprintf(dataptr, logline + global.max_syslog_len - dataptr, format, argp);
if (data_len < 0 || data_len > MAX_SYSLOG_LEN) if (data_len < 0 || data_len > global.max_syslog_len)
data_len = MAX_SYSLOG_LEN; data_len = global.max_syslog_len;
va_end(argp); va_end(argp);
__send_log(p, level, logline, data_len); __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) if (!logsrvs)
return; return;
message[size - 1] = '\n';
/* Send log messages to syslog server. */ /* Send log messages to syslog server. */
nblogger = 0; nblogger = 0;
list_for_each_entry(tmp, logsrvs, list) { 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 ? int *plogfd = logsrv->addr.ss_family == AF_UNIX ?
&logfdunix : &logfdinet; &logfdunix : &logfdinet;
int sent; int sent;
int max;
char backup;
nblogger++; nblogger++;
@ -858,9 +858,23 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
} while (fac_level && log_ptr > dataptr); } while (fac_level && log_ptr > dataptr);
*log_ptr = '<'; *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, MSG_DONTWAIT | MSG_NOSIGNAL,
(struct sockaddr *)&logsrv->addr, get_addr_len(&logsrv->addr)); (struct sockaddr *)&logsrv->addr, get_addr_len(&logsrv->addr));
log_ptr[max - 1] = backup;
if (sent < 0) { if (sent < 0) {
Alert("sendto logger #%d failed: %s (errno=%d)\n", Alert("sendto logger #%d failed: %s (errno=%d)\n",
nblogger, strerror(errno), errno); nblogger, strerror(errno), errno);
@ -1604,7 +1618,7 @@ void sess_log(struct session *s)
tmplog = update_log_hdr(); tmplog = update_log_hdr();
size = tmplog - logline; 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) { if (size > 0) {
__send_log(s->fe, level, logline, size + 1); __send_log(s->fe, level, logline, size + 1);
s->logs.logwait = 0; s->logs.logwait = 0;