Well darn, you're right!

I completely missed that. 

I wish you'd told me a few days ago though. That kind of feedback was exactly 
was I was hoping for when I attached the patch to the Jira ticket. 

Sent from my iPhone

> On 2014/08/10, at 21:49, Ralph Goers <[email protected]> wrote:
> 
> One of the points of getFormattedMessage was to defer the expense of 
> formatting until it is actually required. This change eliminates that 
> savings.  I would have preferred having the async component call 
> getFormattedMessage when it needed to, thus not impacting synchronous 
> configurations.
> 
> Sent from my iPad
> 
>> On Aug 10, 2014, at 5:12 AM, Gary Gregory <[email protected]> wrote:
>> 
>> Instead of calling getFormattedMessage(), how about refactoring it and call 
>> an initSomething method. This will make the call sites less smelly of 
>> programming by side effect. 
>> 
>> Gary
>> 
>> 
>> -------- Original message --------
>> From: [email protected]
>> Date:08/10/2014 02:40 (GMT-05:00)
>> To: [email protected]
>> Subject: svn commit: r1617051 - in /logging/log4j/log4j2/trunk: 
>> log4j-api/src/main/java/org/apache/logging/log4j/message/ 
>> log4j-api/src/test/java/org/apache/logging/log4j/message/ src/changes/ 
>> src/site/xdoc/manual/
>> 
>> Author: rpopma
>> Date: Sun Aug 10 06:40:14 2014
>> New Revision: 1617051
>> 
>> URL: http://svn.apache.org/r1617051
>> Log:
>> LOG4J2-763: Improved FormattedMessage, StringFormattedMessage, 
>> LocalizedMessage, MessageFormatMessage and ObjectMessage for asynchronous 
>> logging to ensure the formatted message does not change even if parameters 
>> are modified by the application. Improved docs for MapMessage and 
>> StructuredDataMessage. Improved site docs.
>> 
>> Added:
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java
>>    (with props)
>> Modified:
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
>>    
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
>>    logging/log4j/log4j2/trunk/src/changes/changes.xml
>>    logging/log4j/log4j2/trunk/src/site/xdoc/manual/async.xml
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -46,12 +46,11 @@ public class FormattedMessage implements
>>         this.messagePattern = messagePattern;
>>         this.argArray = arguments;
>>         this.throwable = throwable;
>> +        getFormattedMessage(); // LOG4J2-763 take snapshot of parameters at 
>> message construction time
>>     }
>> 
>>     public FormattedMessage(final String messagePattern, final Object[] 
>> arguments) {
>> -        this.messagePattern = messagePattern;
>> -        this.argArray = arguments;
>> -        this.throwable = null;
>> +        this(messagePattern, arguments, null);
>>     }
>> 
>>     /**
>> @@ -60,9 +59,7 @@ public class FormattedMessage implements
>>      * @param arg The parameter.
>>      */
>>     public FormattedMessage(final String messagePattern, final Object arg) {
>> -        this.messagePattern = messagePattern;
>> -        this.argArray = new Object[] {arg};
>> -        this.throwable = null;
>> +        this(messagePattern, new Object[] {arg}, null);
>>     }
>> 
>>     /**
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -78,6 +78,7 @@ public class LocalizedMessage implements
>>         this.baseName = baseName;
>>         this.resourceBundle = null;
>>         this.locale = locale;
>> +        getFormattedMessage(); // LOG4J2-763 take snapshot of parameters at 
>> message construction time
>>     }
>> 
>>     public LocalizedMessage(final ResourceBundle bundle, final Locale 
>> locale, final String key,
>> @@ -88,6 +89,7 @@ public class LocalizedMessage implements
>>         this.baseName = null;
>>         this.resourceBundle = bundle;
>>         this.locale = locale;
>> +        getFormattedMessage(); // LOG4J2-763 take snapshot of parameters at 
>> message construction time
>>     }
>> 
>>     public LocalizedMessage(final Locale locale, final String key, final 
>> Object[] arguments) {
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -26,6 +26,11 @@ import org.apache.logging.log4j.util.Str
>> 
>> /**
>>  * Represents a Message that consists of a Map.
>> + * <p>
>> + * Thread-safety note: the contents of this message can be modified after 
>> construction.
>> + * When using asynchronous loggers and appenders it is not recommended to 
>> modify this message after the message is
>> + * logged, because it is undefined whether the logged message string will 
>> contain the old values or the modified
>> + * values.
>>  */
>> public class MapMessage implements MultiformatMessage {
>>     /**
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -49,6 +49,7 @@ public class MessageFormatMessage implem
>>         if (arguments != null && arguments.length > 0 && 
>> arguments[arguments.length - 1] instanceof Throwable) {
>>             this.throwable = (Throwable) arguments[arguments.length - 1];
>>         }
>> +        getFormattedMessage(); // LOG4J2-763 take snapshot of parameters at 
>> message construction time
>>     }
>> 
>>     /**
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -29,6 +29,7 @@ public class ObjectMessage implements Me
>>     private static final long serialVersionUID = -5903272448334166185L;
>> 
>>     private transient Object obj;
>> +    private final String objectString;
>> 
>>     /**
>>      * Create the ObjectMessage.
>> @@ -39,6 +40,9 @@ public class ObjectMessage implements Me
>>             obj = "null";
>>         }
>>         this.obj = obj;
>> +        
>> +        // LOG4J2-763: take snapshot of parameters at message construction 
>> time
>> +        objectString = String.valueOf(obj);
>>     }
>> 
>>     /**
>> @@ -47,7 +51,7 @@ public class ObjectMessage implements Me
>>      */
>>     @Override
>>     public String getFormattedMessage() {
>> -        return obj.toString();
>> +        return objectString;
>>     }
>> 
>>     /**
>> @@ -56,7 +60,7 @@ public class ObjectMessage implements Me
>>      */
>>     @Override
>>     public String getFormat() {
>> -        return obj.toString();
>> +        return objectString;
>>     }
>> 
>>     /**
>> @@ -89,7 +93,7 @@ public class ObjectMessage implements Me
>> 
>>     @Override
>>     public String toString() {
>> -        return "ObjectMessage[obj=" + obj.toString() + ']';
>> +        return "ObjectMessage[obj=" + objectString + ']';
>>     }
>> 
>>     private void writeObject(final ObjectOutputStream out) throws 
>> IOException {
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -48,6 +48,7 @@ public class StringFormattedMessage impl
>>         if (arguments != null && arguments.length > 0 && 
>> arguments[arguments.length - 1] instanceof Throwable) {
>>             this.throwable = (Throwable) arguments[arguments.length - 1];
>>         }
>> +        getFormattedMessage(); // LOG4J2-763 take snapshot of parameters at 
>> message construction time
>>     }
>> 
>>     /**
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -22,6 +22,11 @@ import org.apache.logging.log4j.util.Eng
>> 
>> /**
>>  * Represents a Message that conforms to an RFC 5424 StructuredData element 
>> along with the syslog message.
>> + * <p>
>> + * Thread-safety note: the contents of this message can be modified after 
>> construction.
>> + * When using asynchronous loggers and appenders it is not recommended to 
>> modify this message after the message is
>> + * logged, because it is undefined whether the logged message string will 
>> contain the old values or the modified
>> + * values.
>>  *
>>  * @see <a href="https://tools.ietf.org/html/rfc5424";>RFC 5424</a>
>>  */
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -83,4 +83,16 @@ public class FormattedMessageTest {
>>         result = msg.getFormattedMessage();
>>         assertEquals(testMsg, result);
>>     }
>> +
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final String testMsg = "Test message %s";
>> +        final Mutable param = new Mutable().set("abc");
>> +        FormattedMessage msg = new FormattedMessage(testMsg, param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "Test message abc", 
>> actual);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -64,4 +64,16 @@ public class LocalizedMessageTest {
>>         assertEquals("This is test number 1 with string argument Test.", 
>> msg.getFormattedMessage());
>>     }
>> 
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final String testMsg = "Test message %s";
>> +        final Mutable param = new Mutable().set("abc");
>> +        LocalizedMessage msg = new LocalizedMessage(testMsg, param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "Test message abc", 
>> actual);
>> +    }
>> +
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -70,4 +70,22 @@ public class MapMessageTest {
>>         final String expected = "{message=\"Test message {}\", 
>> project=\"Log4j\"}";
>>         assertEquals(expected, result);
>>     }
>> +
>> +    @Test
>> +    public void testMutableByDesign() { // LOG4J2-763
>> +        MapMessage msg = new MapMessage();
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        msg.put("key1", "value1");
>> +        msg.put("key2", "value2");
>> +        final String result = msg.getFormattedMessage(new String[]{"Java"});
>> +        final String expected = "{key1=\"value1\", key2=\"value2\"}";
>> +        assertEquals(expected, result);
>> +
>> +        // modify parameter after calling msg.getFormattedMessage
>> +        msg.put("key3", "value3");
>> +        final String result2 = msg.getFormattedMessage(new 
>> String[]{"Java"});
>> +        final String expected2 = "{key1=\"value1\", key2=\"value2\", 
>> key3=\"value3\"}";
>> +        assertEquals(expected2, result2);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -61,4 +61,16 @@ public class MessageFormatMessageTest {
>>         final Throwable t = msg.getThrowable();
>>         assertNotNull("No Throwable", t);
>>     }
>> +
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final String testMsg = "Test message {0}";
>> +        final Mutable param = new Mutable().set("abc");
>> +        MessageFormatMessage msg = new MessageFormatMessage(testMsg, param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "Test message abc", 
>> actual);
>> +    }
>> }
>> 
>> Added: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java?rev=1617051&view=auto
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java
>>  (added)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -0,0 +1,34 @@
>> +/*
>> + * Licensed to the Apache Software Foundation (ASF) under one or more
>> + * contributor license agreements. See the NOTICE file distributed with
>> + * this work for additional information regarding copyright ownership.
>> + * The ASF licenses this file to You under the Apache license, Version 2.0
>> + * (the "License"); you may not use this file except in compliance with
>> + * the License. You may obtain a copy of the License at
>> + *
>> + *      http://www.apache.org/licenses/LICENSE-2.0
>> + *
>> + * Unless required by applicable law or agreed to in writing, software
>> + * distributed under the License is distributed on an "AS IS" BASIS,
>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> + * See the license for the specific language governing permissions and
>> + * limitations under the license.
>> + */
>> +package org.apache.logging.log4j.message;
>> +
>> +/**
>> + * Helper class for JUnit tests.
>> + */
>> +class Mutable {
>> +    private String value;
>> +
>> +    public Mutable set(String value) {
>> +        this.value = value;
>> +        return this;
>> +    }
>> +
>> +    @Override
>> +    public String toString() {
>> +        return this.value;
>> +    }
>> +}
>> \ No newline at end of file
>> 
>> Propchange: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/Mutable.java
>> ------------------------------------------------------------------------------
>>    svn:eol-style = native
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -39,4 +39,15 @@ public class ObjectMessageTest {
>>         final String result = msg.getFormattedMessage();
>>         assertEquals(testMsg, result);
>>     }
>> +
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final Mutable param = new Mutable().set("abc");
>> +        ObjectMessage msg = new ObjectMessage(param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "abc", actual);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -36,4 +36,16 @@ public class ParameterizedMessageTest {
>>         result = msg.getFormattedMessage();
>>         assertEquals(testMsg, result);
>>     }
>> +
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final String testMsg = "Test message {}";
>> +        final Mutable param = new Mutable().set("abc");
>> +        ParameterizedMessage msg = new ParameterizedMessage(testMsg, param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "Test message abc", 
>> actual);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -60,4 +60,16 @@ public class StringFormattedMessageTest 
>>         final Throwable t = msg.getThrowable();
>>         assertNotNull("No Throwable", t);
>>     }
>> +
>> +    @Test
>> +    public void testSafeWithMutableParams() { // LOG4J2-763
>> +        final String testMsg = "Test message %s";
>> +        final Mutable param = new Mutable().set("abc");
>> +        StringFormattedMessage msg = new StringFormattedMessage(testMsg, 
>> param);
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        param.set("XYZ");
>> +        String actual = msg.getFormattedMessage();
>> +        assertEquals("Should use initial param value", "Test message abc", 
>> actual);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -43,4 +43,23 @@ public class StructuredDataMessageTest {
>>         final StructuredDataMessage msg = new 
>> StructuredDataMessage("MsgId@12345", testMsg, "Alert");
>>         msg.put("This is a very long key that will violate the key length 
>> validation", "Testing");
>>     }
>> +
>> +    @Test
>> +    public void testMutableByDesign() { // LOG4J2-763
>> +        final String testMsg = "Test message {}";
>> +        StructuredDataMessage msg = new StructuredDataMessage("MsgId@1", 
>> testMsg, "Alert");
>> +
>> +        // modify parameter before calling msg.getFormattedMessage
>> +        msg.put("message", testMsg);
>> +        msg.put("project", "Log4j");
>> +        final String result = msg.getFormattedMessage();
>> +        final String expected = "Alert [MsgId@1 message=\"Test message {}\" 
>> project=\"Log4j\"] Test message {}";
>> +        assertEquals(expected, result);
>> +
>> +        // modify parameter after calling msg.getFormattedMessage
>> +        msg.put("memo", "Added later");
>> +        final String result2 = msg.getFormattedMessage();
>> +        final String expected2 = "Alert [MsgId@1 memo=\"Added later\" 
>> message=\"Test message {}\" project=\"Log4j\"] Test message {}";
>> +        assertEquals(expected2, result2);
>> +    }
>> }
>> 
>> Modified: 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
>>  (original)
>> +++ 
>> logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
>>  Sun Aug 10 06:40:14 2014
>> @@ -73,6 +73,22 @@ public class ThreadDumpMessageTest {
>>         assertEquals(expected, msg.toString());
>>     }
>> 
>> +    @Test
>> +    public void testUseConstructorThread() throws InterruptedException { // 
>> LOG4J2-763
>> +        final ThreadDumpMessage msg = new ThreadDumpMessage("Test");
>> +
>> +        final String[] actual = new String[1];
>> +        Thread other = new Thread("OtherThread") {
>> +            public void run() {
>> +                actual[0] = msg.getFormattedMessage();
>> +            }
>> +        };
>> +        other.start();
>> +        other.join();
>> +        
>> +        assertTrue("No mention of other thread in msg", 
>> !actual[0].contains("OtherThread"));
>> +    }
>> +
>>     private class Thread1 extends Thread {
>>         private final ReentrantLock lock;
>> 
>> 
>> Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
>> +++ logging/log4j/log4j2/trunk/src/changes/changes.xml Sun Aug 10 06:40:14 
>> 2014
>> @@ -22,6 +22,12 @@
>>   </properties>
>>   <body>
>>     <release version="2.0.2" date="2014-0?-??" description="Bug fixes and 
>> enhancements">
>> +      <action issue="LOG4J2-763" dev="rpopma" type="fix">
>> +        Improved FormattedMessage, StringFormattedMessage, 
>> LocalizedMessage, MessageFormatMessage and
>> +        ObjectMessage for asynchronous logging to ensure the formatted 
>> message does not change even if
>> +        parameters are modified by the application. (ParameterizedMessage 
>> was already safe.)
>> +        Improved documentation.
>> +      </action>      
>>       <action issue="LOG4J2-729" dev="rpopma" type="fix">
>>         Emit warning message to console if no configuration file found.
>>       </action>      
>> 
>> Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/async.xml
>> URL: 
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/async.xml?rev=1617051&r1=1617050&r2=1617051&view=diff
>> ==============================================================================
>> --- logging/log4j/log4j2/trunk/src/site/xdoc/manual/async.xml (original)
>> +++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/async.xml Sun Aug 10 
>> 06:40:14 2014
>> @@ -124,6 +124,26 @@
>> logging in addition to synchronous
>> logging for the audit trail.)
>> </li>
>> +     <li>In some rare cases, care must be taken with mutable messages.
>> +       Most of the time you don't need to worry about this. Log4 will 
>> ensure that log messages like 
>> +       <code>logger.debug("My object is {}", myObject)</code> will use the 
>> state of the
>> +       <code>myObject</code> parameter at the time of the call to 
>> <code>logger.debug()</code>.
>> +       The log message will not change even if <code>myObject</code> is 
>> modified later.
>> +       It is safe to asynchronously log mutable objects because most 
>> +       <a 
>> href="../log4j-api/apidocs/org/apache/logging/log4j/message/Message.html">Message</a>
>> +       implementations built-in to Log4j take a snapshot of the parameters.
>> +       There are some exceptions however: 
>> +       <a 
>> href="../log4j-api/apidocs/org/apache/logging/log4j/message/MapMessage.html">MapMessage</a>
>> +       and
>> +       <a 
>> href="../log4j-api/apidocs/org/apache/logging/log4j/message/StructuredDataMessage.html">StructuredDataMessage</a>
>> +       are mutable by design: fields can be added to these messages after 
>> the message object was created.
>> +       These messages should not be modified after they are logged with 
>> asynchronous loggers or
>> +       asynchronous appenders; you may or may not see the modifications in 
>> the resulting log output.
>> +       Similarly, custom  
>> +       <a 
>> href="../log4j-api/apidocs/org/apache/logging/log4j/message/Message.html">Message</a>
>> +       implementations should be designed with asynchronous use in mind, 
>> and either take a snapshot
>> +       of their parameters at construction time, or document their 
>> thread-safety characteristics.
>> +     </li>
>> </ul>
>> </subsection>
>> <a name="AllAsync" />
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
> 

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to