MINOR: config/trace: Add a 'traces' section to declare debug traces

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.
This commit is contained in:
Christopher Faulet 2024-10-01 08:48:38 +02:00
parent 53f52e67a0
commit 15a520d474
5 changed files with 125 additions and 21 deletions

View 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
--------------

View 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);

View 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);

View 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 },

View 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 */ },
}};