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