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