]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: log: adds log forwarding section.
authorEmeric Brun <ebrun@haproxy.com>
Tue, 7 Jul 2020 12:19:42 +0000 (14:19 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 15 Jul 2020 15:50:12 +0000 (17:50 +0200)
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

doc/configuration.txt
include/haproxy/log.h
src/log.c
src/sink.c

index 0b3d564a83a0875fb65998720ad09cf01e1a4bb5..2a4672b05715d277af63d699e78c22590bc06291 100644 (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
index 683c135a0630978629e4552db969a6c2548f8c8b..d8b95a82bdaed907b2c53d69b17738a6c0bf12e3 100644 (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;
 
index b6d5c45fdd4267138c3866d87ae86b68c99e235a..f8ac522010f904cde494c95a600510b5ee08ec90 100644 (file)
--- a/src/log.c
+++ b/src/log.c
 
 #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);
 
index eb9a657c00d39273fe65f931ee6ff55cf5bb74da..4b5dd5b6e0b75703c02ecb7c1c7644d93f8ba9f1 100644 (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;
 }