View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.impl;
18  
19  import java.io.IOException;
20  import java.io.InvalidObjectException;
21  import java.io.ObjectInputStream;
22  import java.io.Serializable;
23  import java.rmi.MarshalledObject;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Objects;
27  
28  import org.apache.logging.log4j.Level;
29  import org.apache.logging.log4j.Marker;
30  import org.apache.logging.log4j.ThreadContext;
31  import org.apache.logging.log4j.core.ContextDataInjector;
32  import org.apache.logging.log4j.util.ReadOnlyStringMap;
33  import org.apache.logging.log4j.core.LogEvent;
34  import org.apache.logging.log4j.core.async.RingBufferLogEvent;
35  import org.apache.logging.log4j.core.config.LoggerConfig;
36  import org.apache.logging.log4j.core.config.Property;
37  import org.apache.logging.log4j.core.util.Clock;
38  import org.apache.logging.log4j.core.util.ClockFactory;
39  import org.apache.logging.log4j.core.util.DummyNanoClock;
40  import org.apache.logging.log4j.core.util.NanoClock;
41  import org.apache.logging.log4j.message.LoggerNameAwareMessage;
42  import org.apache.logging.log4j.message.Message;
43  import org.apache.logging.log4j.message.ReusableMessage;
44  import org.apache.logging.log4j.message.SimpleMessage;
45  import org.apache.logging.log4j.message.TimestampMessage;
46  import org.apache.logging.log4j.util.StackLocatorUtil;
47  import org.apache.logging.log4j.util.StringMap;
48  import org.apache.logging.log4j.status.StatusLogger;
49  import org.apache.logging.log4j.util.Strings;
50  
51  /**
52   * Implementation of a LogEvent.
53   */
54  public class Log4jLogEvent implements LogEvent {
55  
56      private static final long serialVersionUID = -8393305700508709443L;
57      private static final Clock CLOCK = ClockFactory.getClock();
58      private static volatile NanoClock nanoClock = new DummyNanoClock();
59      private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();
60  
61      private final String loggerFqcn;
62      private final Marker marker;
63      private final Level level;
64      private final String loggerName;
65      private Message message;
66      private final long timeMillis;
67      private final transient Throwable thrown;
68      private ThrowableProxy thrownProxy;
69      private final StringMap contextData;
70      private final ThreadContext.ContextStack contextStack;
71      private long threadId;
72      private String threadName;
73      private int threadPriority;
74      private StackTraceElement source;
75      private boolean includeLocation;
76      private boolean endOfBatch = false;
77      /** @since Log4J 2.4 */
78      private final transient long nanoTime;
79  
80      /** LogEvent Builder helper class. */
81      public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
82  
83          private String loggerFqcn;
84          private Marker marker;
85          private Level level;
86          private String loggerName;
87          private Message message;
88          private Throwable thrown;
89          private long timeMillis = CLOCK.currentTimeMillis();
90          private ThrowableProxy thrownProxy;
91          private StringMap contextData = createContextData((List<Property>) null);
92          private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
93          private long threadId;
94          private String threadName;
95          private int threadPriority;
96          private StackTraceElement source;
97          private boolean includeLocation;
98          private boolean endOfBatch = false;
99          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 }