Skip to content

Commit 2b79457

Browse files
committed
MEDIUM: log: add compiling logic to logformat expressions
split parse_logformat_string() into two functions: parse_logformat_string() sticks to the same behavior, but now becomes an helper for lf_expr_compile() which uses explicit arguments so that it becomes possible to use lf_expr_compile() without a proxy, but also compile an expression which was previously prepared for compiling (set string and config hints within the logformat expression to avoid manually storing string and config context if the compiling step happens later). lf_expr_dup() may be used to duplicate an expression before it is compiled, lf_expr_xfer() now makes sure that the input logformat is already compiled. This is some prerequisite works for log-profiles implementation, no functional change should be expected.
1 parent 7a21c3a commit 2b79457

3 files changed

Lines changed: 139 additions & 33 deletions

File tree

include/haproxy/log-t.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,23 @@ struct logformat_node {
168168
const struct logformat_tag *tag; // set if ->type == LOG_FMT_TAG
169169
};
170170

171+
enum lf_expr_flags {
172+
LF_FL_NONE = 0x00,
173+
LF_FL_COMPILED = 0x01
174+
};
175+
171176
/* a full logformat expr made of one or multiple logformat nodes */
172177
struct lf_expr {
173-
struct list list; /* to store lf_expr inside a list */
174-
struct list nodes; /* logformat_node list */
178+
struct list list; /* to store lf_expr inside a list */
179+
union {
180+
struct list nodes; /* logformat_node list */
181+
char *str; /* original string prior to parsing (NULL once compiled) */
182+
};
175183
struct {
176-
char *file; /* file where the lft appears */
177-
int line; /* line where the lft appears */
184+
char *file; /* file where the lft appears */
185+
int line; /* line where the lft appears */
178186
} conf; // parsing hints
187+
uint8_t flags; /* LF_FL_* flags */
179188
};
180189

181190
/* Range of indexes for log sampling. */

include/haproxy/log.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,14 @@ int init_log_buffers(void);
6565
void deinit_log_buffers(void);
6666

6767
void lf_expr_init(struct lf_expr *expr);
68+
int lf_expr_dup(const struct lf_expr *orig, struct lf_expr *dest);
6869
void lf_expr_xfer(struct lf_expr *src, struct lf_expr *dst);
6970
void lf_expr_deinit(struct lf_expr *expr);
7071
static inline int lf_expr_isempty(const struct lf_expr *expr)
7172
{
72-
return LIST_ISEMPTY(&expr->nodes);
73+
return !(expr->flags & LF_FL_COMPILED) || LIST_ISEMPTY(&expr->nodes);
7374
}
75+
int lf_expr_compile(struct lf_expr *lf_expr, struct arg_list *al, int options, int cap, char **err);
7476
int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err);
7577

7678
/* Deinitialize log buffers used for syslog messages */

src/log.c

