]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: config/trace: Add a 'traces' section to declare debug traces
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 1 Oct 2024 06:48:38 +0000 (08:48 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 2 Oct 2024 08:22:51 +0000 (10:22 +0200)
It is no longer supported to declare debug traces, via 'trace' directive, in
a global section. A 'traces' directive must be used instead. The syntax of
the 'trace' directive in these sections remains the same. But it is no
longer experimental.

The main reason for this change is to avoid to have a ring section defined
before a global one. Indeed, for now, forward declarations of ring sections
are not supported. So to configure traces, you had to add a ring section
before the global one defining the traces. Most of time, that meant to have
two global sections :

  global
    [...] # global settings

  ring <name>
    [...]

  global
    [...] # trace config

In addition, it will be possible to easily extend the traces section by
adding some new directives.

doc/configuration.txt
include/haproxy/cfgparse.h
src/cfgparse-global.c
src/cfgparse.c
src/trace.c

index 699796a9955534a9e746c2957161e75755617934..ee601480bf7a056e407d9a903c7ae7265f9d86aa 100644 (file)
@@ -52,6 +52,7 @@ Summary
 3.1.      Process management and security
 3.2.      Performance tuning
 3.3.      Debugging
+3.3.1.    Traces
 3.4.      Userlists
 3.5.      Peers
 3.6.      Mailers
@@ -2903,25 +2904,6 @@ thread-hard-limit <number>
   and a warning is emitted in so that the configuration anomaly can be
   fixed. By default there is no limit. See also "nbthread".
 
-trace <args...>
-  This command configures one "trace" subsystem statement. Each of them can be
-  found in the management manual, and follow the exact same syntax. Only one
-  statement per line is permitted (i.e. if some long trace configurations using
-  semi-colons are to be imported, they must be placed one per line). Any output
-  that the "trace" command would produce will be emitted during the parsing
-  step of the section. Most of the time these will be errors and warnings, but
-  certain incomplete commands might list permissible choices. This command is
-  not meant for regular use, it will generally only be suggested by developers
-  along complex debugging sessions. For this reason it is internally marked as
-  experimental, meaning that "expose-experimental-directives" must appear on a
-  line before any "trace" statement. Note that these directives are parsed on
-  the fly, so referencing a ring buffer that is only declared further will not
-  work. For such use cases it is suggested to place another "global" section
-  with only the "trace" statements after the declaration of that ring. It is
-  important to keep in mind that depending on the trace level and details,
-  enabling traces can severely degrade the global performance. Please refer to
-  the management manual for the statements syntax.
-
 uid <number>
   Changes the process's user ID to <number>. It is recommended that the user ID
   is dedicated to HAProxy or to a small set of similar daemons. HAProxy must
@@ -4350,6 +4332,60 @@ zero-warning
   report errors in such a case. This option is equivalent to command line
   argument "-dW".
 
+3.3.1. Traces
+-------------
+
+For debugging purpose, it is possible to activate traces on an HAProxy's
+subsystem. This will dump debug messages about a specific subsystem. It is a
+very powerful tool to diagnose issues. Traces can be dynamically configured via
+the CLI. It is also possible to predefined some settings in the configuration
+file, in dedicated "traces" sections. More details about traces can be found in
+the management guide. It remains a developper tools used during complex
+debugging sessions.  It is pretty verbose and have a cost, so use it with
+caution. And because it is a developper tool, there is no warranty about the
+backward compatibility of this section.
+
+traces
+  Starts a new traces section. One or multiple "traces" section may be
+  used. All direcitives are evaluated in the declararion order, the last ones
+  overriding previous ones.
+
+trace <args...>
+  Configures on "trace" subsystem. Each of them can be found in the management
+  manual, and follow the exact same syntax. Only one statement per line is
+  permitted (i.e. if some long trace configurations using semi-colons are to be
+  imported, they must be placed one per line). Any output that the "trace"
+  command would produce will be emitted during the parsing step of the
+  section. Most of the time these will be errors and warnings, but certain
+  incomplete commands might list permissible choices. This command is not meant
+  for regular use, it will generally only be suggested by developers along
+  complex debugging sessions. Note that these directives are parsed on the fly,
+  so referencing a ring buffer that is only declared further will not work. It
+  is important to keep in mind that depending on the trace level and details,
+  enabling traces can severely degrade the global performance. Please refer to
+  the management manual for the statements syntax.
+
+  Example:
+    ring buf1
+      size 10485760 # 10MB
+      format timed
+      backing-file /tmp/h1.traces
+
+    ring buf2
+      size 10485760 # 10MB
+      format timed
+      backing-file /tmp/h2.traces
+
+    traces
+      trace h1 sink buf1
+      trace h1 level developer
+      trace h1 verbosity complete
+      trace h1 start now
+
+      trace h2 sink buf1
+      trace h2 level developer
+      trace h2 verbosity complete
+      trace h2 start now
 
 3.4. Userlists
 --------------
index 04c4ed94ae81d7019dcf58905b7c05110fbaf207..8b760cdaef9acb3a067bc92dab131d797f1ec986 100644 (file)
@@ -37,6 +37,7 @@ struct acl_cond;
 #define CFG_PEERS      4
 #define CFG_CRTLIST    5
 #define CFG_CRTSTORE    6
+#define CFG_TRACES      7
 
 /* various keyword modifiers */
 enum kw_mod {
@@ -110,6 +111,7 @@ extern struct proxy *curproxy;
 
 int cfg_parse_global(const char *file, int linenum, char **args, int inv);
 int cfg_parse_listen(const char *file, int linenum, char **args, int inv);
+int cfg_parse_traces(const char *file, int linenum, char **args, int inv);
 int cfg_parse_track_sc_num(unsigned int *track_sc_num,
                            const char *arg, const char *end, char **err);
 int parse_cfg(const struct cfgfile *cfg);
index 4bff01b13f73a7bfe03629d93b392ea6aa376242..12cac06e3c0097938d06be89454fe44731673769 100644 (file)
@@ -897,7 +897,7 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
                                }
                        }
                }
