[
https://issues.apache.org/jira/browse/LOG4J2-1573?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15483317#comment-15483317
]
Steffen Offermann commented on LOG4J2-1573:
-------------------------------------------
This is the stack trace from the debugger (the test class has a different name
in that stack, but the logic is the same):
{noformat}
Thread [main] (Suspended (breakpoint at line 191 in PluginBuilder))
PluginBuilder.injectFields(Builder<?>) line: 191
PluginBuilder.build() line: 121
XmlConfiguration(AbstractConfiguration).createPluginObject(PluginType<?>, Node,
LogEvent) line: 933
XmlConfiguration(AbstractConfiguration).createConfiguration(Node,
LogEvent) line: 873
XmlConfiguration(AbstractConfiguration).createConfiguration(Node,
LogEvent) line: 865
XmlConfiguration(AbstractConfiguration).doConfigure() line: 489
XmlConfiguration(AbstractConfiguration).initialize() line: 226
XmlConfiguration(AbstractConfiguration).start() line: 238
LoggerContext.setConfiguration(Configuration) line: 525
LoggerContext.start(Configuration) line: 258
Log4jContextFactory.getContext(String, ClassLoader, Object, boolean,
URI, String) line: 239
Configurator.initialize(String, ClassLoader, URI, Object) line: 159
Configurator.initialize(String, ClassLoader, String, Object) line: 131
Configurator.initialize(String, ClassLoader, String) line: 101
Configurator.initialize(String, String) line: 188
DependencyTest.verifyThatAsyncAppendersAreAvailable() line: 87
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 606
FrameworkMethod$1.runReflectiveCall() line: 50
FrameworkMethod$1(ReflectiveCallable).run() line: 12
FrameworkMethod.invokeExplosively(Object, Object...) line: 47
InvokeMethod.evaluate() line: 17
RunAfters.evaluate() line: 27
BlockJUnit4ClassRunner(ParentRunner<T>).runLeaf(Statement, Description,
RunNotifier) line: 325
BlockJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 78
BlockJUnit4ClassRunner.runChild(Object, RunNotifier) line: 57
ParentRunner$3.run() line: 290
ParentRunner$1.schedule(Runnable) line: 71
BlockJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line:
288
ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 58
ParentRunner$2.evaluate() line: 268
BlockJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 363
JUnit4TestReference.run(TestExecution) line: 86
TestExecution.run(ITestReference[]) line: 38
RemoteTestRunner.runTests(String[], String, TestExecution) line: 459
RemoteTestRunner.runTests(TestExecution) line: 678
RemoteTestRunner.run() line: 382
RemoteTestRunner.main(String[]) line: 192
{noformat}
The relevant method in {{PluginBuilder.java}} is:
{code}
private void injectFields(final Builder<?> builder) throws
IllegalAccessException {
final List<Field> fields =
TypeUtil.getAllDeclaredFields(builder.getClass());
AccessibleObject.setAccessible(fields.toArray(new Field[] {}), true);
final StringBuilder log = new StringBuilder();
boolean invalid = false;
for (final Field field : fields) {
log.append(log.length() == 0 ? simpleName(builder) + "(" : ", ");
final Annotation[] annotations = field.getDeclaredAnnotations();
final String[] aliases = extractPluginAliases(annotations);
for (final Annotation a : annotations) {
if (a instanceof PluginAliases) {
continue; // already processed
}
final PluginVisitor<? extends Annotation> visitor =
PluginVisitors.findVisitor(a.annotationType());
if (visitor != null) {
final Object value = visitor.setAliases(aliases)
.setAnnotation(a)
.setConversionType(field.getType())
.setStrSubstitutor(configuration.getStrSubstitutor())
.setMember(field)
.visit(configuration, node, event, log);
// don't overwrite default values if the visitor gives us
no value to inject
if (value != null) {
field.set(builder, value);
}
}
}
final Collection<ConstraintValidator<?>> validators =
ConstraintValidators.findValidators(annotations);
final Object value = field.get(builder);
for (final ConstraintValidator<?> validator : validators) {
if (!validator.isValid(field.getName(), value)) {
invalid = true;
}
}
}
log.append(log.length() == 0 ? builder.getClass().getSimpleName() +
"()" : ")");
LOGGER.debug(log.toString());
if (invalid) {
throw new ConfigurationException("Arguments given for element " +
node.getName() + " are invalid");
}
checkForRemainingAttributes();
verifyNodeChildrenUsed();
}
{code}
The builder is of type
{code}
org.apache.logging.log4j.core.appender.ConsoleAppender$Builder@29523ccf
{code}
And the fields are gathered in the line
{code}
TypeUtil.getAllDeclaredFields(builder.getClass());
{code}
{{ConsoleAppender.Builder}} extends {{AbstractOutputStreamAppender}}, which
extends {{AbstractAppender.Builder}}, and there we find:
{code}
public abstract static class Builder<B extends Builder<B>> extends
AbstractFilterable.Builder<B> {
@PluginBuilderAttribute
private boolean ignoreExceptions = true;
@PluginElement("Layout")
@Required
private Layout<? extends Serializable> layout;
...
{code}
In {{PluginBuilder.injectFields()}} the part {code}
if (!validator.isValid(field.getName(), value)) {
invalid = true;
}
{code}
tries to validate the field
{code}
private org.apache.logging.log4j.core.Layout
org.apache.logging.log4j.core.appender.AbstractAppender$Builder.layout
{code}
with {{field.getName()}} equaling _"layout"_, and the validator is
{code}
org.apache.logging.log4j.core.config.plugins.validation.validators.RequiredValidator@71ac6d5
{code}
The method {{RequiredValidator.isValid()}} returns false because the value is
{{null}}:
{code}
@Override
public boolean isValid(final String name, final Object value) {
if (value == null) {
return err(name);
}
...
{code}
Regards,
Steffen
> Layout is no longer optional
> ----------------------------
>
> Key: LOG4J2-1573
> URL: https://issues.apache.org/jira/browse/LOG4J2-1573
> Project: Log4j 2
> Issue Type: Bug
> Components: Appenders
> Affects Versions: 2.7
> Reporter: Steffen Offermann
> Priority: Minor
>
> This configuration used to work in 2.6.2:
> {code:xml}
> <?xml version="1.0" encoding="UTF-8"?>
> <Configuration status="warn">
> <Appenders>
> <Console name="myConsole" target="SYSTEM_OUT"/>
> <Async name="myConsoleAsync">
> <AppenderRef ref="myConsole" />
> </Async>
> </Appenders>
> <Loggers>
> <AsyncRoot level="info" />
> </Loggers>
> </Configuration>
> {code}
> With the current {{master}} (i.e. the upcoming 2.7) this does not work any
> more. The validator complains because the field "layout" is missing in
> {code:xml}
> <Console name="myConsole" target="SYSTEM_OUT" />
> {code}
> According to the current documentation this is a bug:
> ||Parameter Name||Type|| Description||
> | layout | Layout | The Layout to use to format the LogEvent. If no layout is
> supplied the default pattern layout of "%m%n" will be used.
> With this configuration it works:
> {code:xml}
> <?xml version="1.0" encoding="UTF-8"?>
> <Configuration status="warn">
> <Appenders>
> <Console name="myConsole" target="SYSTEM_OUT">
> <DetailsLayout/>
> </Console>
> <Async name="myConsoleAsync">
> <AppenderRef ref="myConsole" />
> </Async>
> </Appenders>
> <Loggers>
> <AsyncRoot level="info" />
> </Loggers>
> </Configuration>
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]