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