From 5617e47f915caff2a6f4b9f89080aa2b510b9a18 Mon Sep 17 00:00:00 2001 From: Aurelien DARRAGON Date: Thu, 19 Mar 2026 12:43:00 +0100 Subject: [PATCH] MINOR: log: support optional 'profile ' argument to do-log action We anticipated that the do-log action should be expanded with optional arguments at some point. Now that we heard of multiple use-cases that could be achieved with do-log action, but that are limitated by the fact that all do-log statements inherit from the implicit log-profile defined on the logger, we need to provide a way for the user to specify that custom log-profile that could be used per do-log actions individually This is what we try to achieve in this commit, by leveraging the prerequisite work performed by the last 2 commits. --- doc/configuration.txt | 26 ++++++++++---- include/haproxy/action-t.h | 5 +++ src/log.c | 71 +++++++++++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 3e1df8acd..a1257973b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -15382,15 +15382,14 @@ disable-l7-retry reason than a connection failure. This can be useful for example to make sure POST requests aren't retried on failure. -do-log +do-log [profile ] Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft X | X | X | X | X | X | X | X This action manually triggers a log emission on the proxy. This means log options on the proxy will be considered (including formatting options such as "log-format"), but it will not interfere with the logs automatically - generated by the proxy during transaction handling. It currently doesn't - support any argument, though extensions may appear in future versions. + generated by the proxy during transaction handling. Using "log-profile", it is possible to precisely describe how the log should be emitted for each of the available contexts where the action may be used. @@ -15400,15 +15399,28 @@ do-log Also, they will be properly reported when using "%OG" logformat alias. + Optional "profile" argument may be used to specify the name of a log-profile + section that should be used for this do-log action specifically instead of + the one associated to the current logger that applies by default. + Example: - log-profile myprof + log-profile my-dft-prof on tcp-req-conn format "Connect: %ci" + log-profile my-local-prof + on tcp-req-conn format "Local Connect: %ci" + frontend myfront - log stdout format rfc5424 profile myprof local0 + log stdout format rfc5424 profile my-dft-prof local0 log-format "log generated using proxy logformat, from '%OG'" - tcp-request connection do-log #uses special log-profile format - tcp-request content do-log #uses proxy logformat + acl local src 127.0.0.1 + # on connection use either log-profile from the logger (my-dft-prof) or + # explicit my-local-prof if source ip is localhost + tcp-request connection do-log if !local + tcp-request connection do-log profile my-local-prof if local + # on content use proxy logformat, since no override was specified + # in my-dft-prof + tcp-request content do-log do-resolve(,[,ipv4|ipv6]) Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index c10360e2b..984f55c64 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -198,6 +198,11 @@ struct act_rule { struct server *srv; /* target server to attach the connection */ struct sample_expr *name; /* used to differentiate idle connections */ } attach_srv; /* 'attach-srv' rule */ + struct { + enum log_orig_id orig; + char *profile_name; + struct log_profile *profile; + } do_log; /* 'do-log' action */ struct { int value; struct sample_expr *expr; diff --git a/src/log.c b/src/log.c index 0a3d32d8d..a544ea18c 100644 --- a/src/log.c +++ b/src/log.c @@ -6927,24 +6927,87 @@ static int px_parse_log_steps(char **args, int section_type, struct proxy *curpx static enum act_return do_log_action(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags) { + struct process_send_log_ctx ctx; + /* do_log() expects valid session pointer */ BUG_ON(sess == NULL); - do_log(sess, s, log_orig(rule->arg.expr_int.value, LOG_ORIG_FL_NONE)); + ctx.origin = log_orig(rule->arg.do_log.orig, LOG_ORIG_FL_NONE); + ctx.sess = sess; + ctx.stream = s; + ctx.profile = rule->arg.do_log.profile; + + do_log_ctx(&ctx); return ACT_RET_CONT; } -/* Parse a "do_log" action. It doesn't take any argument +static int do_log_action_check(struct act_rule *rule, struct proxy *px, char **err) +{ + if (rule->arg.do_log.profile_name) { + struct log_profile *prof; + + prof = log_profile_find_by_name(rule->arg.do_log.profile_name); + if (!prof) { + memprintf(err, "do-log action: profile '%s' is invalid", rule->arg.do_log.profile_name); + ha_free(&rule->arg.do_log.profile_name); + return 0; + } + + ha_free(&rule->arg.do_log.profile_name); + + if (!log_profile_postcheck(px, prof, err)) { + memprintf(err, "do-log action on %s %s uses incompatible log-profile '%s': %s", proxy_type_str(px), px->id, prof->id, *err); + return 0; + } + rule->arg.do_log.profile = prof; + } + return 1; // success +} + +static void do_log_action_release(struct act_rule *rule) +{ + ha_free(&rule->arg.do_log.profile_name); +} + + +/* Parse a "do_log" action. It takes optional "log-profile" argument to + * specifically use a given log-profile when generating the log message + * * May be used from places where per-context actions are usually registered */ enum act_parse_ret do_log_parse_act(enum log_orig_id id, const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err) { + int cur_arg = *orig_arg; + rule->action_ptr = do_log_action; rule->action = ACT_CUSTOM; - rule->release_ptr = NULL; - rule->arg.expr_int.value = id; + rule->check_ptr = do_log_action_check; + rule->release_ptr = do_log_action_release; + rule->arg.do_log.orig = id; + + while (*args[*orig_arg]) { + if (!strcmp(args[*orig_arg], "profile")) { + if (!*args[*orig_arg + 1]) { + memprintf(err, + "action '%s': 'profile' expects argument.", + args[cur_arg-1]); + return ACT_RET_PRS_ERR; + } + rule->arg.do_log.profile_name = strdup(args[*orig_arg + 1]); + if (!rule->arg.do_log.profile_name) { + memprintf(err, + "action '%s': memory error when setting 'profile'", + args[cur_arg-1]); + return ACT_RET_PRS_ERR; + } + *orig_arg += 2; + } + else + break; + } + return ACT_RET_PRS_OK; } -- 2.47.3