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