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