MEDIUM: log: adds log forwarding section.

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 <name>
  Creates a new log forwarder proxy identified as <name>.

bind <addr> [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 <address> [len <length>] [format <format>] [sample <ranges>:<smp_size>]
    <facility> [<level> [<minlevel>]]
  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
This commit is contained in:
Emeric Brun 2020-07-07 14:19:42 +02:00 committed by Willy Tarreau
parent 54932b4408
commit 12941c82d0
4 changed files with 220 additions and 6 deletions

View File

@ -2738,6 +2738,57 @@ timeout server <timeout>
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 <name>
Creates a new log forwarder proxy identified as <name>.
bind <addr> [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 <address> [len <length>] [format <format>] [sample <ranges>:<smp_size>]
<facility> [<level> [<minlevel>]]
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 /<path> [, ...] [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

View File

@ -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;

147
src/log.c
View File

@ -26,12 +26,15 @@
#include <haproxy/api.h>
#include <haproxy/applet-t.h>
#include <haproxy/cfgparse.h>
#include <haproxy/cli.h>
#include <haproxy/fd.h>
#include <haproxy/frontend.h>
#include <haproxy/global.h>
#include <haproxy/http.h>
#include <haproxy/listener.h>
#include <haproxy/log.h>
#include <haproxy/proxy.h>
#include <haproxy/ring.h>
#include <haproxy/sample.h>
#include <haproxy/sink.h>
@ -43,6 +46,9 @@
#include <haproxy/version.h>
/* 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);

View File

@ -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;
}