The docs contain the following warning, with the emphasizes taken from the official docs:
Lookups in the children of the Route component are not evaluated at configuration time. The substitution is delayed until the Route element is evaluated. This means that ${...} expression should not be escaped as $${...}.
https://logging.apache.org/log4j/2.x/manual/appenders/delegating.html#Route
I have the following two configs, which seem to behave the same:
<Routing name="[...]">
<Routes pattern="$${ctx:foo}">
<Route key="true" ref="RollingFileFoo" />
<Route key="$${ctx:foo}" ref="RollingFile" />
</Routes>
</Routing>
vs.
<Routing name="[...]">
<Routes pattern="$${ctx:foo}">
<Route key="true" ref="RollingFileFoo" />
<Route key="${ctx:foo}" ref="RollingFile" />
</Routes>
</Routing>
The behaviour from my understanding is that pattern
gets an expression configured and that expression is interpolated at config time, why $$
is needed for escaping reasons. So at runtime the pattern actually is ${ctx:foo}
and evaluated for each and every log statement passing the appender.
Step 2: If the expression doesn't find a key of the given name foo
, then the result of the pattern is kept as the pattern itself. That's why there's an additional key
with the pattern itself as a value to match against. And this is where things become interesting:
Is that result now expected to be with $$
like configured in the file or $
only like taken at runtime?
I'm a bit confused which the correct value is because both seem to work exactly the same. Which shouldn't happen if the key
attribute implements an equals comparison to it's value.
public Route getRoute(final String key) {
for (final Route route : routes) {
if (Objects.equals(route.getKey(), key)) {
return route;
}
}
return null;
}
From my understanding, the escaping using $$
in pattern
is necessary because that pattern otherwise gets resolved already at config time, but it needs to be forwarded into runtime. If the difference is that key
is simply NEVER resolved at all at config time and really only taken as string as-is, then I would have expected $$
to fail, because that value is not available at runtime?
I've received an answer at the mailing list, thanks Piotr! So $$
at both places is correct in my case and children
really refers to the body of the Route
only.
There is subtle difference between the two configurations:
* In the first case the value of `key` will always be a literal
`${ctx:foo}`.
* In the second case Log4j Core will expand `${ctx:foo}` at
configuration time. Since the configuration of Log4j Core happens
very early in the startup sequence, `foo` will almost certainly be
undefined, so `key` will be a literal `${ctx:foo}`. In the very
unlikely case `foo` is defined at configuration time, however, the
value of `foo` will be substituted and your configuration will not
work as expected.
To see the difference, try adding:
static {
ThreadContext.put("foo", "true");
}
private static final Logger logger = LogManager.getLogger();
at the very beginning of the main class of your application.
Piotr
https://lists.apache.org/thread/jvj5s5txdknyx8b8o0tqxtvyrf0zzyfd