[ 
https://issues.apache.org/jira/browse/LOG4J2-2874?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Volkan Yazici updated LOG4J2-2874:
----------------------------------
    Description: 
Upon further investigation on [Fred Eisele's question in the mailing 
list|http://mail-archives.apache.org/mod_mbox/logging-log4j-user/202006.mbox/%3CCAJGQK3M63cx37tfLUSB0X9x3U8PmvUdDOigX-kUTMs6r2nP6hg%40mail.gmail.com%3E],
 I figured {{Layout}}'s way of handling {{Message}}'s of type 
{{StringBuilderFormattable}} is broken when the actual message is wrapped by 
some other message. Consider the following Java code:

{code:java}
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.StringBuilderFormattable;
import org.junit.Test;

public class TraceExitTest {

    @Test
    public void test() {
        System.setProperty(
                "log4j.configurationFile",
                "/tmp/traceExit.xml");
        Logger logger = LogManager.getLogger("TraceExit");
        int result = logger.traceExit(
                new CustomMessage("Volkan was here"),
                1);
        logger.warn("result = %d", result);
    }

    private static final class CustomMessage
            implements Message, StringBuilderFormattable {

        private final String text;

        private CustomMessage(String text) {
            this.text = text;
        }

        @Override
        public String getFormattedMessage() {
            throw new UnsupportedOperationException();
        }

        @Override
        public String getFormat() {
            return "";
        }

        @Override
        public Object[] getParameters() {
            return new Object[0];
        }

        @Override
        public Throwable getThrowable() {
            return null;
        }

        @Override
        public void formatTo(StringBuilder buffer) {
            buffer.append(text);
        }

    }

}
{code}

In combination with the following {{/tmp/traceExit.xml}}:

{code:xml}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%msg%n%throwable"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="TRACE">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
{code}

Log4j produces the {{An exception occurred processing Appender Console 
java.lang.UnsupportedOperationException}} error message. In the case of
{{PatternLayout}}, it handles {{StringBuilderFormattable}} with a simple
{{instanceof}} check -- just like any other layout. Though check out the
following {{AbstractLogger#traceExit()}} implementation:

{code:java}
@Override
public <R> R traceExit(final Message message, final R result) {
    // If the message is null, traceEnter returned null because ...
    if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, null)) {
        logMessageSafely(...,
flowMessageFactory.newExitMessage(result, message), null);
    }
    return result;
}
{code}
 
{{CustomMessage}} gets wrapped by a 
{{DefaultFlowMessageFactory.SimpleExitMessage}} invalidating the {{instanceof 
StringBuilderFormattable}} check.

One can fix the existing layouts by improving the check as follows:

{code:java}
final boolean formattable =
        logEvent.getMessage() instanceof StringBuilderFormattable ||
                (logEvent.getMessage() instanceof FlowMessage && ((FlowMessage) 
logEvent.getMessage()) instanceof StringBuilderFormattable);
{code}

Though I have my doubts if this will cover every single case.

  was:
Upon further investigation on [Fred Eisele's question in the mailing 
list|http://mail-archives.apache.org/mod_mbox/logging-log4j-user/202006.mbox/%3CCAJGQK3M63cx37tfLUSB0X9x3U8PmvUdDOigX-kUTMs6r2nP6hg%40mail.gmail.com%3E],
 I figured {{Layout}}s way of handling {{Message}}s of type 
{{StringBuilderFormattable}} is broken when the actual message is wrapped by 
some other message. Consider the following Java code:

{code:java}
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.StringBuilderFormattable;
import org.junit.Test;

public class TraceExitTest {

    @Test
    public void test() {
        System.setProperty(
                "log4j.configurationFile",
                "/tmp/traceExit.xml");
        Logger logger = LogManager.getLogger("TraceExit");
        int result = logger.traceExit(
                new CustomMessage("Volkan was here"),
                1);
        logger.warn("result = %d", result);
    }

    private static final class CustomMessage
            implements Message, StringBuilderFormattable {

        private final String text;

        private CustomMessage(String text) {
            this.text = text;
        }

        @Override
        public String getFormattedMessage() {
            throw new UnsupportedOperationException();
        }

        @Override
        public String getFormat() {
            return "";
        }

        @Override
        public Object[] getParameters() {
            return new Object[0];
        }

        @Override
        public Throwable getThrowable() {
            return null;
        }

        @Override
        public void formatTo(StringBuilder buffer) {
            buffer.append(text);
        }

    }

}
{code}

In combination with the following {{/tmp/traceExit.xml}}:

{code:xml}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%msg%n%throwable"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="TRACE">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
{code}

Log4j produces the {{An exception occurred processing Appender Console
java.lang.UnsupportedOperationException}} error message. In the case of
{{PatternLayout}}, it handles {{StringBuilderFormattable}} with a simple
{{instanceof}} check -- just like any other layout. Though check out the
following {{AbstractLogger#traceExit()}} implementation:

{code:java}
@Override
public <R> R traceExit(final Message message, final R result) {
    // If the message is null, traceEnter returned null because ...
    if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, null)) {
        logMessageSafely(...,
flowMessageFactory.newExitMessage(result, message), null);
    }
    return result;
}
{code}
 
{{CustomMessage}} gets wrapped by a 
{{DefaultFlowMessageFactory.SimpleExitMessage}} invalidating the {{instanceof 
StringBuilderFormattable}} check.

One can fix the existing layouts by improving the check as follows:

{code:java}
final boolean formattable =
        logEvent.getMessage() instanceof StringBuilderFormattable ||
                (logEvent.getMessage() instanceof FlowMessage && ((FlowMessage) 
logEvent.getMessage()) instanceof StringBuilderFormattable);
{code}

Though I have my doubts if this will cover every single case.


> StringBuilderFormattable check failure in layouts due to wrapped Message
> ------------------------------------------------------------------------
>
>                 Key: LOG4J2-2874
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-2874
>             Project: Log4j 2
>          Issue Type: Bug
>            Reporter: Volkan Yazici
>            Priority: Major
>
> Upon further investigation on [Fred Eisele's question in the mailing 
> list|http://mail-archives.apache.org/mod_mbox/logging-log4j-user/202006.mbox/%3CCAJGQK3M63cx37tfLUSB0X9x3U8PmvUdDOigX-kUTMs6r2nP6hg%40mail.gmail.com%3E],
>  I figured {{Layout}}'s way of handling {{Message}}'s of type 
> {{StringBuilderFormattable}} is broken when the actual message is wrapped by 
> some other message. Consider the following Java code:
> {code:java}
> import org.apache.logging.log4j.message.Message;
> import org.apache.logging.log4j.util.StringBuilderFormattable;
> import org.junit.Test;
> public class TraceExitTest {
>     @Test
>     public void test() {
>         System.setProperty(
>                 "log4j.configurationFile",
>                 "/tmp/traceExit.xml");
>         Logger logger = LogManager.getLogger("TraceExit");
>         int result = logger.traceExit(
>                 new CustomMessage("Volkan was here"),
>                 1);
>         logger.warn("result = %d", result);
>     }
>     private static final class CustomMessage
>             implements Message, StringBuilderFormattable {
>         private final String text;
>         private CustomMessage(String text) {
>             this.text = text;
>         }
>         @Override
>         public String getFormattedMessage() {
>             throw new UnsupportedOperationException();
>         }
>         @Override
>         public String getFormat() {
>             return "";
>         }
>         @Override
>         public Object[] getParameters() {
>             return new Object[0];
>         }
>         @Override
>         public Throwable getThrowable() {
>             return null;
>         }
>         @Override
>         public void formatTo(StringBuilder buffer) {
>             buffer.append(text);
>         }
>     }
> }
> {code}
> In combination with the following {{/tmp/traceExit.xml}}:
> {code:xml}
> <?xml version="1.0" encoding="UTF-8"?>
> <Configuration status="ERROR">
>     <Appenders>
>         <Console name="Console" target="SYSTEM_OUT">
>             <PatternLayout pattern="%msg%n%throwable"/>
>         </Console>
>     </Appenders>
>     <Loggers>
>         <Root level="TRACE">
>             <AppenderRef ref="Console"/>
>         </Root>
>     </Loggers>
> </Configuration>
> {code}
> Log4j produces the {{An exception occurred processing Appender Console 
> java.lang.UnsupportedOperationException}} error message. In the case of
> {{PatternLayout}}, it handles {{StringBuilderFormattable}} with a simple
> {{instanceof}} check -- just like any other layout. Though check out the
> following {{AbstractLogger#traceExit()}} implementation:
> {code:java}
> @Override
> public <R> R traceExit(final Message message, final R result) {
>     // If the message is null, traceEnter returned null because ...
>     if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, 
> null)) {
>         logMessageSafely(...,
> flowMessageFactory.newExitMessage(result, message), null);
>     }
>     return result;
> }
> {code}
>  
> {{CustomMessage}} gets wrapped by a 
> {{DefaultFlowMessageFactory.SimpleExitMessage}} invalidating the {{instanceof 
> StringBuilderFormattable}} check.
> One can fix the existing layouts by improving the check as follows:
> {code:java}
> final boolean formattable =
>         logEvent.getMessage() instanceof StringBuilderFormattable ||
>                 (logEvent.getMessage() instanceof FlowMessage && 
> ((FlowMessage) logEvent.getMessage()) instanceof StringBuilderFormattable);
> {code}
> Though I have my doubts if this will cover every single case.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to