diff --git a/doc/configuration.txt b/doc/configuration.txt index 0b3d564a8..2a4672b05 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2738,6 +2738,57 @@ timeout server timeout server 10s server mysyslogsrv 127.0.0.1:6514 log-proto octet-count +3.10. Log forwarding +------------------- + +It is possible to declare one or multiple log forwarding section, +haproxy will forward all received log messages to a log servers list. + +log-forward + Creates a new log forwarder proxy identified as . + +bind [param*] + Used to configure a log udp listener to receive messages to forward. + Only udp listeners are allowed, address must be prefixed using + 'udp@', 'udp4@' or 'udp6@'. This supports for all "bind" parameters + found in 5.1 paragraph but most of them are irrelevant for udp/syslog case. + +log global +log
[len ] [format ] [sample :] + [ []] + Used to configure target log servers. See more details on proxies + documentation. + If no format specified, haproxy tries to keep the incoming log format. + Configured facility is ignored, except if incoming message does not + present a facility but one is mandatory on the outgoing format. + If there is no timestamp available in the input format, but the field + exists in output format, haproxy will use the local date. + + Example: + global + log stderr format iso local7 + + ring myring + description "My local buffer" + format rfc5424 + maxlen 1200 + size 32764 + timeout connect 5s + timeout server 10s + # syslog tcp server + server mysyslogsrv 127.0.0.1:514 log-proto octet-count + + log-forward sylog-loadb + bind udp4@127.0.0.1:1514 + # all messages on stderr + log global + # all messages on local tcp syslog server + log ring@myring local0 + # load balance messages on 4 udp syslog servers + log 127.0.0.1:10001 sample 1:4 local0 + log 127.0.0.1:10002 sample 2:4 local0 + log 127.0.0.1:10003 sample 3:4 local0 + log 127.0.0.1:10004 sample 4:4 local0 4. Proxies ---------- @@ -3323,14 +3374,14 @@ bind / [, ...] [param*] - 'ipv4@' -> address is always IPv4 - 'ipv6@' -> address is always IPv6 - 'udp@' -> address is resolved as IPv4 or IPv6 and - protocol UDP is used. Currently there is no proxy - mode supporting those listeners. + protocol UDP is used. Currently those listeners are + supported only in log-forward sections. - 'udp4@' -> address is always IPv4 and protocol UDP - is used. Currently there is no proxy mode supporting - those listeners. + is used. Currently those listeners are supported + only in log-forward sections. - 'udp6@' -> address is always IPv6 and protocol UDP - is used. Currently there is no proxy mode supporting - those listeners. + is used. Currently those listeners are supported + only in log-forward sections. - 'unix@' -> address is a path to a local unix socket - 'abns@' -> address is in abstract namespace (Linux only). Note: since abstract sockets are not "rebindable", they diff --git a/include/haproxy/log.h b/include/haproxy/log.h index 683c135a0..d8b95a82b 100644 --- a/include/haproxy/log.h +++ b/include/haproxy/log.h @@ -43,6 +43,9 @@ extern char default_rfc5424_sd_log_format[]; extern unsigned int dropped_logs; +/* lof forward proxy list */ +extern struct proxy *cfg_log_forward; + extern THREAD_LOCAL char *logline; extern THREAD_LOCAL char *logline_rfc5424; diff --git a/src/log.c b/src/log.c index b6d5c45fd..f8ac52201 100644 --- a/src/log.c +++ b/src/log.c @@ -26,12 +26,15 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -43,6 +46,9 @@ #include +/* log forward proxy list */ +struct proxy *cfg_log_forward; + struct log_fmt_st { char *name; }; @@ -3543,6 +3549,144 @@ out: return; } +/* + * Parse "log-forward" section and create corresponding sink buffer. + * + * The function returns 0 in success case, otherwise, it returns error + * flags. + */ +int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm) +{ + int err_code = 0; + struct proxy *px; + char *errmsg = NULL; + const char *err = NULL; + + if (strcmp(args[0], "log-forward") == 0) { + if (!*args[1]) { + ha_alert("parsing [%s:%d] : missing name for ip-forward section.\n", file, linenum); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto out; + + err = invalid_char(args[1]); + if (err) { + ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n", + file, linenum, *err, args[0], args[1]); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + + for (px = cfg_log_forward ; px ; px = px->next) { + if (strcmp(px->id, args[1]) == 0) { + ha_alert("Parsing [%s:%d]: log-forward section '%s' has the same name as another log-forward section declared at %s:%d.\n", + file, linenum, args[1], px->conf.file, px->conf.line); + err_code |= ERR_ALERT | ERR_FATAL; + } + } + + px = calloc(1, sizeof *px); + if (!px) { + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + px->next = cfg_log_forward; + cfg_log_forward = px; + + init_new_proxy(px); + px->conf.file = strdup(file); + px->conf.line = linenum; + px->mode = PR_MODE_SYSLOG; + px->id = strdup(args[1]); + + } + else if (strcmp(args[0], "bind") == 0) { + int cur_arg; + static int kws_dumped; + struct bind_conf *bind_conf; + struct bind_kw *kw; + struct listener *l; + + cur_arg = 1; + + bind_conf = bind_conf_alloc(cfg_log_forward, file, linenum, + NULL, xprt_get(XPRT_RAW)); + + if (!str2listener(args[1], cfg_log_forward, bind_conf, file, linenum, &errmsg)) { + if (errmsg && *errmsg) { + indent_msg(&errmsg, 2); + ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); + } + else { + ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n", + file, linenum, args[0], args[1], args[2]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + list_for_each_entry(l, &bind_conf->listeners, by_bind) { + /* Currently, only UDP handlers are allowed */ + if (l->proto->sock_domain != AF_CUST_UDP4 && l->proto->sock_domain != AF_CUST_UDP6) { + ha_alert("parsing [%s:%d] : '%s %s' : error, listening address must be prefixed using 'udp@', 'udp4@' or 'udp6@' %s.\n", + file, linenum, args[0], args[1], args[2]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + l->maxaccept = global.tune.maxaccept ? global.tune.maxaccept : 64; + global.maxsock++; + } + cur_arg++; + + while (*args[cur_arg] && (kw = bind_find_kw(args[cur_arg]))) { + int ret; + + ret = kw->parse(args, cur_arg, cfg_log_forward, bind_conf, &errmsg); + err_code |= ret; + if (ret) { + if (errmsg && *errmsg) { + indent_msg(&errmsg, 2); + ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); + } + else + ha_alert("parsing [%s:%d]: error encountered while processing '%s'\n", + file, linenum, args[cur_arg]); + if (ret & ERR_FATAL) + goto out; + } + cur_arg += 1 + kw->skip; + } + if (*args[cur_arg] != 0) { + char *kws = NULL; + + if (!kws_dumped) { + kws_dumped = 1; + bind_dump_kws(&kws); + indent_msg(&kws, 4); + } + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n", + file, linenum, args[cur_arg], cursection, + kws ? " Registered keywords :" : "", kws ? kws: ""); + free(kws); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + else if (strcmp(args[0], "log") == 0) { + if (!parse_logsrv(args, &cfg_log_forward->logsrvs, (kwm == KWM_NO), &errmsg)) { + ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } +out: + return err_code; +} + + /* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */ static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private) { @@ -3564,6 +3708,9 @@ static struct cli_kw_list cli_kws = {{ },{ INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); +/* config parsers for this section */ +REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL); + REGISTER_PER_THREAD_ALLOC(init_log_buffers); REGISTER_PER_THREAD_FREE(deinit_log_buffers); diff --git a/src/sink.c b/src/sink.c index eb9a657c0..4b5dd5b6e 100644 --- a/src/sink.c +++ b/src/sink.c @@ -1001,6 +1001,19 @@ int post_sink_resolve() } } } + + for (px = cfg_log_forward; px; px = px->next) { + list_for_each_entry_safe(logsrv, logb, &px->logsrvs, list) { + if (logsrv->type == LOG_TARGET_BUFFER) { + sink = sink_find(logsrv->ring_name); + if (!sink || sink->type != SINK_TYPE_BUFFER) { + ha_alert("log-forward '%s' log server uses unknown ring named '%s'.\n", px->id, logsrv->ring_name); + err_code |= ERR_ALERT | ERR_FATAL; + } + logsrv->sink = sink; + } + } + } return err_code; }