Lines changed: 123 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,9 @@ int parse_logformat_tag_args(char *args, struct logformat_node *node, char **err
372372
* ignored when arg_len is 0. Neither <tag> nor <tag_len> may be null.
373373
* Returns false in error case and err is filled, otherwise returns true.
374374
*/
375-
int parse_logformat_tag(char *arg, int arg_len, char *name, int name_len, int typecast, char *tag, int tag_len, struct proxy *curproxy, struct lf_expr *lf_expr, int *defoptions, char **err)
375+
static int parse_logformat_tag(char *arg, int arg_len, char *name, int name_len, int typecast,
376+
char *tag, int tag_len, struct lf_expr *lf_expr,
377+
int *defoptions, char **err)
376378
{
377379
int j;
378380
struct list *list_format= &lf_expr->nodes;
@@ -465,7 +467,9 @@ int add_to_logformat_list(char *start, char *end, int type, struct lf_expr *lf_e
465467
*
466468
* In error case, the function returns 0, otherwise it returns 1.
467469
*/
468-
int add_sample_to_logformat_list(char *text, char *name, int name_len, int typecast, char *arg, int arg_len, struct proxy *curpx, struct lf_expr *lf_expr, int options, int cap, char **err, char **endptr)
470+
static int add_sample_to_logformat_list(char *text, char *name, int name_len, int typecast,
471+
char *arg, int arg_len, struct lf_expr *lf_expr,
472+
struct arg_list *al, int options, int cap, char **err, char **endptr)
469473
{
470474
char *cmd[2];
471475
struct list *list_format = &lf_expr->nodes;
@@ -477,8 +481,8 @@ int add_sample_to_logformat_list(char *text, char *name, int name_len, int typec
477481
cmd[1] = "";
478482
cmd_arg = 0;
479483

480-
expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err,
481-
&curpx->conf.args, endptr);
484+
expr = sample_parse_expr(cmd, &cmd_arg, lf_expr->conf.file, lf_expr->conf.line, err,
485+
al, endptr);
482486
if (!expr) {
483487
memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
484488
goto error_free;
@@ -515,7 +519,7 @@ int add_sample_to_logformat_list(char *text, char *name, int name_len, int typec
515519

516520
if ((options & LOG_OPT_HTTP) && (expr->fetch->use & (SMP_USE_L6REQ|SMP_USE_L6RES))) {
517521
ha_warning("parsing [%s:%d] : L6 sample fetch <%s> ignored in HTTP log-format string.\n",
518-
curpx->conf.args.file, curpx->conf.args.line, text);
522+
lf_expr->conf.file, lf_expr->conf.line, text);
519523
}
520524

521525
LIST_APPEND(list_format, &node->list);
@@ -527,24 +531,25 @@ int add_sample_to_logformat_list(char *text, char *name, int name_len, int typec
527531
}
528532

529533
/*
530-
* Parse the log_format string and fill a logformat expression. The logformat
531-
* expression will be scheduled for postcheck on the proxy unless the proxy was
532-
* already checked, in which case all checks will be performed right away.
534+
* Compile logformat expression (from string to list of logformat nodes)
533535
*
534536
* Tag name are preceded by % and composed by characters [a-zA-Z0-9]* : %tagname
535537
* You can set arguments using { } : %{many arguments}tagname.
536-
* The curproxy->conf.args.ctx must be set by the caller.
537538
*
538-
* fmt: the string to parse
539-
* curproxy: the proxy affected
540539
* lf_expr: the destination logformat expression (logformat_node list)
540+
* which is supposed to be configured (str and conf set) but
541+
* shouldn't be compiled (shoudn't contain any nodes)
542+
* al: arg list where sample expr should store arg dependency (if the logformat
543+
* expression involves sample expressions), may be NULL
541544
* options: LOG_OPT_* to force on every node
542545
* cap: all SMP_VAL_* flags supported by the consumer
543546
*
544547
* The function returns 1 in success case, otherwise, it returns 0 and err is filled.
545548
*/
546-
int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_expr *lf_expr, int options, int cap, char **err)
549+
int lf_expr_compile(struct lf_expr *lf_expr,
550+
struct arg_list *al, int options, int cap, char **err)
547551
{
552+
char *fmt = lf_expr->str; /* will be freed */
548553
char *sp, *str, *backfmt; /* start pointer for text parts */
549554
char *arg = NULL; /* start pointer for args */
550555
char *tag = NULL; /* start pointer for tags */
@@ -557,22 +562,25 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_ex
557562
int cformat; /* current token format */
558563
int pformat; /* previous token format */
559564

565+
BUG_ON((lf_expr->flags & LF_FL_COMPILED));
566+
567+
if (!fmt)
568+
return 1; // nothing to do
569+
560570
sp = str = backfmt = strdup(fmt);
561571
if (!str) {
562572
memprintf(err, "out of memory error");
563573
return 0;
564574
}
565575

566-
/* reset the old expr first (if previously defined) */
567-
lf_expr_deinit(lf_expr);
568-
569-
/* Save some parsing infos to raise relevant error messages during
570-
* postparsing if needed.
576+
/* Prepare lf_expr nodes, past this lf_expr doesn't know about ->str
577+
* anymore as ->str and ->nodes are part of the same union. ->str has
578+
* been saved as local 'fmt' string pointer, so we must free it before
579+
* returning.
571580
*/
572-
if (curproxy->conf.args.file) {
573-
lf_expr->conf.file = strdup(curproxy->conf.args.file);
574-
lf_expr->conf.line = curproxy->conf.args.line;
575-
}
581+
LIST_INIT(&lf_expr->nodes);
582+
/* we must set the compiled flag now for proper deinit in case of failure */
583+
lf_expr->flags |= LF_FL_COMPILED;
576584

577585
for (cformat = LF_INIT; cformat != LF_END; str++) {
578586
pformat = cformat;
@@ -687,7 +695,7 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_ex
687695
* part of the expression, which MUST be the trailing
688696
* angle bracket.
689697
*/
690-
if (!add_sample_to_logformat_list(tag, name, name_len, typecast, arg, arg_len, curproxy, lf_expr, options, cap, err, &str))
698+
if (!add_sample_to_logformat_list(tag, name, name_len, typecast, arg, arg_len, lf_expr, al, options, cap, err, &str))
691699
goto fail;
692700

693701
if (*str == ']') {
@@ -733,7 +741,7 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_ex
733741
if (cformat != pformat || pformat == LF_SEPARATOR) {
734742
switch (pformat) {
735743
case LF_TAG:
736-
if (!parse_logformat_tag(arg, arg_len, name, name_len, typecast, tag, tag_len, curproxy, lf_expr, &options, err))
744+
if (!parse_logformat_tag(arg, arg_len, name, name_len, typecast, tag, tag_len, lf_expr, &options, err))
737745
goto fail;
738746
break;
739747
case LF_TEXT:
@@ -750,8 +758,57 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_ex
750758
memprintf(err, "truncated line after '%s'", tag ? tag : arg ? arg : "%");
751759
goto fail;
752760
}
761+
ha_free(&fmt);
753762
ha_free(&backfmt);
754763

764+
return 1;
765+
fail:
766+
ha_free(&fmt);
767+
ha_free(&backfmt);
768+
return 0;
769+
}
770+
771+
/* lf_expr_compile() helper: uses <curproxy> to deduce settings and
772+
* simplify function usage, mostly for legacy purpose
773+
*
774+
* curproxy->conf.args.ctx must be set by the caller.
775+
*
776+
* The logformat expression will be scheduled for postcheck on the proxy unless
777+
* the proxy was already checked, in which case all checks will be performed right
778+
* away.
779+
*
780+
* Returns 1 on success and 0 on failure. On failure: <lf_expr> will be cleaned
781+
* up and <err> will be set.
782+
*/
783+
int parse_logformat_string(const char *fmt, struct proxy *curproxy,
784+
struct lf_expr *lf_expr,
785+
int options, int cap, char **err)
786+
{
787+
int ret;
788+
789+
790+
/* reinit lf_expr (if previously set) */
791+
lf_expr_deinit(lf_expr);
792+
793+
lf_expr->str = strdup(fmt);
794+
if (!lf_expr->str) {
795+
memprintf(err, "out of memory error");
796+
goto fail;
797+
}
798+
799+
/* Save some parsing infos to raise relevant error messages during
800+
* postparsing if needed
801+
*/
802+
if (curproxy->conf.args.file) {
803+
lf_expr->conf.file = strdup(curproxy->conf.args.file);
804+
lf_expr->conf.line = curproxy->conf.args.line;
805+
}
806+
807+
ret = lf_expr_compile(lf_expr, &curproxy->conf.args, options, cap, err);
808+
809+
if (!ret)
810+
goto fail;
811+
755812
if (!(curproxy->flags & PR_FL_CHECKED)) {
756813
/* add the lf_expr to the proxy checks to delay postparsing
757814
* since config-related proxy properties are not stable yet
@@ -765,10 +822,10 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_ex
765822
if (!lf_expr_postcheck(lf_expr, curproxy, err))
766823
goto fail;
767824
}
768-
769825
return 1;
826+
770827
fail:
771-
ha_free(&backfmt);
828+
lf_expr_deinit(lf_expr);
772829
return 0;
773830
}
774831

@@ -848,6 +905,7 @@ static int postcheck_logformat_proxy(struct proxy *px)
848905
int err_code = ERR_NONE;
849906

850907
list_for_each_entry_safe(lf_expr, back_lf, &px->conf.lf_checks, list) {
908+
BUG_ON(!(lf_expr->flags & LF_FL_COMPILED));
851909
if (!lf_expr_postcheck(lf_expr, px, &err))
852910
err_code |= ERR_FATAL | ERR_ALERT;
853911
/* check performed, ensure it doesn't get checked twice */
@@ -2697,24 +2755,28 @@ void free_logformat_list(struct list *fmt)
26972755
/* Prepares log-format expression struct */
26982756
void lf_expr_init(struct lf_expr *expr)
26992757
{
2700-
LIST_INIT(&expr->nodes);
27012758
LIST_INIT(&expr->list);
2759+
expr->flags = LF_FL_NONE;
2760+
expr->str = NULL;
27022761
expr->conf.file = NULL;
27032762
expr->conf.line = 0;
27042763
}
27052764

27062765
/* Releases and resets a log-format expression */
27072766
void lf_expr_deinit(struct lf_expr *expr)
27082767
{
2709-
free_logformat_list(&expr->nodes);
2768+
if ((expr->flags & LF_FL_COMPILED))
2769+
free_logformat_list(&expr->nodes);
2770+
else
2771+
free(expr->str);
27102772
free(expr->conf.file);
27112773
/* remove from parent list (if any) */
27122774
LIST_DEL_INIT(&expr->list);
27132775

27142776
lf_expr_init(expr);
27152777
}
27162778

2717-
/* Transfer log-format expression from <src> to <dst>
2779+
/* Transfer a compiled log-format expression from <src> to <dst>
27182780
* at the end of the operation, <src> is reset
27192781
*/
27202782
void lf_expr_xfer(struct lf_expr *src, struct lf_expr *dst)
@@ -2724,9 +2786,15 @@ void lf_expr_xfer(struct lf_expr *src, struct lf_expr *dst)
27242786
/* first, reset any existing expr */
27252787
lf_expr_deinit(dst);
27262788

2789+
BUG_ON(!(src->flags & LF_FL_COMPILED));
2790+
27272791
/* then proceed with transfer between <src> and <dst> */
27282792
dst->conf.file = src->conf.file;
27292793
dst->conf.line = src->conf.line;
2794+
2795+
dst->flags |= LF_FL_COMPILED;
2796+
LIST_INIT(&dst->nodes);
2797+
27302798
list_for_each_entry_safe(lf, lfb, &src->nodes, list) {
27312799
LIST_DELETE(&lf->list);
27322800
LIST_APPEND(&dst->nodes, &lf->list);
@@ -2742,6 +2810,33 @@ void lf_expr_xfer(struct lf_expr *src, struct lf_expr *dst)
27422810
lf_expr_init(src);
27432811
}
27442812

2813+
/* tries to duplicate an uncompiled logformat expression from <orig> to <dest>
2814+
*
2815+
* Returns 1 on success and 0 on failure.
2816+
*/
2817+
int lf_expr_dup(const struct lf_expr *orig, struct lf_expr *dest)
2818+
{
2819+
BUG_ON((orig->flags & LF_FL_COMPILED));
2820+
lf_expr_deinit(dest);
2821+
if (orig->str) {
2822+
dest->str = strdup(orig->str);
2823+
if (!dest->str)
2824+
goto error;
2825+
}
2826+
if (orig->conf.file) {
2827+
dest->conf.file = strdup(orig->conf.file);
2828+
if (!dest->conf.file)
2829+
goto error;
2830+
}
2831+
dest->conf.line = orig->conf.line;
2832+
2833+
return 1;
2834+
2835+
error:
2836+
lf_expr_deinit(dest);
2837+
return 0;
2838+
}
2839+
27452840
/* Builds a log line in <dst> based on <lf_expr>, and stops before reaching
27462841
* <maxsize> characters. Returns the size of the output string in characters,
27472842
* not counting the trailing zero which is always added if the resulting size

0 commit comments

Comments
 (0)