[ 
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

Reply via email to