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.util.Strings;
043
044/**
045 * Implementation of a LogEvent.
046 */
047public class Log4jLogEvent implements LogEvent {
048
049    private static final long serialVersionUID = -1351367343806656055L;
050    private static final Clock CLOCK = ClockFactory.getClock();
051    private static volatile NanoClock nanoClock = new DummyNanoClock();
052    private final String loggerFqcn;
053    private final Marker marker;
054    private final Level level;
055    private final String loggerName;
056    private final Message message;
057    private final long timeMillis;
058    private final transient Throwable thrown;
059    private ThrowableProxy thrownProxy;
060    private final Map<String, String> contextMap;
061    private final ThreadContext.ContextStack contextStack;
062    private String threadName;
063    private StackTraceElement source;
064    private boolean includeLocation;
065    private boolean endOfBatch = false;
066    /** @since Log4J 2.4 */
067    private final transient long nanoTime;
068
069    /** LogEvent Builder helper class. */
070    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
071
072        private String loggerFqcn;
073        private Marker marker;
074        private Level level;
075        private String loggerName;
076        private Message message;
077        private Throwable thrown;
078        private long timeMillis = CLOCK.currentTimeMillis();
079        private ThrowableProxy thrownProxy;
080        private Map<String, String> contextMap = ThreadContext.getImmutableContext();
081        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
082        private String threadName = null;
083        private StackTraceElement source;
084        private boolean includeLocation;
085        private boolean endOfBatch = false;
086        private long nanoTime;
087        
088        public Builder() {
089        }
090        
091        public Builder(LogEvent other) {
092            Objects.requireNonNull(other);
093            if (other instanceof RingBufferLogEvent) {
094                RingBufferLogEvent evt = (RingBufferLogEvent) other;
095                evt.initializeBuilder(this);
096                return;
097            }
098            this.loggerFqcn = other.getLoggerFqcn();
099            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
295public 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}