[ 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: log4j-dev-unsubscr...@logging.apache.org For additional commands, e-mail: log4j-dev-h...@logging.apache.org