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 699796a..ee60148 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 04c4ed9..8b760cd 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 4bff01b..12cac06 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 bd7c709..85b74f0 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 852397a..ea0d65e 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 */ },
 }};