1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-04-07 01:31:35 +00:00

MEDIUM: log: Use linked lists for loggers

This patch settles the 2 loggers limitation.
Loggers are now stored in linked lists.

Using "global log", the global loggers list content is added at the end
of the current proxy list. Each "log" entries are added at the end of
the proxy list.

"no log" flush a logger list.
This commit is contained in:
William Lallemand 2011-10-12 17:50:54 +02:00 committed by Willy Tarreau
parent 0cec331a0e
commit 0f99e34978
10 changed files with 159 additions and 151 deletions

View File

@ -998,7 +998,7 @@ http-check send-state X - X X
http-request - X X X
id - X X X
ignore-persist - X X X
log X X X X
log (*) X X X X
maxconn X X X -
mode X X X X
monitor fail - X X -
@ -2560,9 +2560,16 @@ ignore-persist { if | unless } <condition>
log global
log <address> <facility> [<level> [<minlevel>]]
no log
Enable per-instance logging of events and traffic.
May be used in sections : defaults | frontend | listen | backend
yes | yes | yes | yes
Prefix :
no should be used when the logger list must be flushed. For example,
if you don't want to inherit from the default logger list. This
prefix does not allow arguments.
Arguments :
global should be used when the instance's logging parameters are the
same as the global ones. This is the most common usage. "global"
@ -2604,14 +2611,9 @@ log <address> <facility> [<level> [<minlevel>]]
emerg alert crit err warning notice info debug
Note that up to two "log" entries may be specified per instance. However, if
"log global" is used and if the "global" section already contains 2 log
entries, then additional log entries will be ignored.
Also, it is important to keep in mind that it is the frontend which decides
what to log from a connection, and that in case of content switching, the log
entries from the backend will be ignored. Connections are logged at level
"info".
It is important to keep in mind that it is the frontend which decides what to
log from a connection, and that in case of content switching, the log entries
from the backend will be ignored. Connections are logged at level "info".
However, backend log declaration define how and where servers status changes
will be logged. Level "notice" will be used to indicate a server going up,

View File

