From 0f99e349780083834afc188b499357c87d5b04fd Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Wed, 12 Oct 2011 17:50:54 +0200 Subject: 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. --- doc/configuration.txt | 20 +++-- include/types/global.h | 5 +- include/types/log.h | 4 + include/types/proxy.h | 5 +- src/cfgparse.c | 198 ++++++++++++++++++++++++++---------------------- src/frontend.c | 2 +- src/haproxy.c | 15 +++- src/log.c | 57 +++++--------- src/proto_http.c | 2 +- src/proxy.c | 2 +- 10 files changed, 159 insertions(+), 151 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index afe1bfc..8aeeb27 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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 } log global log
[ []] +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
[ []] 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, diff --git a/include/types/global.h b/include/types/global.h index 56110e8..9e63050 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -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 */ diff --git a/include/types/log.h b/include/types/log.h index 44b37a0..9b700b0 100644 --- a/include/types/log.h +++ b/include/types/log.h @@ -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 */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 2abd213..593260e 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -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 */ diff --git a/src/cfgparse.c b/src/cfgparse.c index b6650cd..bf2eb36 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -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
and 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 and 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; diff --git a/src/frontend.c b/src/frontend.c index 19980c0..a32018c 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -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) diff --git a/src/haproxy.c b/src/haproxy.c index cc16359..363f306 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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); diff --git a/src/log.c b/src/log.c index 162ff48..dd1b63f 100644 --- a/src/log.c +++ b/src/log.c @@ -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; diff --git a/src/proto_http.c b/src/proto_http.c index fd298b7..87115d2 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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; diff --git a/src/proxy.c b/src/proxy.c index caec45d..28094a8 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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 */ } /* -- 1.7.2.3