Index: src/main/java/ch/qos/logback/classic/Logger.java =================================================================== --- src/main/java/ch/qos/logback/classic/Logger.java (revision 1645) +++ src/main/java/ch/qos/logback/classic/Logger.java (working copy) @@ -482,7 +482,22 @@ private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params, final Throwable t) { - LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params); + // fix for bug #100 & bug #118 + String[] argumentArray=null; + if(params!=null) { + argumentArray=new String[params.length]; + for(int i=0;iLoggingEvent instance is created. This @@ -42,11 +41,11 @@ */ public class LoggingEvent implements Serializable { - private static final long serialVersionUID = 3075964498087694229L; + /** + * + */ + private static final long serialVersionUID = 4586026859841638369L; - private static final int NULL_ARGUMENT_ARRAY = -1; - private static final String NULL_ARGUMENT_ARRAY_ELEMENT = "NULL_ARGUMENT_ARRAY_ELEMENT"; - /** * */ @@ -78,9 +77,9 @@ private transient Level level; private String message; - private String formattedMessage; + private transient String formattedMessage; - private transient Object[] argumentArray; + private String[] argumentArray; private ThrowableInformation throwableInfo; @@ -101,7 +100,7 @@ } public LoggingEvent(String fqcn, Logger logger, Level level, String message, - Throwable throwable, Object[] argArray) { + Throwable throwable, String[] argArray) { this.fqnOfLoggerClass = fqcn; this.loggerRemoteView = logger.getLoggerRemoteView(); this.level = level; @@ -114,27 +113,30 @@ // bug 85 (we previously failed to set this.argumentArray) this.argumentArray = argArray; - if (argArray != null) { - formattedMessage = MessageFormatter.arrayFormat(message, argArray); + // formattedMessage is initialized lazily in getFormattedMessage + /* + if (argumentArray != null) { + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray); } else { formattedMessage = message; } + */ + timeStamp = System.currentTimeMillis(); // the case is ugly but under the circumstances acceptable - LogbackMDCAdapter logbackMDCAdapter = (LogbackMDCAdapter) MDC - .getMDCAdapter(); + LogbackMDCAdapter logbackMDCAdapter = (LogbackMDCAdapter) MDC.getMDCAdapter(); mdcPropertyMap = logbackMDCAdapter.getPropertyMap(); } - public void setArgumentArray(Object[] argArray) { + public void setArgumentArray(String[] argArray) { if (this.argumentArray != null) { throw new IllegalStateException("argArray has been already set"); } this.argumentArray = argArray; } - public Object[] getArgumentArray() { + public String[] getArgumentArray() { return this.argumentArray; } @@ -186,13 +188,13 @@ * This method should be called prior to serializing an event. It should also * be called when using asynchronous logging. * - *

- * Note that due to performance concerns, this method does NOT extract caller - * data. It is the responsability of the calller to extract caller + *

