001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.impl;
018
019import java.io.InvalidObjectException;
020import java.io.ObjectInputStream;
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Objects;
027
028import org.apache.logging.log4j.Level;
029import org.apache.logging.log4j.Marker;
030import org.apache.logging.log4j.ThreadContext;
031import org.apache.logging.log4j.core.LogEvent;
032import org.apache.logging.log4j.core.async.RingBufferLogEvent;
033import org.apache.logging.log4j.core.config.LoggerConfig;
034import org.apache.logging.log4j.core.config.Property;
035import org.apache.logging.log4j.core.util.Clock;
036import org.apache.logging.log4j.core.util.ClockFactory;
037import org.apache.logging.log4j.core.util.DummyNanoClock;
038import org.apache.logging.log4j.core.util.NanoClock;
039import org.apache.logging.log4j.message.LoggerNameAwareMessage;
040import org.apache.logging.log4j.message.Message;
041import org.apache.logging.log4j.message.TimestampMessage;
042import org.apache.logging.log4j.status.StatusLogger;
043import org.apache.logging.log4j.util.Strings;
044
045/**
046 * Implementation of a LogEvent.
047 */
048public class Log4jLogEvent implements LogEvent {
049
050    private static final long serialVersionUID = -1351367343806656055L;
051    private static final Clock CLOCK = ClockFactory.getClock();
052    private static volatile NanoClock nanoClock = new DummyNanoClock();
053    private final String loggerFqcn;
054    private final Marker marker;
055    private final Level level;
056    private final String loggerName;
057    private final Message message;
058    private final long timeMillis;
059    private final transient Throwable thrown;
060    private ThrowableProxy thrownProxy;
061    private final Map<String, String> contextMap;
062    private final ThreadContext.ContextStack contextStack;
063    private String threadName;
064    private StackTraceElement source;
065    private boolean includeLocation;
066    private boolean endOfBatch = false;
067    /** @since Log4J 2.4 */
068    private final transient long nanoTime;
069
070    /** LogEvent Builder helper class. */
071    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
072
073        private String loggerFqcn;
074        private Marker marker;
075        private Level level;
076        private String loggerName;
077        private Message message;
078        private Throwable thrown;
079        private long timeMillis = CLOCK.currentTimeMillis();
080        private ThrowableProxy thrownProxy;
081        private Map<String, String> contextMap = ThreadContext.getImmutableContext();
082        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
083        private String threadName = null;
084        private StackTraceElement source;
085        private boolean includeLocation;
086        private boolean endOfBatch = false;
087        private long nanoTime;
088        
089        public Builder() {
090        }
091        
092        public Builder(LogEvent other) {
093            Objects.requireNonNull(other);
094            if (other instanceof RingBufferLogEvent) {
095                RingBufferLogEvent evt = (RingBufferLogEvent) other;
096                evt.initializeBuilder(this);
097                return;
098            }
099            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
296public 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}