001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.impl;
018
019import java.io.IOException;
020import java.io.InvalidObjectException;
021import java.io.ObjectInputStream;
022import java.io.Serializable;
023import java.rmi.MarshalledObject;
024import java.util.List;
025import java.util.Map;
026import java.util.Objects;
027
028import org.apache.logging.log4j.Level;
029import org.apache.logging.log4j.Marker;
030import org.apache.logging.log4j.ThreadContext;
031import org.apache.logging.log4j.core.ContextDataInjector;
032import org.apache.logging.log4j.util.ReadOnlyStringMap;
033import org.apache.logging.log4j.core.LogEvent;
034import org.apache.logging.log4j.core.async.RingBufferLogEvent;
035import org.apache.logging.log4j.core.config.LoggerConfig;
036import org.apache.logging.log4j.core.config.Property;
037import org.apache.logging.log4j.core.util.Clock;
038import org.apache.logging.log4j.core.util.ClockFactory;
039import org.apache.logging.log4j.core.util.DummyNanoClock;
040import org.apache.logging.log4j.core.util.NanoClock;
041import org.apache.logging.log4j.message.LoggerNameAwareMessage;
042import org.apache.logging.log4j.message.Message;
043import org.apache.logging.log4j.message.ReusableMessage;
044import org.apache.logging.log4j.message.SimpleMessage;
045import org.apache.logging.log4j.message.TimestampMessage;
046import org.apache.logging.log4j.util.StackLocatorUtil;
047import org.apache.logging.log4j.util.StringMap;
048import org.apache.logging.log4j.status.StatusLogger;
049import org.apache.logging.log4j.util.Strings;
050
051/**
052 * Implementation of a LogEvent.
053 */
054public class Log4jLogEvent implements LogEvent {
055
056    private static final long serialVersionUID = -8393305700508709443L;
057    private static final Clock CLOCK = ClockFactory.getClock();
058    private static volatile NanoClock nanoClock = new DummyNanoClock();
059    private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();
060
061    private final String loggerFqcn;
062    private final Marker marker;
063    private final Level level;
064    private final String loggerName;
065    private Message message;
066    private final long timeMillis;
067    private final transient Throwable thrown;
068    private ThrowableProxy thrownProxy;
069    private final StringMap contextData;
070    private final ThreadContext.ContextStack contextStack;
071    private long threadId;
072    private String threadName;
073    private int threadPriority;
074    private StackTraceElement source;
075    private boolean includeLocation;
076    private boolean endOfBatch = false;
077    /** @since Log4J 2.4 */
078    private final transient long nanoTime;
079
080    /** LogEvent Builder helper class. */
081    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
082
083        private String loggerFqcn;
084        private Marker marker;
085        private Level level;
086        private String loggerName;
087        private Message message;
088        private Throwable thrown;
089        private long timeMillis = CLOCK.currentTimeMillis();
090        private ThrowableProxy thrownProxy;
091        private StringMap contextData = createContextData((List<Property>) null);
092        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
093        private long threadId;
094        private String threadName;
095        private int threadPriority;
096        private StackTraceElement source;
097        private boolean includeLocation;
098        private boolean endOfBatch = false;
099        private long nanoTime;
100
101        public Builder() {
102        }
103
104        public Builder(final LogEvent other) {
105            Objects.requireNonNull(other);
106            if (other instanceof RingBufferLogEvent) {
107                ((RingBufferLogEvent) other).initializeBuilder(this);
108                return;
109            }
110            if (other instanceof MutableLogEvent) {
111                ((MutableLogEvent) other).initializeBuilder(this);
112                return;
113            }
114            this.loggerFqcn = other.getLoggerFqcn();
115            this.marker = other.getMarker();
116            this.level = other.getLevel();
117            this.loggerName = other.getLoggerName();
118            this.message = other.getMessage();
119            this.timeMillis = other.getTimeMillis();
120            this.thrown = other.getThrown();
121            this.contextStack = other.getContextStack();
122            this.includeLocation = other.isIncludeLocation();
123            this.endOfBatch = other.isEndOfBatch();
124            this.nanoTime = other.getNanoTime();
125
126            // Avoid unnecessarily initializing thrownProxy, threadName and source if possible
127            if (other instanceof Log4jLogEvent) {
128                final Log4jLogEvent evt = (Log4jLogEvent) other;
129                this.contextData = evt.contextData;
130                this.thrownProxy = evt.thrownProxy;
131                this.source = evt.source;
132                this.threadId = evt.threadId;
133                this.threadName = evt.threadName;
134                this.threadPriority = evt.threadPriority;
135            } else {
136                if (other.getContextData() instanceof StringMap) {
137                    this.contextData = (StringMap) other.getContextData();
138                } else {
139                    if (this.contextData.isFrozen()) {
140                        this.contextData = ContextDataFactory.createContextData();
141                    } else {
142                        this.contextData.clear();
143                    }
144                    this.contextData.putAll(other.getContextData());
145
146                }
147                this.thrownProxy = other.getThrownProxy();
148                this.source = other.getSource();
149                this.threadId = other.getThreadId();
150                this.threadName = other.getThreadName();
151                this.threadPriority = other.getThreadPriority();
152            }
153        }
154
155        public Builder setLevel(final Level level) {
156            this.level = level;
157            return this;
158        }
159
160        public Builder setLoggerFqcn(final String loggerFqcn) {
161            this.loggerFqcn = loggerFqcn;
162            return this;
163        }
164
165        public Builder setLoggerName(final String loggerName) {
166            this.loggerName = loggerName;
167            return this;
168        }
169
170        public Builder setMarker(final Marker marker) {
171            this.marker = marker;
172            return this;
173        }
174
175        public Builder setMessage(final Message message) {
176            this.message = message;
177            return this;
178        }
179
180        public Builder setThrown(final Throwable thrown) {
181            this.thrown = thrown;
182            return this;
183        }
184
185        public Builder setTimeMillis(final long timeMillis) {
186            this.timeMillis = timeMillis;
187            return this;
188        }
189
190        public Builder setThrownProxy(final ThrowableProxy thrownProxy) {
191            this.thrownProxy = thrownProxy;
192            return this;
193        }
194
195        @Deprecated
196        public Builder setContextMap(final Map<String, String> contextMap) {
197            contextData = ContextDataFactory.createContextData(); // replace with new instance
198            if (contextMap != null) {
199                for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
200                    contextData.putValue(entry.getKey(), entry.getValue());
201                }
202            }
203            return this;
204        }
205
206        public Builder setContextData(final StringMap contextData) {
207            this.contextData = contextData;
208            return this;
209        }
210
211        public Builder setContextStack(final ThreadContext.ContextStack contextStack) {
212            this.contextStack = contextStack;
213            return this;
214        }
215
216        public Builder setThreadId(final long threadId) {
217            this.threadId = threadId;
218            return this;
219        }
220
221        public Builder setThreadName(final String threadName) {
222            this.threadName = threadName;
223            return this;
224        }
225
226        public Builder setThreadPriority(final int threadPriority) {
227            this.threadPriority = threadPriority;
228            return this;
229        }
230
231        public Builder setSource(final StackTraceElement source) {
232            this.source = source;
233            return this;
234        }
235
236        public Builder setIncludeLocation(final boolean includeLocation) {
237            this.includeLocation = includeLocation;
238            return this;
239        }
240
241        public Builder setEndOfBatch(final boolean endOfBatch) {
242            this.endOfBatch = endOfBatch;
243            return this;
244        }
245
246        /**
247         * Sets the nano time for the event.
248         * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event
249         *          was created.
250         * @return this builder
251         */
252        public Builder setNanoTime(final long nanoTime) {
253            this.nanoTime = nanoTime;
254            return this;
255        }
256
257        @Override
258        public Log4jLogEvent build() {
259            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
260                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
261                    nanoTime);
262            result.setIncludeLocation(includeLocation);
263            result.setEndOfBatch(endOfBatch);
264            return result;
265        }
266    }
267
268    /**
269     * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty.
270     * @return a new empty builder.
271     */
272    public static Builder newBuilder() {
273        return new Builder();
274    }
275
276    public Log4jLogEvent() {
277        this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
278                0, null, CLOCK.currentTimeMillis(), nanoClock.nanoTime());
279    }
280
281    /**
282    *
283    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
284    */
285   @Deprecated
286   public Log4jLogEvent(final long timestamp) {
287       this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
288               0, null, timestamp, nanoClock.nanoTime());
289   }
290
291   /**
292    * Constructor.
293    * @param loggerName The name of the Logger.
294    * @param marker The Marker or null.
295    * @param loggerFQCN The fully qualified class name of the caller.
296    * @param level The logging Level.
297    * @param message The Message.
298    * @param t A Throwable or null.
299    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
300    */
301   @Deprecated
302   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
303                        final Message message, final Throwable t) {
304       this(loggerName, marker, loggerFQCN, level, message, null, t);
305   }
306
307   /**
308    * Constructor.
309    * @param loggerName The name of the Logger.
310    * @param marker The Marker or null.
311    * @param loggerFQCN The fully qualified class name of the caller.
312    * @param level The logging Level.
313    * @param message The Message.
314    * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap.
315    * @param t A Throwable or null.
316    */
317   // This constructor is called from LogEventFactories.
318   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
319                        final Message message, final List<Property> properties, final Throwable t) {
320       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
321           ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
322           0, // thread name
323           null, // stack trace element
324           0,
325           null, // LOG4J2-628 use log4j.Clock for timestamps
326           // LOG4J2-744 unless TimestampMessage already has one
327           message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :
328               CLOCK.currentTimeMillis(), nanoClock.nanoTime());
329   }
330
331   /**
332    * Constructor.
333    * @param loggerName The name of the Logger.
334    * @param marker The Marker or null.
335    * @param loggerFQCN The fully qualified class name of the caller.
336    * @param level The logging Level.
337    * @param message The Message.
338    * @param t A Throwable or null.
339    * @param mdc The mapped diagnostic context.
340    * @param ndc the nested diagnostic context.
341    * @param threadName The name of the thread.
342    * @param location The locations of the caller.
343    * @param timestampMillis The timestamp of the event.
344    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
345    */
346   @Deprecated
347   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
348                        final Message message, final Throwable t, final Map<String, String> mdc,
349                        final ThreadContext.ContextStack ndc, final String threadName,
350                        final StackTraceElement location, final long timestampMillis) {
351       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0,
352               threadName, 0, location, timestampMillis, nanoClock.nanoTime());
353   }
354
355   /**
356    * Create a new LogEvent.
357    * @param loggerName The name of the Logger.
358    * @param marker The Marker or null.
359    * @param loggerFQCN The fully qualified class name of the caller.
360    * @param level The logging Level.
361    * @param message The Message.
362    * @param thrown A Throwable or null.
363    * @param thrownProxy A ThrowableProxy or null.
364    * @param mdc The mapped diagnostic context.
365    * @param ndc the nested diagnostic context.
366    * @param threadName The name of the thread.
367    * @param location The locations of the caller.
368    * @param timestamp The timestamp of the event.
369    * @return a new LogEvent
370    * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release.
371    */
372    @Deprecated
373    public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
374                                            final Level level, final Message message, final Throwable thrown,
375                                            final ThrowableProxy thrownProxy,
376                                            final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
377                                            final String threadName, final StackTraceElement location,
378                                            final long timestamp) {
379        final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
380                thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, nanoClock.nanoTime());
381        return result;
382    }
383
384    /**
385     * Constructor.
386     * @param loggerName The name of the Logger.
387     * @param marker The Marker or null.
388     * @param loggerFQCN The fully qualified class name of the caller.
389     * @param level The logging Level.
390     * @param message The Message.
391     * @param thrown A Throwable or null.
392     * @param thrownProxy A ThrowableProxy or null.
393     * @param contextData The key-value pairs from the context.
394     * @param contextStack the nested diagnostic context.
395     * @param threadId the thread ID
396     * @param threadName The name of the thread.
397     * @param threadPriority the thread priority
398     * @param source The locations of the caller.
399     * @param timestampMillis The timestamp of the event.
400     * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
401     *          created.
402     */
403    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
404            final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
405            final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
406            final String threadName, final int threadPriority, final StackTraceElement source,
407            final long timestampMillis, final long nanoTime) {
408        this.loggerName = loggerName;
409        this.marker = marker;
410        this.loggerFqcn = loggerFQCN;
411        this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
412        this.message = message;
413        this.thrown = thrown;
414        this.thrownProxy = thrownProxy;
415        this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
416        this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
417        this.timeMillis = message instanceof TimestampMessage
418                ? ((TimestampMessage) message).getTimestamp()
419                : timestampMillis;
420        this.threadId = threadId;
421        this.threadName = threadName;
422        this.threadPriority = threadPriority;
423        this.source = source;
424        if (message != null && message instanceof LoggerNameAwareMessage) {
425            ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
426        }
427        this.nanoTime = nanoTime;
428    }
429
430    private static StringMap createContextData(final Map<String, String> contextMap) {
431        final StringMap result = ContextDataFactory.createContextData();
432        if (contextMap != null) {
433            for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
434                result.putValue(entry.getKey(), entry.getValue());
435            }
436        }
437        return result;
438    }
439
440    private static StringMap createContextData(final List<Property> properties) {
441        final StringMap reusable = ContextDataFactory.createContextData();
442        return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
443    }
444
445    /**
446     * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
447     * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
448     */
449    public static NanoClock getNanoClock() {
450        return nanoClock;
451    }
452
453    /**
454     * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
455     * <p>
456     * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
457     * configuration changes.
458     *
459     * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
460     */
461    public static void setNanoClock(final NanoClock nanoClock) {
462        Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
463        StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
464    }
465
466    /**
467     * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event.
468     * @return a new fully initialized builder.
469     */
470    public Builder asBuilder() {
471        return new Builder(this);
472    }
473
474    @Override
475    public Log4jLogEvent toImmutable() {
476        if (getMessage() instanceof ReusableMessage) {
477            makeMessageImmutable();
478        }
479        return this;
480    }
481
482    /**
483     * Returns the logging Level.
484     * @return the Level associated with this event.
485     */
486    @Override
487    public Level getLevel() {
488        return level;
489    }
490
491    /**
492     * Returns the name of the Logger used to generate the event.
493     * @return The Logger name.
494     */
495    @Override
496    public String getLoggerName() {
497        return loggerName;
498    }
499
500    /**
501     * Returns the Message associated with the event.
502     * @return The Message.
503     */
504    @Override
505    public Message getMessage() {
506        return message;
507    }
508
509    public void makeMessageImmutable() {
510        message = new SimpleMessage(message.getFormattedMessage());
511    }
512
513    @Override
514    public long getThreadId() {
515        if (threadId == 0) {
516            threadId = Thread.currentThread().getId();
517        }
518        return threadId;
519    }
520
521    /**
522     * Returns the name of the Thread on which the event was generated.
523     * @return The name of the Thread.
524     */
525    @Override
526    public String getThreadName() {
527        if (threadName == null) {
528            threadName = Thread.currentThread().getName();
529        }
530        return threadName;
531    }
532
533    @Override
534    public int getThreadPriority() {
535        if (threadPriority == 0) {
536            threadPriority = Thread.currentThread().getPriority();
537        }
538        return threadPriority;
539    }
540
541    /**
542     * Returns the time in milliseconds from the epoch when the event occurred.
543     * @return The time the event occurred.
544     */
545    @Override
546    public long getTimeMillis() {
547        return timeMillis;
548    }
549
550    /**
551     * Returns the Throwable associated with the event, or null.
552     * @return The Throwable associated with the event.
553     */
554    @Override
555    public Throwable getThrown() {
556        return thrown;
557    }
558
559    /**
560     * Returns the ThrowableProxy associated with the event, or null.
561     * @return The ThrowableProxy associated with the event.
562     */
563    @Override
564    public ThrowableProxy getThrownProxy() {
565        if (thrownProxy == null && thrown != null) {
566            thrownProxy = new ThrowableProxy(thrown);
567        }
568        return thrownProxy;
569    }
570
571
572    /**
573     * Returns the Marker associated with the event, or null.
574     * @return the Marker associated with the event.
575     */
576    @Override
577    public Marker getMarker() {
578        return marker;
579    }
580
581    /**
582     * The fully qualified class name of the class that was called by the caller.
583     * @return the fully qualified class name of the class that is performing logging.
584     */
585    @Override
586    public String getLoggerFqcn() {
587        return loggerFqcn;
588    }
589
590    /**
591     * Returns the {@code ReadOnlyStringMap} containing context data key-value pairs.
592     * @return the {@code ReadOnlyStringMap} containing context data key-value pairs
593     * @since 2.7
594     */
595    @Override
596    public ReadOnlyStringMap getContextData() {
597        return contextData;
598    }
599    /**
600     * Returns the immutable copy of the ThreadContext Map.
601     * @return The context Map.
602     */
603    @Override
604    public Map<String, String> getContextMap() {
605        return contextData.toMap();
606    }
607
608    /**
609     * Returns an immutable copy of the ThreadContext stack.
610     * @return The context Stack.
611     */
612    @Override
613    public ThreadContext.ContextStack getContextStack() {
614        return contextStack;
615    }
616
617    /**
618     * Returns the StackTraceElement for the caller. This will be the entry that occurs right
619     * before the first occurrence of FQCN as a class name.
620     * @return the StackTraceElement for the caller.
621     */
622    @Override
623    public StackTraceElement getSource() {
624        if (source != null) {
625            return source;
626        }
627        if (loggerFqcn == null || !includeLocation) {
628            return null;
629        }
630        source = StackLocatorUtil.calcLocation(loggerFqcn);
631        return source;
632    }
633
634    @Override
635    public boolean isIncludeLocation() {
636        return includeLocation;
637    }
638
639    @Override
640    public void setIncludeLocation(final boolean includeLocation) {
641        this.includeLocation = includeLocation;
642    }
643
644    @Override
645    public boolean isEndOfBatch() {
646        return endOfBatch;
647    }
648
649    @Override
650    public void setEndOfBatch(final boolean endOfBatch) {
651        this.endOfBatch = endOfBatch;
652    }
653
654    @Override
655    public long getNanoTime() {
656        return nanoTime;
657    }
658
659    /**
660     * Creates a LogEventProxy that can be serialized.
661     * @return a LogEventProxy.
662     */
663    protected Object writeReplace() {
664        getThrownProxy(); // ensure ThrowableProxy is initialized
665        return new LogEventProxy(this, this.includeLocation);
666    }
667
668    /**
669     * Take a snapshot of the specified {@code LogEvent}.
670     *
671     * @param event the event to take a snapshot of
672     * @param includeLocation if true, this method will obtain caller location information
673     * @return snapshot of the event as a {@code Serializable} object
674     * @see #deserialize(Serializable)
675     * @see #serialize(Log4jLogEvent, boolean)
676     */
677    public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
678        if (event instanceof Log4jLogEvent) {
679            event.getThrownProxy(); // ensure ThrowableProxy is initialized
680            return new LogEventProxy((Log4jLogEvent) event, includeLocation);
681        }
682        return new LogEventProxy(event, includeLocation);
683    }
684
685    /**
686     * Take a snapshot of the specified {@code Log4jLogEvent}.
687     *
688     * @param event the event to take a snapshot of
689     * @param includeLocation if true, this method will obtain caller location information
690     * @return snapshot of the event as a {@code Serializable} object
691     * @see #deserialize(Serializable)
692     * @see #serialize(LogEvent, boolean)
693     */
694    public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
695        event.getThrownProxy(); // ensure ThrowableProxy is initialized
696        return new LogEventProxy(event, includeLocation);
697    }
698
699    public static boolean canDeserialize(final Serializable event) {
700        return event instanceof LogEventProxy;
701    }
702
703    public static Log4jLogEvent deserialize(final Serializable event) {
704        Objects.requireNonNull(event, "Event cannot be null");
705        if (event instanceof LogEventProxy) {
706            final LogEventProxy proxy = (LogEventProxy) event;
707            final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
708                    proxy.loggerFQCN, proxy.level, proxy.message,
709                    proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId,
710                    proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoTime);
711            result.setEndOfBatch(proxy.isEndOfBatch);
712            result.setIncludeLocation(proxy.isLocationRequired);
713            return result;
714        }
715        throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
716    }
717
718    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
719        throw new InvalidObjectException("Proxy required");
720    }
721
722    public static LogEvent createMemento(final LogEvent logEvent) {
723        return new Log4jLogEvent.Builder(logEvent).build();
724    }
725    
726    /**
727     * Creates and returns a new immutable copy of this {@code Log4jLogEvent}.
728     *
729     * @return a new immutable copy of the data in this {@code Log4jLogEvent}
730     */
731    public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
732        return deserialize(serialize(event, includeLocation));
733    }
734
735    @Override
736    public String toString() {
737        final StringBuilder sb = new StringBuilder();
738        final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
739        sb.append("Logger=").append(n);
740        sb.append(" Level=").append(level.name());
741        sb.append(" Message=").append(message == null ? null : message.getFormattedMessage());
742        return sb.toString();
743    }
744
745    @Override
746    public boolean equals(final Object o) {
747        if (this == o) {
748            return true;
749        }
750        if (o == null || getClass() != o.getClass()) {
751            return false;
752        }
753
754        final Log4jLogEvent that = (Log4jLogEvent) o;
755
756        if (endOfBatch != that.endOfBatch) {
757            return false;
758        }
759        if (includeLocation != that.includeLocation) {
760            return false;
761        }
762        if (timeMillis != that.timeMillis) {
763            return false;
764        }
765        if (nanoTime != that.nanoTime) {
766            return false;
767        }
768        if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
769            return false;
770        }
771        if (level != null ? !level.equals(that.level) : that.level != null) {
772            return false;
773        }
774        if (source != null ? !source.equals(that.source) : that.source != null) {
775            return false;
776        }
777        if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
778            return false;
779        }
780        if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) {
781            return false;
782        }
783        if (!message.equals(that.message)) {
784            return false;
785        }
786        if (!loggerName.equals(that.loggerName)) {
787            return false;
788        }
789        if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
790            return false;
791        }
792        if (threadId != that.threadId) {
793            return false;
794        }
795        if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
796            return false;
797        }
798        if (threadPriority != that.threadPriority) {
799            return false;
800        }
801        if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
802            return false;
803        }
804        if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
805            return false;
806        }
807
808        return true;
809    }
810
811    @Override
812    public int hashCode() {
813        // Check:OFF: MagicNumber
814        int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
815        result = 31 * result + (marker != null ? marker.hashCode() : 0);
816        result = 31 * result + (level != null ? level.hashCode() : 0);
817        result = 31 * result + loggerName.hashCode();
818        result = 31 * result + message.hashCode();
819        result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32));
820        result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
821        result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
822        result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
823        result = 31 * result + (contextData != null ? contextData.hashCode() : 0);
824        result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
825        result = 31 * result + (int) (threadId ^ (threadId >>> 32));
826        result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
827        result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
828        result = 31 * result + (source != null ? source.hashCode() : 0);
829        result = 31 * result + (includeLocation ? 1 : 0);
830        result = 31 * result + (endOfBatch ? 1 : 0);
831        // Check:ON: MagicNumber
832        return result;
833    }
834
835    /**
836     * Proxy pattern used to serialize the LogEvent.
837     */
838    static class LogEventProxy implements Serializable {
839
840        private static final long serialVersionUID = -8634075037355293699L;
841        private final String loggerFQCN;
842        private final Marker marker;
843        private final Level level;
844        private final String loggerName;
845        // transient since 2.8
846        private final transient Message message;
847        /** since 2.8 */
848        private MarshalledObject<Message> marshalledMessage;
849        /** since 2.8 */
850        private String messageString;
851        private final long timeMillis;
852        private final transient Throwable thrown;
853        private final ThrowableProxy thrownProxy;
854        /** @since 2.7 */
855        private final StringMap contextData;
856        private final ThreadContext.ContextStack contextStack;
857        /** @since 2.6 */
858        private final long threadId;
859        private final String threadName;
860        /** @since 2.6 */
861        private final int threadPriority;
862        private final StackTraceElement source;
863        private final boolean isLocationRequired;
864        private final boolean isEndOfBatch;
865        /** @since 2.4 */
866        private final transient long nanoTime;
867
868        public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
869            this.loggerFQCN = event.loggerFqcn;
870            this.marker = event.marker;
871            this.level = event.level;
872            this.loggerName = event.loggerName;
873            this.message = event.message instanceof ReusableMessage
874                    ? memento((ReusableMessage) event.message)
875                    : event.message;
876            this.timeMillis = event.timeMillis;
877            this.thrown = event.thrown;
878            this.thrownProxy = event.thrownProxy;
879            this.contextData = event.contextData;
880            this.contextStack = event.contextStack;
881            this.source = includeLocation ? event.getSource() : null;
882            this.threadId = event.getThreadId();
883            this.threadName = event.getThreadName();
884            this.threadPriority = event.getThreadPriority();
885            this.isLocationRequired = includeLocation;
886            this.isEndOfBatch = event.endOfBatch;
887            this.nanoTime = event.nanoTime;
888        }
889
890        public LogEventProxy(final LogEvent event, final boolean includeLocation) {
891            this.loggerFQCN = event.getLoggerFqcn();
892            this.marker = event.getMarker();
893            this.level = event.getLevel();
894            this.loggerName = event.getLoggerName();
895
896            final Message temp = event.getMessage();
897            message = temp instanceof ReusableMessage
898                    ? memento((ReusableMessage) temp)
899                    : temp;
900            this.timeMillis = event.getTimeMillis();
901            this.thrown = event.getThrown();
902            this.thrownProxy = event.getThrownProxy();
903            this.contextData = memento(event.getContextData());
904            this.contextStack = event.getContextStack();
905            this.source = includeLocation ? event.getSource() : null;
906            this.threadId = event.getThreadId();
907            this.threadName = event.getThreadName();
908            this.threadPriority = event.getThreadPriority();
909            this.isLocationRequired = includeLocation;
910            this.isEndOfBatch = event.isEndOfBatch();
911            this.nanoTime = event.getNanoTime();
912        }
913
914        private static Message memento(final ReusableMessage message) {
915            return message.memento();
916        }
917
918        private static StringMap memento(final ReadOnlyStringMap data) {
919            final StringMap result = ContextDataFactory.createContextData();
920            result.putAll(data);
921            return result;
922        }
923
924        private static MarshalledObject<Message> marshall(final Message msg) {
925            try {
926                return new MarshalledObject<>(msg);
927            } catch (final Exception ex) {
928                return null;
929            }
930        }
931
932        private void writeObject(final java.io.ObjectOutputStream s) throws IOException {
933            this.messageString = message.getFormattedMessage();
934            this.marshalledMessage = marshall(message);
935            s.defaultWriteObject();
936        }
937
938        /**
939         * Returns a Log4jLogEvent using the data in the proxy.
940         * @return Log4jLogEvent.
941         */
942        protected Object readResolve() {
943            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown,
944                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
945                    nanoTime);
946            result.setEndOfBatch(isEndOfBatch);
947            result.setIncludeLocation(isLocationRequired);
948            return result;
949        }
950
951        private Message message() {
952            if (marshalledMessage != null) {
953                try {
954                    return marshalledMessage.get();
955                } catch (final Exception ex) {
956                }
957            }
958            return new SimpleMessage(messageString);
959        }
960    }
961
962}