-               
+
                best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_GLOBAL, common_kw_list);
                if (best)
                        ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
index bd7c709b5c4f9a8a8ffd7e2f2fae78c4f0fd732d..85b74f0d6b7f3db08f5add398dd5237b48aaf2c3 100644 (file)
@@ -4937,6 +4937,7 @@ REGISTER_CONFIG_SECTION("userlist",       cfg_parse_users,     NULL);
 REGISTER_CONFIG_SECTION("peers",          cfg_parse_peers,     NULL);
 REGISTER_CONFIG_SECTION("mailers",        cfg_parse_mailers,   NULL);
 REGISTER_CONFIG_SECTION("namespace_list", cfg_parse_netns,     NULL);
+REGISTER_CONFIG_SECTION("traces",         cfg_parse_traces,    NULL);
 
 static struct cfg_kw_list cfg_kws = {{ },{
        { CFG_GLOBAL, "default-path",     cfg_parse_global_def_path },
index 852397ac7fe6f1a16a6f5d279e114cac00a368d3..ea0d65e27af3ceba6b9b4e047564a8613e1f3f2e 100644 (file)
@@ -1025,9 +1025,74 @@ static int cfg_parse_trace(char **args, int section_type, struct proxy *curpx,
                }
                ha_free(&msg);
        }
+
        return 0;
 }
 
+/*
+ * parse a line in a <traces> section. Returns the error code, 0 if OK, or
+ * any combination of :
+ *  - ERR_ABORT: must abort ASAP
+ *  - ERR_FATAL: we can continue parsing but not start the service
+ *  - ERR_WARN: a warning has been emitted
+ *  - ERR_ALERT: an alert has been emitted
+ * Only the two first ones can stop processing, the two others are just
+ * indicators.
+ */
+int cfg_parse_traces(const char *file, int linenum, char **args, int inv)
+{
+       int err_code = 0;
+       char *errmsg = NULL;
+
+       if (strcmp(args[0], "traces") == 0) {  /* new section */
+               /* no option, nothing special to do */
+               alertif_too_many_args(0, file, linenum, args, &err_code);
+               goto out;
+       }
+       else {
+               struct cfg_kw_list *kwl;
+               const char *best;
+               int index;
+               int rc;
+
+               list_for_each_entry(kwl, &cfg_keywords.list, list) {
+                       for (index = 0; kwl->kw[index].kw != NULL; index++) {
+                               if (kwl->kw[index].section != CFG_TRACES)
+                                       continue;
+                               if (strcmp(kwl->kw[index].kw, args[0]) == 0) {
+                                       if (check_kw_experimental(&kwl->kw[index], file, linenum, &errmsg)) {
+                                               ha_alert("%s\n", errmsg);
+                                               err_code |= ERR_ALERT | ERR_FATAL;
+                                               goto out;
+                                       }
+
+                                       rc = kwl->kw[index].parse(args, CFG_TRACES, NULL, NULL, file, linenum, &errmsg);
+                                       if (rc < 0) {
+                                               ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
+                                               err_code |= ERR_ALERT | ERR_FATAL;
+                                       }
+                                       else if (rc > 0) {
+                                               ha_warning("parsing [%s:%d] : %s\n", file, linenum, errmsg);
+                                               err_code |= ERR_WARN;
+                                       }
+                                       goto out;
+                               }
+                       }
+               }
+
+               best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_TRACES, NULL);
+               if (best)
+                       ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
+               else
+                       ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global");
+               err_code |= ERR_ALERT | ERR_FATAL;
+       }
+
+  out:
+       free(errmsg);
+       return err_code;
+}
+
 /* parse the command, returns 1 if a message is returned, otherwise zero */
 static int cli_parse_trace(char **args, char *payload, struct appctx *appctx, void *private)
 {
@@ -1108,7 +1173,7 @@ static struct cli_kw_list cli_kws = {{ },{
 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
 
 static struct cfg_kw_list cfg_kws = {ILH, {
-       { CFG_GLOBAL, "trace", cfg_parse_trace, KWF_EXPERIMENTAL },
+       { CFG_TRACES, "trace", cfg_parse_trace },
        { /* END */ },
 }};