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