@ -79,10 +79,7 @@ struct global {
char *pidfile;
char *node, *desc; /* node name & description */
char *log_tag; /* name for syslog */
int logfac1, logfac2;
int loglev1, loglev2;
int minlvl1, minlvl2;
struct logsrv logsrv1, logsrv2;
struct list logsrvs;
char *log_send_hostname; /* set hostname in syslog header */
struct {
int maxpollevents; /* max number of poll events at once */

View File

@ -48,7 +48,11 @@
#define LW_RSPHDR 2048 /* response header(s) */
struct logsrv {
struct list list;
struct sockaddr_storage addr;
int facility;
int level;
int minlvl;
};
#endif /* _TYPES_LOG_H */

View File

@ -283,10 +283,7 @@ struct proxy {
char *iface_name; /* bind interface name or NULL */
int (*accept)(struct session *s); /* application layer's accept() */
struct proxy *next;
struct logsrv logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
int minlvl1, minlvl2; /* minimum log level for each server, 0 by default */
struct list logsrvs;
int to_log; /* things to be logged (LW_*) */
int stop_time; /* date to stop listening, when stopping != 0 (int ticks) */
struct hdr_exp *req_exp; /* regular expressions for request headers */

View File

@ -891,40 +891,57 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
goto out;
}
}
else if (!strcmp(args[0], "log") && kwm == KWM_NO) { /* no log */
/* delete previous herited or defined syslog servers */
struct logsrv *back;
struct logsrv *tmp;
if (*(args[1]) != 0) {
Alert("parsing [%s:%d]:%s : 'no log' does not expect arguments.\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
list_for_each_entry_safe(tmp, back, &global.logsrvs, list) {
LIST_DEL(&tmp->list);
free(tmp);
}
}
else if (!strcmp(args[0], "log")) { /* syslog server address */
struct logsrv logsrv;
int facility, level, minlvl;
struct logsrv *logsrv;
if (*(args[1]) == 0 || *(args[2]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <address> and <facility> as arguments.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
facility = get_log_facility(args[2]);
if (facility < 0) {
logsrv = calloc(1, sizeof(struct logsrv));
logsrv->facility = get_log_facility(args[2]);
if (logsrv->facility < 0) {
Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[2]);
err_code |= ERR_ALERT | ERR_FATAL;
facility = 0;
logsrv->facility = 0;
}
level = 7; /* max syslog level = debug */
logsrv->level = 7; /* max syslog level = debug */
if (*(args[3])) {
level = get_log_level(args[3]);
if (level < 0) {
logsrv->level = get_log_level(args[3]);
if (logsrv->level < 0) {
Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[3]);
err_code |= ERR_ALERT | ERR_FATAL;
level = 0;
logsrv->level = 0;
}
}
minlvl = 0; /* limit syslog level to this level (emerg) */
logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */
if (*(args[4])) {
minlvl = get_log_level(args[4]);
if (minlvl < 0) {
logsrv->minlvl = get_log_level(args[4]);
if (logsrv->minlvl < 0) {
Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[4]);
err_code |= ERR_ALERT | ERR_FATAL;
minlvl = 0;
logsrv->minlvl = 0;
}
}
@ -934,37 +951,24 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
Alert("parsing [%s:%d] : Socket path '%s' too long (max %d)\n", file, linenum,
args[1], (int)sizeof(((struct sockaddr_un *)&sk)->sun_path) - 1);
err_code |= ERR_ALERT | ERR_FATAL;
free(logsrv);
goto out;
}
logsrv.addr = *sk;
logsrv->addr = *sk;
} else {
struct sockaddr_storage *sk = str2sa(args[1]);
if (!sk) {
Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
free(logsrv);
goto out;
}
logsrv.addr = *sk;
if (!get_host_port(&logsrv.addr))
set_host_port(&logsrv.addr, SYSLOG_PORT);
logsrv->addr = *sk;
if (!get_host_port(&logsrv->addr))
set_host_port(&logsrv->addr, SYSLOG_PORT);
}
if (global.logfac1 == -1) {
global.logsrv1 = logsrv;
global.logfac1 = facility;
global.loglev1 = level;
global.minlvl1 = minlvl;
}
else if (global.logfac2 == -1) {
global.logsrv2 = logsrv;
global.logfac2 = facility;
global.loglev2 = level;
global.minlvl2 = minlvl;
}
else {
Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
}
LIST_ADDQ(&global.logsrvs, &logsrv->list);
}
else if (!strcmp(args[0], "log-send-hostname")) { /* set the hostname in syslog header */
char *name;
@ -1319,6 +1323,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
unsigned val;
int err_code = 0;
struct acl_cond *cond = NULL;
struct logsrv *tmp;
if (!strcmp(args[0], "listen"))
rc = PR_CAP_LISTEN;
@ -1521,14 +1526,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
}
curproxy->mode = defproxy.mode;
curproxy->logfac1 = defproxy.logfac1;
curproxy->logsrv1 = defproxy.logsrv1;
curproxy->loglev1 = defproxy.loglev1;
curproxy->minlvl1 = defproxy.minlvl1;
curproxy->logfac2 = defproxy.logfac2;
curproxy->logsrv2 = defproxy.logsrv2;
curproxy->loglev2 = defproxy.loglev2;
curproxy->minlvl2 = defproxy.minlvl2;
/* copy default logsrvs to curproxy */
list_for_each_entry(tmp, &defproxy.logsrvs, list) {
struct logsrv *node = malloc(sizeof(struct logsrv));
memcpy(node, tmp, sizeof(struct logsrv));
LIST_INIT(&node->list);
LIST_ADDQ(&curproxy->logsrvs, &node->list);
}
curproxy->grace = defproxy.grace;
curproxy->conf.used_listener_id = EB_ROOT;
curproxy->conf.used_server_id = EB_ROOT;
@ -4503,44 +4509,64 @@ stats_error_parsing:
newsrv->prev_state = newsrv->state;
}
}
else if (!strcmp(args[0], "log") && kwm == KWM_NO) {
/* delete previous herited or defined syslog servers */
struct logsrv *back;
if (*(args[1]) != 0) {
Alert("parsing [%s:%d]:%s : 'no log' does not expect arguments.\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
list_for_each_entry_safe(tmp, back, &curproxy->logsrvs, list) {
LIST_DEL(&tmp->list);
free(tmp);
}
}
else if (!strcmp(args[0], "log")) { /* syslog server address */
struct logsrv logsrv;
int facility;
struct logsrv *logsrv;
if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
curproxy->logfac1 = global.logfac1;
curproxy->logsrv1 = global.logsrv1;
curproxy->loglev1 = global.loglev1;
curproxy->minlvl1 = global.minlvl1;
curproxy->logfac2 = global.logfac2;
curproxy->logsrv2 = global.logsrv2;
curproxy->loglev2 = global.loglev2;
curproxy->minlvl2 = global.minlvl2;
/* copy global.logrsvs linked list to the end of curproxy->logsrvs */
list_for_each_entry(tmp, &global.logsrvs, list) {
struct logsrv *node = malloc(sizeof(struct logsrv));
memcpy(node, tmp, sizeof(struct logsrv));
LIST_INIT(&node->list);
LIST_ADDQ(&curproxy->logsrvs, &node->list);
}
}
else if (*(args[1]) && *(args[2])) {
int level, minlvl;
facility = get_log_facility(args[2]);
if (facility < 0) {
logsrv = calloc(1, sizeof(struct logsrv));
logsrv->facility = get_log_facility(args[2]);
if (logsrv->facility < 0) {
Alert("parsing [%s:%d] : unknown log facility '%s'\n", file, linenum, args[2]);
exit(1);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
level = 7; /* max syslog level = debug */
logsrv->level = 7; /* max syslog level = debug */
if (*(args[3])) {
level = get_log_level(args[3]);
if (level < 0) {
logsrv->level = get_log_level(args[3]);
if (logsrv->level < 0) {
Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[3]);
exit(1);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
minlvl = 0; /* limit syslog level to this level (emerg) */
logsrv->minlvl = 0; /* limit syslog level to this level (emerg) */
if (*(args[4])) {
minlvl = get_log_level(args[4]);
if (level < 0) {
logsrv->minlvl = get_log_level(args[4]);
if (logsrv->level < 0) {
Alert("parsing [%s:%d] : unknown optional minimum log level '%s'\n", file, linenum, args[4]);
exit(1);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
@ -4552,7 +4578,7 @@ stats_error_parsing:
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
logsrv.addr = *sk;
logsrv->addr = *sk;
} else {
struct sockaddr_storage *sk = str2sa(args[1]);
if (!sk) {
@ -4560,28 +4586,12 @@ stats_error_parsing:
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
logsrv.addr = *sk;
if (!get_host_port(&logsrv.addr))
set_host_port(&logsrv.addr, SYSLOG_PORT);
}
if (curproxy->logfac1 == -1) {
curproxy->logsrv1 = logsrv;
curproxy->logfac1 = facility;
curproxy->loglev1 = level;
curproxy->minlvl1 = minlvl;
}
else if (curproxy->logfac2 == -1) {
curproxy->logsrv2 = logsrv;
curproxy->logfac2 = facility;
curproxy->loglev2 = level;
curproxy->minlvl2 = minlvl;
}
else {
Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
logsrv->addr = *sk;
if (!get_host_port(&logsrv->addr))
set_host_port(&logsrv->addr, SYSLOG_PORT);
}
LIST_ADDQ(&curproxy->logsrvs, &logsrv->list);
}
else {
Alert("parsing [%s:%d] : 'log' expects either <address[:port]> and <facility> or 'global' as arguments.\n",
@ -5427,9 +5437,14 @@ int readcfgfile(const char *file)
/* check for keyword modifiers "no" and "default" */
if (!strcmp(args[0], "no")) {
char *tmp;
kwm = KWM_NO;
tmp = args[0];
for (arg=0; *args[arg+1]; arg++)
args[arg] = args[arg+1]; // shift args after inversion
*tmp = '\0'; // fix the next arg to \0
args[arg] = tmp;
}
else if (!strcmp(args[0], "default")) {
kwm = KWM_DEF;
@ -5437,8 +5452,9 @@ int readcfgfile(const char *file)
args[arg] = args[arg+1]; // shift args after inversion
}
if (kwm != KWM_STD && strcmp(args[0], "option") != 0) {
Alert("parsing [%s:%d]: negation/default currently supported only for options.\n", file, linenum);
if (kwm != KWM_STD && strcmp(args[0], "option") != 0 && \
strcmp(args[0], "log") != 0) {
Alert("parsing [%s:%d]: negation/default currently supported only for options and log.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
}
@ -6072,7 +6088,7 @@ out_uri_auth_compat:
curproxy->to_log &= ~LW_BYTES;
if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) &&
(curproxy->cap & PR_CAP_FE) && curproxy->to_log && curproxy->logfac1 < 0) {
(curproxy->cap & PR_CAP_FE) && curproxy->to_log && LIST_ISEMPTY(&curproxy->logsrvs)) {
Warning("config : log format ignored for %s '%s' since it has no log address.\n",
proxy_type_str(curproxy), curproxy->id);
err_code |= ERR_WARN;

View File

@ -144,7 +144,7 @@ int frontend_accept(struct session *s)
}
if ((s->fe->mode == PR_MODE_TCP || s->fe->mode == PR_MODE_HTTP)
&& (s->fe->logfac1 >= 0 || s->fe->logfac2 >= 0)) {
&& (!LIST_ISEMPTY(&s->fe->logsrvs))) {
if (likely(s->fe->to_log)) {
/* we have the client ip */
if (s->logs.logwait & LW_CLIP)

View File

@ -103,10 +103,7 @@ int relative_pid = 1; /* process id starting at 1 */
/* global options */
struct global global = {
logfac1 : -1,
logfac2 : -1,
loglev1 : 7, /* max syslog level : debug */
loglev2 : 7,
.logsrvs = LIST_HEAD_INIT(global.logsrvs),
.stats_sock = {
.perm = {
.ux = {
@ -788,6 +785,7 @@ void deinit(void)
struct wordlist *wl, *wlb;
struct cond_wordlist *cwl, *cwlb;
struct uri_auth *uap, *ua = NULL;
struct logsrv *log, *logb;
int i;
deinit_signals();
@ -893,6 +891,11 @@ void deinit(void)
free(rdr);
}
list_for_each_entry_safe(log, logb, &p->logsrvs, list) {
LIST_DEL(&log->list);
free(log);
}
deinit_tcp_rules(&p->tcp_req.inspect_rules);
deinit_tcp_rules(&p->tcp_req.l4_rules);
@ -995,6 +998,10 @@ void deinit(void)
free(oldpids); oldpids = NULL;
free(global_listener_queue_task); global_listener_queue_task = NULL;
list_for_each_entry_safe(log, logb, &global.logsrvs, list) {
LIST_DEL(&log->list);
free(log);
}
list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
LIST_DEL(&wl->list);
free(wl);

View File

@ -157,10 +157,9 @@ void send_log(struct proxy *p, int level, const char *message, ...)
static char *dataptr = NULL;
int fac_level;
int hdr_len, data_len;
struct logsrv *logsrvs[2];
int facilities[2], loglevel[2], minlvl[2];
struct list *logsrvs = NULL;
struct logsrv *tmp = NULL;
int nblogger;
int nbloggers = 0;
char *log_ptr;
if (level < 0 || message == NULL)
@ -201,42 +200,25 @@ void send_log(struct proxy *p, int level, const char *message, ...)
dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
if (p == NULL) {
if (global.logfac1 >= 0) {
logsrvs[nbloggers] = &global.logsrv1;
facilities[nbloggers] = global.logfac1;
loglevel[nbloggers] = global.loglev1;
minlvl[nbloggers] = global.minlvl1;
nbloggers++;
}
if (global.logfac2 >= 0) {
logsrvs[nbloggers] = &global.logsrv2;
facilities[nbloggers] = global.logfac2;
loglevel[nbloggers] = global.loglev2;
minlvl[nbloggers] = global.minlvl2;
nbloggers++;
if (!LIST_ISEMPTY(&global.logsrvs)) {
logsrvs = &global.logsrvs;
}
} else {
if (p->logfac1 >= 0) {
logsrvs[nbloggers] = &p->logsrv1;
facilities[nbloggers] = p->logfac1;
loglevel[nbloggers] = p->loglev1;
minlvl[nbloggers] = p->minlvl1;
nbloggers++;
}
if (p->logfac2 >= 0) {
logsrvs[nbloggers] = &p->logsrv2;
facilities[nbloggers] = p->logfac2;
loglevel[nbloggers] = p->loglev2;
minlvl[nbloggers] = p->minlvl2;
nbloggers++;
if (!LIST_ISEMPTY(&p->logsrvs)) {
logsrvs = &p->logsrvs;
}
}
if (!logsrvs)
return;
/* Lazily set up syslog sockets for protocol families of configured
* syslog servers. */
for (nblogger = 0; nblogger < nbloggers; nblogger++) {
const struct logsrv *logsrv = logsrvs[nblogger];
nblogger = 0;
list_for_each_entry(tmp, logsrvs, list) {
const struct logsrv *logsrv = tmp;
int proto, *plogfd;
if (logsrv->addr.ss_family == AF_UNIX) {
proto = 0;
plogfd = &logfdunix;
@ -258,17 +240,19 @@ void send_log(struct proxy *p, int level, const char *message, ...)
setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
/* does nothing under Linux, maybe needed for others */
shutdown(*plogfd, SHUT_RD);
nblogger++;
}
/* Send log messages to syslog server. */
for (nblogger = 0; nblogger < nbloggers; nblogger++) {
const struct logsrv *logsrv = logsrvs[nblogger];
nblogger = 0;
list_for_each_entry(tmp, logsrvs, list) {
const struct logsrv *logsrv = tmp;
int *plogfd = logsrv->addr.ss_family == AF_UNIX ?
&logfdunix : &logfdinet;
int sent;
/* we can filter the level of the messages that are sent to each logger */
if (level > loglevel[nblogger])
if (level > logsrv->level)
continue;
/* For each target, we may have a different facility.
@ -278,7 +262,7 @@ void send_log(struct proxy *p, int level, const char *message, ...)
* time, we only change the facility in the pre-computed header,
* and we change the pointer to the header accordingly.
*/
fac_level = (facilities[nblogger] << 3) + MAX(level, minlvl[nblogger]);
fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
log_ptr = logmsg + 3; /* last digit of the log level */
do {
*log_ptr = '0' + fac_level % 10;
@ -295,6 +279,7 @@ void send_log(struct proxy *p, int level, const char *message, ...)
Alert("sendto logger #%d failed: %s (errno=%d)\n",
nblogger, strerror(errno), errno);
}
nblogger++;
}
}
@ -320,7 +305,7 @@ void tcp_sess_log(struct session *s)
addr_to_str(&s->si[0].addr.from, pn, sizeof(pn));
get_localtime(s->logs.tv_accept.tv_sec, &tm);
if (fe->logfac1 < 0 && fe->logfac2 < 0)
if(LIST_ISEMPTY(&fe->logsrvs))
return;
prx_log = fe;

View File

@ -1097,7 +1097,7 @@ void http_sess_log(struct session *s)
if (!err && (fe->options2 & PR_O2_NOLOGNORM))
return;
if (fe->logfac1 < 0 && fe->logfac2 < 0)
if (LIST_ISEMPTY(&fe->logsrvs))
return;
prx_log = fe;

View File

@ -436,11 +436,11 @@ void init_new_proxy(struct proxy *p)
LIST_INIT(&p->req_add);
LIST_INIT(&p->rsp_add);
LIST_INIT(&p->listener_queue);
LIST_INIT(&p->logsrvs);
/* Timeouts are defined as -1 */
proxy_reset_timeouts(p);
p->tcp_rep.inspect_delay = TICK_ETERNITY;
p->logfac1 = p->logfac2 = -1; /* log disabled */
}
/*