mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-21 21:26:58 +00:00
MINOR: log: add log-profile parsing logic
This patch implements prerequisite log-profile struct and parser logic. It has no effect during runtime for now. Logformat expressions provided in log-profile "steps" are postchecked during postparsing for each proxy "log" directive that makes use of a given profile. (this allows to ensure that the logformat expressions used in the profile are compatible with proxy using them)
This commit is contained in:
parent
33f3bec7ee
commit
15e9c7da6b
@ -233,6 +233,7 @@ struct log_target {
|
||||
|
||||
enum logger_flags {
|
||||
LOGGER_FL_NONE = 0x00,
|
||||
LOGGER_FL_RESOLVED = 0x01,
|
||||
};
|
||||
|
||||
struct logger {
|
||||
@ -247,6 +248,10 @@ struct logger {
|
||||
int minlvl;
|
||||
int maxlen;
|
||||
struct logger *ref;
|
||||
union {
|
||||
struct log_profile *prof; /* postparsing */
|
||||
char *prof_str; /* preparsing */
|
||||
};
|
||||
struct {
|
||||
char *file; /* file where the logger appears */
|
||||
int line; /* line where the logger appears */
|
||||
@ -267,6 +272,28 @@ enum log_orig {
|
||||
LOG_ORIG_TXN_CLOSE, /* during stream termination */
|
||||
};
|
||||
|
||||
struct log_profile_step {
|
||||
struct lf_expr logformat;
|
||||
struct lf_expr logformat_sd;
|
||||
};
|
||||
|
||||
struct log_profile {
|
||||
struct list list;
|
||||
struct {
|
||||
char *file;
|
||||
int line;
|
||||
} conf;
|
||||
char *id;
|
||||
struct buffer log_tag; // override log-tag
|
||||
struct log_profile_step *accept;
|
||||
struct log_profile_step *request;
|
||||
struct log_profile_step *connect;
|
||||
struct log_profile_step *response;
|
||||
struct log_profile_step *close;
|
||||
struct log_profile_step *error; // override error-log-format
|
||||
struct log_profile_step *any; // override log-format
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_LOG_T_H */
|
||||
|
||||
/*
|
||||
|
@ -134,6 +134,7 @@ int postresolve_logger_list(struct proxy *px, struct list *loggers, const char *
|
||||
struct logger *dup_logger(struct logger *def);
|
||||
void free_logger(struct logger *logger);
|
||||
void deinit_log_target(struct log_target *target);
|
||||
struct log_profile *log_profile_find_by_name(const char *name);
|
||||
|
||||
/* Parse "log" keyword and update the linked list. */
|
||||
int parse_logger(char **args, struct list *loggers, int do_del, const char *file, int linenum, char **err);
|
||||
|
@ -46,12 +46,12 @@ static const char *common_kw_list[] = {
|
||||
"ssl-server-verify", "maxconnrate", "maxsessrate", "maxsslrate",
|
||||
"maxcomprate", "maxpipes", "maxzlibmem", "maxcompcpuusage", "ulimit-n",
|
||||
"chroot", "description", "node", "pidfile", "unix-bind", "log",
|
||||
"log-send-hostname", "server-state-base", "server-state-file",
|
||||
"log-tag", "spread-checks", "max-spread-checks", "cpu-map", "setenv",
|
||||
"presetenv", "unsetenv", "resetenv", "strict-limits", "localpeer",
|
||||
"numa-cpu-mapping", "defaults", "listen", "frontend", "backend",
|
||||
"peers", "resolvers", "cluster-secret", "no-quic", "limited-quic",
|
||||
"stats-file",
|
||||
"log-profile", "log-send-hostname", "log-tag", "server-state-base",
|
||||
"server-state-file", "spread-checks", "max-spread-checks", "cpu-map",
|
||||
"setenv", "presetenv", "unsetenv", "resetenv", "strict-limits",
|
||||
"localpeer", "numa-cpu-mapping", "defaults", "listen", "frontend",
|
||||
"backend", "peers", "resolvers", "cluster-secret", "no-quic",
|
||||
"limited-quic", "stats-file",
|
||||
NULL /* must be last */
|
||||
};
|
||||
|
||||
|
332
src/log.c
332
src/log.c
@ -361,6 +361,8 @@ struct logformat_node_args node_args_list[] = {
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static struct list log_profile_list = LIST_HEAD_INIT(log_profile_list);
|
||||
|
||||
/*
|
||||
* callback used to configure addr source retrieval
|
||||
*/
|
||||
@ -1403,6 +1405,9 @@ static int postcheck_log_backend(struct proxy *be)
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* forward declaration */
|
||||
static int log_profile_postcheck(struct proxy *px, struct log_profile *prof, char **err);
|
||||
|
||||
/* resolves a single logger entry (it is expected to be called
|
||||
* at postparsing stage)
|
||||
*
|
||||
@ -1419,6 +1424,7 @@ static int resolve_logger(struct proxy *px, struct logger *logger, char **msg)
|
||||
struct log_target *target = &logger->target;
|
||||
int err_code = ERR_NONE;
|
||||
|
||||
/* resolve logger target */
|
||||
if (target->type == LOG_TARGET_BUFFER)
|
||||
err_code = sink_resolve_logger_buffer(logger, msg);
|
||||
else if (target->type == LOG_TARGET_BACKEND) {
|
||||
@ -1440,17 +1446,46 @@ static int resolve_logger(struct proxy *px, struct logger *logger, char **msg)
|
||||
|
||||
target->flags |= LOG_TARGET_FL_RESOLVED;
|
||||
|
||||
if (err_code & ERR_CODE)
|
||||
goto end;
|
||||
|
||||
/* postcheck logger profile */
|
||||
if (logger->prof_str) {
|
||||
struct log_profile *prof;
|
||||
|
||||
prof = log_profile_find_by_name(logger->prof_str);
|
||||
if (!prof) {
|
||||
memprintf(msg, "unknown log-profile '%s'", logger->prof_str);
|
||||
ha_free(&logger->prof_str);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
ha_free(&logger->prof_str);
|
||||
logger->prof = prof;
|
||||
|
||||
if (!log_profile_postcheck(px, logger->prof, msg)) {
|
||||
memprintf(msg, "uses incompatible log-profile '%s': %s", logger->prof->id, *msg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
logger->flags |= LOGGER_FL_RESOLVED;
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* tries to duplicate <def> logger
|
||||
* (only possible before the logger is resolved)
|
||||
*
|
||||
* Returns the newly allocated and duplicated logger or NULL
|
||||
* in case of error.
|
||||
*/
|
||||
struct logger *dup_logger(struct logger *def)
|
||||
{
|
||||
struct logger *cpy = malloc(sizeof(*cpy));
|
||||
struct logger *cpy;
|
||||
|
||||
BUG_ON(def->flags & LOGGER_FL_RESOLVED);
|
||||
cpy = malloc(sizeof(*cpy));
|
||||
|
||||
/* copy everything that can be easily copied */
|
||||
memcpy(cpy, def, sizeof(*cpy));
|
||||
@ -1475,6 +1510,11 @@ struct logger *dup_logger(struct logger *def)
|
||||
memcpy(cpy->lb.smp_rgs, def->lb.smp_rgs,
|
||||
sizeof(*cpy->lb.smp_rgs) * def->lb.smp_rgs_sz);
|
||||
}
|
||||
if (def->prof_str) {
|
||||
cpy->prof_str = strdup(def->prof_str);
|
||||
if (!cpy->prof_str)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* inherit from original reference if set */
|
||||
cpy->ref = (def->ref) ? def->ref : def;
|
||||
@ -1499,6 +1539,8 @@ void free_logger(struct logger *logger)
|
||||
ha_free(&logger->conf.file);
|
||||
deinit_log_target(&logger->target);
|
||||
free(logger->lb.smp_rgs);
|
||||
if (!(logger->flags & LOGGER_FL_RESOLVED))
|
||||
ha_free(&logger->prof_str);
|
||||
free(logger);
|
||||
}
|
||||
|
||||
@ -1764,6 +1806,18 @@ int parse_logger(char **args, struct list *loggers, int do_del, const char *file
|
||||
cur_arg += 2;
|
||||
}
|
||||
|
||||
if (strcmp(args[cur_arg], "profile") == 0) {
|
||||
char *prof_str;
|
||||
|
||||
prof_str = args[cur_arg+1];
|
||||
if (!prof_str) {
|
||||
memprintf(err, "expected log-profile name");
|
||||
goto error;
|
||||
}
|
||||
logger->prof_str = strdup(prof_str);
|
||||
cur_arg += 2;
|
||||
}
|
||||
|
||||
/* parse the facility */
|
||||
logger->facility = get_log_facility(args[cur_arg]);
|
||||
if (logger->facility < 0) {
|
||||
@ -5884,6 +5938,280 @@ out:
|
||||
return err_code;
|
||||
}
|
||||
|
||||
static inline void log_profile_step_init(struct log_profile_step *lprof_step)
|
||||
{
|
||||
lf_expr_init(&lprof_step->logformat);
|
||||
lf_expr_init(&lprof_step->logformat_sd);
|
||||
}
|
||||
|
||||
static inline void log_profile_step_free(struct log_profile_step *lprof_step)
|
||||
{
|
||||
if (!lprof_step)
|
||||
return;
|
||||
|
||||
lf_expr_deinit(&lprof_step->logformat);
|
||||
lf_expr_deinit(&lprof_step->logformat_sd);
|
||||
free(lprof_step);
|
||||
}
|
||||
|
||||
/* postcheck a single log profile step for a given <px> (it is expected to be
|
||||
* called at postparsing stage)
|
||||
*
|
||||
* Returns 1 on success and 0 on error, <msg> will be set on error.
|
||||
*/
|
||||
static inline int log_profile_step_postcheck(struct proxy *px, const char *step_name,
|
||||
struct log_profile_step *step,
|
||||
char **err)
|
||||
{
|
||||
if (!step)
|
||||
return 1; // nothing to do
|
||||
|
||||
if (!lf_expr_isempty(&step->logformat) &&
|
||||
!lf_expr_postcheck(&step->logformat, px, err)) {
|
||||
memprintf(err, "'on %s format' in file '%s' at line %d: %s",
|
||||
step_name,
|
||||
step->logformat_sd.conf.file,
|
||||
step->logformat_sd.conf.line,
|
||||
*err);
|
||||
return 0;
|
||||
}
|
||||
if (!lf_expr_isempty(&step->logformat_sd) &&
|
||||
!lf_expr_postcheck(&step->logformat_sd, px, err)) {
|
||||
memprintf(err, "'on %s sd' in file '%s' at line %d: %s",
|
||||
step_name,
|
||||
step->logformat_sd.conf.file,
|
||||
step->logformat_sd.conf.line,
|
||||
*err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* postcheck a log profile struct for a given <px> (it is expected to be called
|
||||
* at postparsing stage)
|
||||
*
|
||||
* Returns 1 on success and 0 on error, <msg> will be set on error.
|
||||
*/
|
||||
static int log_profile_postcheck(struct proxy *px, struct log_profile *prof, char **err)
|
||||
{
|
||||
/* log profile steps are only relevant under proxy
|
||||
* context
|
||||
*/
|
||||
if (!px)
|
||||
return 1; /* nothing to do */
|
||||
|
||||
/* postcheck lf_expr for log profile steps */
|
||||
if (!log_profile_step_postcheck(px, "accept", prof->accept, err) ||
|
||||
!log_profile_step_postcheck(px, "request", prof->request, err) ||
|
||||
!log_profile_step_postcheck(px, "connect", prof->connect, err) ||
|
||||
!log_profile_step_postcheck(px, "response", prof->response, err) ||
|
||||
!log_profile_step_postcheck(px, "close", prof->close, err) ||
|
||||
!log_profile_step_postcheck(px, "error", prof->error, err) ||
|
||||
!log_profile_step_postcheck(px, "any", prof->any, err))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void log_profile_free(struct log_profile *prof)
|
||||
{
|
||||
ha_free(&prof->id);
|
||||
ha_free(&prof->conf.file);
|
||||
chunk_destroy(&prof->log_tag);
|
||||
|
||||
log_profile_step_free(prof->accept);
|
||||
log_profile_step_free(prof->request);
|
||||
log_profile_step_free(prof->connect);
|
||||
log_profile_step_free(prof->response);
|
||||
log_profile_step_free(prof->close);
|
||||
log_profile_step_free(prof->error);
|
||||
log_profile_step_free(prof->any);
|
||||
|
||||
ha_free(&prof);
|
||||
}
|
||||
|
||||
/* Deinitialize all known log profiles */
|
||||
static void deinit_log_profiles()
|
||||
{
|
||||
struct log_profile *prof, *back;
|
||||
|
||||
list_for_each_entry_safe(prof, back, &log_profile_list, list) {
|
||||
LIST_DEL_INIT(&prof->list);
|
||||
log_profile_free(prof);
|
||||
}
|
||||
}
|
||||
|
||||
struct log_profile *log_profile_find_by_name(const char *name)
|
||||
{
|
||||
struct log_profile *current;
|
||||
|
||||
list_for_each_entry(current, &log_profile_list, list) {
|
||||
if (strcmp(current->id, name) == 0)
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse "log-profile" section and register the corresponding profile
|
||||
* with its name
|
||||
*
|
||||
* The function returns 0 in success case, otherwise, it returns error
|
||||
* flags.
|
||||
*/
|
||||
int cfg_parse_log_profile(const char *file, int linenum, char **args, int kwm)
|
||||
{
|
||||
int err_code = ERR_NONE;
|
||||
static struct log_profile *prof = NULL;
|
||||
char *errmsg = NULL;
|
||||
const char *err = NULL;
|
||||
|
||||
if (strcmp(args[0], "log-profile") == 0) {
|
||||
if (!*args[1]) {
|
||||
ha_alert("parsing [%s:%d] : missing name for log-profile 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;
|
||||
}
|
||||
|
||||
prof = log_profile_find_by_name(args[1]);
|
||||
if (prof) {
|
||||
ha_alert("Parsing [%s:%d]: log-profile section '%s' has the same name as another log-profile section declared at %s:%d.\n",
|
||||
file, linenum, args[1], prof->conf.file, prof->conf.line);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
prof = calloc(1, sizeof(*prof));
|
||||
if (prof == NULL || !(prof->id = strdup(args[1]))) {
|
||||
ha_alert("Parsing [%s:%d]: cannot allocate memory for log-profile section '%s'.\n",
|
||||
file, linenum, args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
prof->conf.file = strdup(file);
|
||||
prof->conf.line = linenum;
|
||||
|
||||
/* add to list */
|
||||
LIST_APPEND(&log_profile_list, &prof->list);
|
||||
}
|
||||
else if (strcmp(args[0], "log-tag") == 0) { /* override log-tag */
|
||||
if (*(args[1]) == 0) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a tag for use in syslog.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
chunk_destroy(&prof->log_tag);
|
||||
chunk_initlen(&prof->log_tag, strdup(args[1]), strlen(args[1]), strlen(args[1]));
|
||||
if (b_orig(&prof->log_tag) == NULL) {
|
||||
chunk_destroy(&prof->log_tag);
|
||||
ha_alert("parsing [%s:%d]: cannot allocate memory for '%s'.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "on") == 0) { /* log profile step */
|
||||
struct log_profile_step **target_step;
|
||||
struct lf_expr *target_lf;
|
||||
int cur_arg;
|
||||
|
||||
/* get targeted log-profile step */
|
||||
if (strcmp(args[1], "accept") == 0)
|
||||
target_step = &prof->accept;
|
||||
else if (strcmp(args[1], "request") == 0)
|
||||
target_step = &prof->request;
|
||||
else if (strcmp(args[1], "connect") == 0)
|
||||
target_step = &prof->connect;
|
||||
else if (strcmp(args[1], "response") == 0)
|
||||
target_step = &prof->response;
|
||||
else if (strcmp(args[1], "close") == 0)
|
||||
target_step = &prof->close;
|
||||
else if (strcmp(args[1], "error") == 0)
|
||||
target_step = &prof->error;
|
||||
else if (strcmp(args[1], "any") == 0)
|
||||
target_step = &prof->any;
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a log step.\n"
|
||||
"expected values are: 'accept', 'request', 'connect', 'response', 'close', 'error' or 'any'\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*target_step == NULL) {
|
||||
/* first time */
|
||||
*target_step = malloc(sizeof(**target_step));
|
||||
if (*target_step == NULL) {
|
||||
ha_alert("parsing [%s:%d]: cannot allocate memory for '%s %s'.\n", file, linenum, args[0], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
log_profile_step_init(*target_step);
|
||||
}
|
||||
|
||||
cur_arg = 2;
|
||||
|
||||
while (*(args[cur_arg]) != 0) {
|
||||
/* regular format or SD (structured-data) one? */
|
||||
if (strcmp(args[cur_arg], "format") == 0)
|
||||
target_lf = &(*target_step)->logformat;
|
||||
else if (strcmp(args[cur_arg], "sd") == 0)
|
||||
target_lf = &(*target_step)->logformat_sd;
|
||||
else
|
||||
break;
|
||||
|
||||
/* parse and assign logformat expression */
|
||||
lf_expr_deinit(target_lf); /* if already configured */
|
||||
|
||||
if (*(args[cur_arg + 1]) == 0) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects a logformat string.\n",
|
||||
file, linenum, args[0], args[1], args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
target_lf->str = strdup(args[cur_arg + 1]);
|
||||
target_lf->conf.file = strdup(file);
|
||||
target_lf->conf.line = linenum;
|
||||
|
||||
if (!lf_expr_compile(target_lf, NULL,
|
||||
LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
|
||||
SMP_VAL_FE_LOG_END, &errmsg)) {
|
||||
ha_alert("Parsing [%s:%d]: failed to parse logformat: %s.\n",
|
||||
file, linenum, errmsg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cur_arg += 2;
|
||||
}
|
||||
if (cur_arg == 2 || *(args[cur_arg]) != 0) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s' expects 'format' and/or 'sd'.\n",
|
||||
file, linenum, args[0], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : unknown keyword '%s' in log-profile section.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_ABORT;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
ha_free(&errmsg);
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* function: post-resolve a single list of loggers
|
||||
*
|
||||
* Returns err_code which defaults to ERR_NONE and can be set to a combination
|
||||
@ -5945,6 +6273,7 @@ static int postresolve_loggers()
|
||||
|
||||
/* config parsers for this section */
|
||||
REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL);
|
||||
REGISTER_CONFIG_SECTION("log-profile", cfg_parse_log_profile, NULL);
|
||||
REGISTER_POST_CHECK(postresolve_loggers);
|
||||
REGISTER_POST_PROXY_CHECK(postcheck_log_backend);
|
||||
REGISTER_POST_PROXY_CHECK(postcheck_logformat_proxy);
|
||||
@ -5953,6 +6282,7 @@ REGISTER_PER_THREAD_ALLOC(init_log_buffers);
|
||||
REGISTER_PER_THREAD_FREE(deinit_log_buffers);
|
||||
|
||||
REGISTER_POST_DEINIT(deinit_log_forward);
|
||||
REGISTER_POST_DEINIT(deinit_log_profiles);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
|
Loading…
Reference in New Issue
Block a user