Note that due to performance concerns, this method does NOT extract + * caller data. It is the responsability of the calller to extract caller * information. */ public void prepareForDeferredProcessing() { this.getThreadName(); + } public LoggerRemoteView getLoggerRemoteView() { @@ -211,6 +213,7 @@ if (this.message != null) { throw new IllegalStateException( "The message for this event has been set already."); + // if this exception is removed, formattedMessage must be set null after setting message! } this.message = message; } @@ -237,7 +240,7 @@ * * @return The time as measured when this class was loaded into memory. */ - public static final long getStartTime() { + public static long getStartTime() { return startTime; } @@ -277,6 +280,13 @@ } public String getFormattedMessage() { + if(formattedMessage == null) { + if (argumentArray != null) { + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray); + } else { + formattedMessage = message; + } + } return formattedMessage; } @@ -287,20 +297,6 @@ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(level.levelInt); - if (argumentArray != null) { - int len = argumentArray.length; - out.writeInt(len); - for (int i = 0; i < argumentArray.length; i++) { - if (argumentArray[i] != null) { - out.writeObject(argumentArray[i].toString()); - } else { - out.writeObject(NULL_ARGUMENT_ARRAY_ELEMENT); - } - } - } else { - out.writeInt(NULL_ARGUMENT_ARRAY); - } - } private void readObject(ObjectInputStream in) throws IOException, @@ -308,19 +304,8 @@ in.defaultReadObject(); int levelInt = in.readInt(); level = Level.toLevel(levelInt); - - int argArrayLen = in.readInt(); - if (argArrayLen != NULL_ARGUMENT_ARRAY) { - argumentArray = new String[argArrayLen]; - for (int i = 0; i < argArrayLen; i++) { - Object val = in.readObject(); - if (!NULL_ARGUMENT_ARRAY_ELEMENT.equals(val)) { - argumentArray[i] = val; - } - } - } } - + @Override public String toString() { StringBuffer sb = new StringBuffer('['); @@ -329,4 +314,5 @@ sb.append("\n"); return sb.toString(); } + } Index: src/test/java/ch/qos/logback/classic/net/LoggingEventSerializationTest.java =================================================================== --- src/test/java/ch/qos/logback/classic/net/LoggingEventSerializationTest.java (revision 0) +++ src/test/java/ch/qos/logback/classic/net/LoggingEventSerializationTest.java (revision 0) @@ -0,0 +1,138 @@ +package ch.qos.logback.classic.net; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggerContextRemoteView; +import ch.qos.logback.classic.spi.LoggerRemoteView; +import ch.qos.logback.classic.spi.LoggingEvent; +import junit.framework.TestCase; +import org.slf4j.MDC; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Map; + +public class LoggingEventSerializationTest extends TestCase { + + LoggerContext lc; + Logger logger; + + ByteArrayOutputStream bos; + ObjectOutputStream oos; + ObjectInputStream inputStream; + + public void setUp() throws Exception { + super.setUp(); + lc = new LoggerContext(); + lc.setName("testContext"); + logger = lc.getLogger(LoggerContext.ROOT_NAME); + } + + public void tearDown() throws Exception { + super.tearDown(); + lc = null; + logger = null; + } + + public void testBasic() throws Exception { + // create the byte output stream + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + + LoggingEvent event = createLoggingEvent(); + oos.writeObject(event); + + // create the input stream based on the ouput stream + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + inputStream = new ObjectInputStream(bis); + + LoggingEvent remoteEvent = (LoggingEvent) inputStream.readObject(); + + assertEquals("test message", remoteEvent.getMessage()); + assertEquals(Level.DEBUG, remoteEvent.getLevel()); + } + + public void testContext() throws Exception { + // create the byte output stream + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + + lc.putProperty("testKey", "testValue"); + LoggingEvent event = createLoggingEvent(); + oos.writeObject(event); + + // create the input stream based on the ouput stream + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + inputStream = new ObjectInputStream(bis); + + LoggingEvent remoteEvent = (LoggingEvent) inputStream.readObject(); + + LoggerRemoteView loggerRemoteView = remoteEvent.getLoggerRemoteView(); + assertNotNull(loggerRemoteView); + assertEquals("root", loggerRemoteView.getName()); + + LoggerContextRemoteView loggerContextRemoteView = loggerRemoteView + .getLoggerContextView(); + assertNotNull(loggerContextRemoteView); + assertEquals("testContext", loggerContextRemoteView.getName()); + Map props = loggerContextRemoteView.getPropertyMap(); + assertNotNull(props); + assertEquals("testValue", props.get("testKey")); + } + + public void testMDC() throws Exception { + // create the byte output stream + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + + MDC.put("key", "testValue"); + LoggingEvent event = createLoggingEvent(); + oos.writeObject(event); + + // create the input stream based on the ouput stream + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + inputStream = new ObjectInputStream(bis); + + LoggingEvent remoteEvent = (LoggingEvent) inputStream.readObject(); + + Map MDCPropertyMap = remoteEvent.getMDCPropertyMap(); + assertEquals("testValue", MDCPropertyMap.get("key")); + } + + public void testUpdatedMDC() throws Exception { + // create the byte output stream + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + + MDC.put("key", "testValue"); + LoggingEvent event1 = createLoggingEvent(); + oos.writeObject(event1); + + MDC.put("key", "updatedTestValue"); + LoggingEvent event2 = createLoggingEvent(); + oos.writeObject(event2); + + // create the input stream based on the ouput stream + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + inputStream = new ObjectInputStream(bis); + + // skip over one object + inputStream.readObject(); + LoggingEvent remoteEvent2 = (LoggingEvent) inputStream.readObject(); + + // We observe the second logging event. It should provide us with + // the updated MDC property. + Map MDCPropertyMap = remoteEvent2.getMDCPropertyMap(); + assertEquals("updatedTestValue", MDCPropertyMap.get("key")); + } + + private LoggingEvent createLoggingEvent() { + LoggingEvent le = new LoggingEvent(this.getClass().getName(), logger, + Level.DEBUG, "test message", null, null); + return le; + } + +}