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.IOException;
020import java.io.InvalidObjectException;
021import java.io.ObjectInputStream;
022import java.io.Serializable;
023import java.rmi.MarshalledObject;
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.ContextDataInjector;
032import org.apache.logging.log4j.core.util.*;
033import org.apache.logging.log4j.core.time.Instant;
034import org.apache.logging.log4j.core.time.MutableInstant;
035import org.apache.logging.log4j.util.ReadOnlyStringMap;
036import org.apache.logging.log4j.core.LogEvent;
037import org.apache.logging.log4j.core.async.RingBufferLogEvent;
038import org.apache.logging.log4j.core.config.LoggerConfig;
039import org.apache.logging.log4j.core.config.Property;
040import org.apache.logging.log4j.message.LoggerNameAwareMessage;
041import org.apache.logging.log4j.message.Message;
042import org.apache.logging.log4j.message.ReusableMessage;
043import org.apache.logging.log4j.message.SimpleMessage;
044import org.apache.logging.log4j.message.TimestampMessage;
045import org.apache.logging.log4j.util.StackLocatorUtil;
046import org.apache.logging.log4j.util.StringMap;
047import org.apache.logging.log4j.status.StatusLogger;
048import org.apache.logging.log4j.util.Strings;
049
050/**
051 * Implementation of a LogEvent.
052 */
053public class Log4jLogEvent implements LogEvent {
054
055    private static final long serialVersionUID = -8393305700508709443L;
056    private static final Clock CLOCK = ClockFactory.getClock();
057    private static volatile NanoClock nanoClock = new DummyNanoClock();
058    private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();
059
060    private final String loggerFqcn;
061    private final Marker marker;
062    private final Level level;
063    private final String loggerName;
064    private Message message;
065    private final MutableInstant instant = new MutableInstant();
066    private final transient Throwable thrown;
067    private ThrowableProxy thrownProxy;
068    private final StringMap contextData;
069    private final ThreadContext.ContextStack contextStack;
070    private long threadId;
071    private String threadName;
072    private int threadPriority;
073    private StackTraceElement source;
074    private boolean includeLocation;
075    private boolean endOfBatch = false;
076    /** @since Log4J 2.4 */
077    private final transient long nanoTime;
078
079    /** LogEvent Builder helper class. */
080    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
081
082        private String loggerFqcn;
083        private Marker marker;
084        private Level level;
085        private String loggerName;
086        private Message message;
087        private Throwable thrown;
088        private final MutableInstant instant = new MutableInstant();
089        private ThrowableProxy thrownProxy;
090        private StringMap contextData = createContextData((List<Property>) null);
091        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
092        private long threadId;
093        private String threadName;
094        private int threadPriority;
095        private StackTraceElement source;
096        private boolean includeLocation;
097        private boolean endOfBatch = false;
098        private long nanoTime;
099
100        public Builder() {
101        }
102
103        public Builder(final LogEvent other) {
104            Objects.requireNonNull(other);
105            if (other instanceof RingBufferLogEvent) {
106                ((RingBufferLogEvent) other).initializeBuilder(this);
107                return;
108            }
109            if (other instanceof MutableLogEvent) {
110                ((MutableLogEvent) other).initializeBuilder(this);
111                return;
112            }
113            this.loggerFqcn = other.getLoggerFqcn();
114            this.marker = other.getMarker();
115            this.level = other.getLevel();
116            this.loggerName = other.getLoggerName();
117            this.message = other.getMessage();
118            this.instant.initFrom(other.getInstant());
119            this.thrown = other.getThrown();
120            this.contextStack = other.getContextStack();
121            this.includeLocation = other.isIncludeLocation();
122            this.endOfBatch = other.isEndOfBatch();
123            this.nanoTime = other.getNanoTime();
124
125            // Avoid unnecessarily initializing thrownProxy, threadName and source if possible
126            if (other instanceof Log4jLogEvent) {
127                final Log4jLogEvent evt = (Log4jLogEvent) other;
128                this.contextData = evt.contextData;
129                this.thrownProxy = evt.thrownProxy;
130                this.source = evt.source;
131                this.threadId = evt.threadId;
132                this.threadName = evt.threadName;
133                this.threadPriority = evt.threadPriority;
134            } else {
135                if (other.getContextData() instanceof StringMap) {
136                    this.contextData = (StringMap) other.getContextData();
137                } else {
138                    if (this.contextData.isFrozen()) {
139                        this.contextData = ContextDataFactory.createContextData();
140                    } else {
141                        this.contextData.clear();
142                    }
143                    this.contextData.putAll(other.getContextData());
144
145                }
146                this.thrownProxy = other.getThrownProxy();
147                this.source = other.getSource();
148                this.threadId = other.getThreadId();
149                this.threadName = other.getThreadName();
150                this.threadPriority = other.getThreadPriority();
151            }
152        }
153
154        public Builder setLevel(final Level level) {
155            this.level = level;
156            return this;
157        }
158
159        public Builder setLoggerFqcn(final String loggerFqcn) {
160            this.loggerFqcn = loggerFqcn;
161            return this;
162        }
163
164        public Builder setLoggerName(final String loggerName) {
165            this.loggerName = loggerName;
166            return this;
167        }
168
169        public Builder setMarker(final Marker marker) {
170            this.marker = marker;
171            return this;
172        }
173
174        public Builder setMessage(final Message message) {
175            this.message = message;
176            return this;
177        }
178
179        public Builder setThrown(final Throwable thrown) {
180            this.thrown = thrown;
181            return this;
182        }
183
184        public Builder setTimeMillis(final long timeMillis) {
185            this.instant.initFromEpochMilli(timeMillis, 0);
186            return this;
187        }
188
189        public Builder setInstant(final Instant instant) {
190            this.instant.initFrom(instant);
191            return this;
192        }
193
194        public Builder setThrownProxy(final ThrowableProxy thrownProxy) {
195            this.thrownProxy = thrownProxy;
196            return this;
197        }
198
199        @Deprecated
200        public Builder setContextMap(final Map<String, String> contextMap) {
201            contextData = ContextDataFactory.createContextData(); // replace with new instance
202            if (contextMap != null) {
203                for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
204                    contextData.putValue(entry.getKey(), entry.getValue());
205                }
206            }
207            return this;
208        }
209
210        public Builder setContextData(final StringMap contextData) {
211            this.contextData = contextData;
212            return this;
213        }
214
215        public Builder setContextStack(final ThreadContext.ContextStack contextStack) {
216            this.contextStack = contextStack;
217            return this;
218        }
219
220        public Builder setThreadId(final long threadId) {
221            this.threadId = threadId;
222            return this;
223        }
224
225        public Builder setThreadName(final String threadName) {
226            this.threadName = threadName;
227            return this;
228        }
229
230        public Builder setThreadPriority(final int threadPriority) {
231            this.threadPriority = threadPriority;
232            return this;
233        }
234
235        public Builder setSource(final StackTraceElement source) {
236            this.source = source;
237            return this;
238        }
239
240        public Builder setIncludeLocation(final boolean includeLocation) {
241            this.includeLocation = includeLocation;
242            return this;
243        }
244
245        public Builder setEndOfBatch(final boolean endOfBatch) {
246            this.endOfBatch = endOfBatch;
247            return this;
248        }
249
250        /**
251         * Sets the nano time for the event.
252         * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event
253         *          was created.
254         * @return this builder
255         */
256        public Builder setNanoTime(final long nanoTime) {
257            this.nanoTime = nanoTime;
258            return this;
259        }
260
261        @Override
262        public Log4jLogEvent build() {
263            initTimeFields();
264            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
265                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source,
266                    instant.getEpochMillisecond(), instant.getNanoOfMillisecond(), nanoTime);
267            result.setIncludeLocation(includeLocation);
268            result.setEndOfBatch(endOfBatch);
269            return result;
270        }
271
272        private void initTimeFields() {
273            if (instant.getEpochMillisecond() == 0) {
274                instant.initFrom(CLOCK);
275            }
276        }
277    }
278
279    /**
280     * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty.
281     * @return a new empty builder.
282     */
283    public static Builder newBuilder() {
284        return new Builder();
285    }
286
287    public Log4jLogEvent() {
288        this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
289                0, null, CLOCK, nanoClock.nanoTime());
290    }
291
292    /**
293    *
294    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
295    */
296   @Deprecated
297   public Log4jLogEvent(final long timestamp) {
298       this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
299               0, null, timestamp, 0, nanoClock.nanoTime());
300   }
301
302   /**
303    * Constructor.
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 t A Throwable or null.
310    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
311    */
312   @Deprecated
313   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
314                        final Message message, final Throwable t) {
315       this(loggerName, marker, loggerFQCN, level, message, null, t);
316   }
317
318   /**
319    * Constructor.
320    * @param loggerName The name of the Logger.
321    * @param marker The Marker or null.
322    * @param loggerFQCN The fully qualified class name of the caller.
323    * @param level The logging Level.
324    * @param message The Message.
325    * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap.
326    * @param t A Throwable or null.
327    */
328   // This constructor is called from LogEventFactories.
329   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
330                        final Message message, final List<Property> properties, final Throwable t) {
331       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
332           ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
333           0, // thread id
334           null, // thread name
335           0, // thread priority
336           null, // StackTraceElement source
337           CLOCK, //
338           nanoClock.nanoTime());
339   }
340
341    /**
342     * Constructor.
343     * @param loggerName The name of the Logger.
344     * @param marker The Marker or null.
345     * @param loggerFQCN The fully qualified class name of the caller.
346     * @param level The logging Level.
347     * @param message The Message.
348     * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap.
349     * @param t A Throwable or null.
350     */
351    // This constructor is called from LogEventFactories.
352    public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN,
353        final StackTraceElement source, final Level level, final Message message, final List<Property> properties,
354        final Throwable t) {
355        this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
356            ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
357            0, // thread id
358            null, // thread name
359            0, // thread priority
360            source, // StackTraceElement source
361            CLOCK, //
362            nanoClock.nanoTime());
363    }
364
365   /**
366    * Constructor.
367    * @param loggerName The name of the Logger.
368    * @param marker The Marker or null.
369    * @param loggerFQCN The fully qualified class name of the caller.
370    * @param level The logging Level.
371    * @param message The Message.
372    * @param t A Throwable or null.
373    * @param mdc The mapped diagnostic context.
374    * @param ndc the nested diagnostic context.
375    * @param threadName The name of the thread.
376    * @param location The locations of the caller.
377    * @param timestampMillis The timestamp of the event.
378    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
379    */
380   @Deprecated
381   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
382                        final Message message, final Throwable t, final Map<String, String> mdc,
383                        final ThreadContext.ContextStack ndc, final String threadName,
384                        final StackTraceElement location, final long timestampMillis) {
385       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0,
386               threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime());
387   }
388
389   /**
390    * Create a new LogEvent.
391    * @param loggerName The name of the Logger.
392    * @param marker The Marker or null.
393    * @param loggerFQCN The fully qualified class name of the caller.
394    * @param level The logging Level.
395    * @param message The Message.
396    * @param thrown A Throwable or null.
397    * @param thrownProxy A ThrowableProxy or null.
398    * @param mdc The mapped diagnostic context.
399    * @param ndc the nested diagnostic context.
400    * @param threadName The name of the thread.
401    * @param location The locations of the caller.
402    * @param timestamp The timestamp of the event.
403    * @return a new LogEvent
404    * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release.
405    */
406    @Deprecated
407    public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
408                                            final Level level, final Message message, final Throwable thrown,
409                                            final ThrowableProxy thrownProxy,
410                                            final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
411                                            final String threadName, final StackTraceElement location,
412                                            final long timestamp) {
413        final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
414                thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime());
415        return result;
416    }
417
418    /**
419     * Constructor.
420     * @param loggerName The name of the Logger.
421     * @param marker The Marker or null.
422     * @param loggerFQCN The fully qualified class name of the caller.
423     * @param level The logging Level.
424     * @param message The Message.
425     * @param thrown A Throwable or null.
426     * @param thrownProxy A ThrowableProxy or null.
427     * @param contextData The key-value pairs from the context.
428     * @param contextStack the nested diagnostic context.
429     * @param threadId the thread ID
430     * @param threadName The name of the thread.
431     * @param threadPriority the thread priority
432     * @param source The locations of the caller.
433     * @param timestampMillis The timestamp of the event.
434     * @param nanoOfMillisecond the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999}
435     * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
436     *          created.
437     */
438    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
439            final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
440            final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
441            final String threadName, final int threadPriority, final StackTraceElement source,
442            final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) {
443        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
444        final long millis = message instanceof TimestampMessage
445                ? ((TimestampMessage) message).getTimestamp()
446                : timestampMillis;
447        instant.initFromEpochMilli(millis, nanoOfMillisecond);
448    }
449    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
450                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
451                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
452                          final String threadName, final int threadPriority, final StackTraceElement source,
453                          final Clock clock, final long nanoTime) {
454        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
455        if (message instanceof TimestampMessage) {
456            instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
457        } else {
458            instant.initFrom(clock);
459        }
460    }
461    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
462                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
463                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
464                          final String threadName, final int threadPriority, final StackTraceElement source,
465                          final long nanoTime) {
466        this.loggerName = loggerName;
467        this.marker = marker;
468        this.loggerFqcn = loggerFQCN;
469        this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
470        this.message = message;
471        this.thrown = thrown;
472        this.thrownProxy = thrownProxy;
473        this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
474        this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
475        this.threadId = threadId;
476        this.threadName = threadName;
477        this.threadPriority = threadPriority;
478        this.source = source;
479        if (message instanceof LoggerNameAwareMessage) {
480            ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
481        }
482        this.nanoTime = nanoTime;
483    }
484
485    private static StringMap createContextData(final Map<String, String> contextMap) {
486        final StringMap result = ContextDataFactory.createContextData();
487        if (contextMap != null) {
488            for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
489                result.putValue(entry.getKey(), entry.getValue());
490            }
491        }
492        return result;
493    }
494
495    private static StringMap createContextData(final List<Property> properties) {
496        final StringMap reusable = ContextDataFactory.createContextData();
497        return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
498    }
499
500    /**
501     * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
502     * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
503     */
504    public static NanoClock getNanoClock() {
505        return nanoClock;
506    }
507
508    /**
509     * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
510     * <p>
511     * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
512     * configuration changes.
513     *
514     * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
515     */
516    public static void setNanoClock(final NanoClock nanoClock) {
517        Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
518        StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
519    }
520
521    /**
522     * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event.
523     * @return a new fully initialized builder.
524     */
525    public Builder asBuilder() {
526        return new Builder(this);
527    }
528
529    @Override
530    public Log4jLogEvent toImmutable() {
531        if (getMessage() instanceof ReusableMessage) {
532            makeMessageImmutable();
533        }
534        return this;
535    }
536
537    /**
538     * Returns the logging Level.
539     * @return the Level associated with this event.
540     */
541    @Override
542    public Level getLevel() {
543        return level;
544    }
545
546    /**
547     * Returns the name of the Logger used to generate the event.
548     * @return The Logger name.
549     */
550    @Override
551    public String getLoggerName() {
552        return loggerName;
553    }
554
555    /**
556     * Returns the Message associated with the event.
557     * @return The Message.
558     */
559    @Override
560    public Message getMessage() {
561        return message;
562    }
563
564    public void makeMessageImmutable() {
565        message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters());
566    }
567
568    @Override
569    public long getThreadId() {
570        if (threadId == 0) {
571            threadId = Thread.currentThread().getId();
572        }
573        return threadId;
574    }
575
576    /**
577     * Returns the name of the Thread on which the event was generated.
578     * @return The name of the Thread.
579     */
580    @Override
581    public String getThreadName() {
582        if (threadName == null) {
583            threadName = Thread.currentThread().getName();
584        }
585        return threadName;
586    }
587
588    @Override
589    public int getThreadPriority() {
590        if (threadPriority == 0) {
591            threadPriority = Thread.currentThread().getPriority();
592        }
593        return threadPriority;
594    }
595
596    /**
597     * {@inheritDoc}
598     */
599    @Override
600    public long getTimeMillis() {
601        return instant.getEpochMillisecond();
602    }
603
604    /**
605     * {@inheritDoc}
606     * @since 2.11
607     */
608    @Override
609    public Instant getInstant() {
610        return instant;
611    }
612
613    /**
614     * Returns the Throwable associated with the event, or null.
615     * @return The Throwable associated with the event.
616     */
617    @Override
618    public Throwable getThrown() {
619        return thrown;
620    }
621
622    /**
623     * Returns the ThrowableProxy associated with the event, or null.
624     * @return The ThrowableProxy associated with the event.
625     */
626    @Override
627    public ThrowableProxy getThrownProxy() {
628        if (thrownProxy == null && thrown != null) {
629            thrownProxy = new ThrowableProxy(thrown);
630        }
631        return thrownProxy;
632    }
633
634
635    /**
636     * Returns the Marker associated with the event, or null.
637     * @return the Marker associated with the event.
638     */
639    @Override
640    public Marker getMarker() {
641        return marker;
642    }
643
644    /**
645     * The fully qualified class name of the class that was called by the caller.
646     * @return the fully qualified class name of the class that is performing logging.
647     */
648    @Override
649    public String getLoggerFqcn() {
650        return loggerFqcn;
651    }
652
653    /**
654     * Returns the {@code ReadOnlyStringMap} containing context data key-value pairs.
655     * @return the {@code ReadOnlyStringMap} containing context data key-value pairs
656     * @since 2.7
657     */
658    @Override
659    public ReadOnlyStringMap getContextData() {
660        return contextData;
661    }
662    /**
663     * Returns the immutable copy of the ThreadContext Map.
664     * @return The context Map.
665     */
666    @Override
667    public Map<String, String> getContextMap() {
668        return contextData.toMap();
669    }
670
671    /**
672     * Returns an immutable copy of the ThreadContext stack.
673     * @return The context Stack.
674     */
675    @Override
676    public ThreadContext.ContextStack getContextStack() {
677        return contextStack;
678    }
679
680    /**
681     * Returns the StackTraceElement for the caller. This will be the entry that occurs right
682     * before the first occurrence of FQCN as a class name.
683     * @return the StackTraceElement for the caller.
684     */
685    @Override
686    public StackTraceElement getSource() {
687        if (source != null) {
688            return source;
689        }
690        if (loggerFqcn == null || !includeLocation) {
691            return null;
692        }
693        source = StackLocatorUtil.calcLocation(loggerFqcn);
694        return source;
695    }
696
697    @Override
698    public boolean isIncludeLocation() {
699        return includeLocation;
700    }
701
702    @Override
703    public void setIncludeLocation(final boolean includeLocation) {
704        this.includeLocation = includeLocation;
705    }
706
707    @Override
708    public boolean isEndOfBatch() {
709        return endOfBatch;
710    }
711
712    @Override
713    public void setEndOfBatch(final boolean endOfBatch) {
714        this.endOfBatch = endOfBatch;
715    }
716
717    @Override
718    public long getNanoTime() {
719        return nanoTime;
720    }
721
722    /**
723     * Creates a LogEventProxy that can be serialized.
724     * @return a LogEventProxy.
725     */
726    protected Object writeReplace() {
727        getThrownProxy(); // ensure ThrowableProxy is initialized
728        return new LogEventProxy(this, this.includeLocation);
729    }
730
731    /**
732     * Take a snapshot of the specified {@code LogEvent}.
733     *
734     * @param event the event to take a snapshot of
735     * @param includeLocation if true, this method will obtain caller location information
736     * @return snapshot of the event as a {@code Serializable} object
737     * @see #deserialize(Serializable)
738     * @see #serialize(Log4jLogEvent, boolean)
739     */
740    public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
741        if (event instanceof Log4jLogEvent) {
742            event.getThrownProxy(); // ensure ThrowableProxy is initialized
743            return new LogEventProxy((Log4jLogEvent) event, includeLocation);
744        }
745        return new LogEventProxy(event, includeLocation);
746    }
747
748    /**
749     * Take a snapshot of the specified {@code Log4jLogEvent}.
750     *
751     * @param event the event to take a snapshot of
752     * @param includeLocation if true, this method will obtain caller location information
753     * @return snapshot of the event as a {@code Serializable} object
754     * @see #deserialize(Serializable)
755     * @see #serialize(LogEvent, boolean)
756     */
757    public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
758        event.getThrownProxy(); // ensure ThrowableProxy is initialized
759        return new LogEventProxy(event, includeLocation);
760    }
761
762    public static boolean canDeserialize(final Serializable event) {
763        return event instanceof LogEventProxy;
764    }
765
766    public static Log4jLogEvent deserialize(final Serializable event) {
767        Objects.requireNonNull(event, "Event cannot be null");
768        if (event instanceof LogEventProxy) {
769            final LogEventProxy proxy = (LogEventProxy) event;
770            final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
771                    proxy.loggerFQCN, proxy.level, proxy.message,
772                    proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId,
773                    proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond,
774                    proxy.nanoTime);
775            result.setEndOfBatch(proxy.isEndOfBatch);
776            result.setIncludeLocation(proxy.isLocationRequired);
777            return result;
778        }
779        throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
780    }
781
782    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
783        throw new InvalidObjectException("Proxy required");
784    }
785
786    public static LogEvent createMemento(final LogEvent logEvent) {
787        return new Log4jLogEvent.Builder(logEvent).build();
788    }
789
790    /**
791     * Creates and returns a new immutable copy of this {@code Log4jLogEvent}.
792     *
793     * @return a new immutable copy of the data in this {@code Log4jLogEvent}
794     */
795    public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
796        return deserialize(serialize(event, includeLocation));
797    }
798
799    @Override
800    public String toString() {
801        final StringBuilder sb = new StringBuilder();
802        final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
803        sb.append("Logger=").append(n);
804        sb.append(" Level=").append(level.name());
805        sb.append(" Message=").append(message == null ? null : message.getFormattedMessage());
806        return sb.toString();
807    }
808
809    @Override
810    public boolean equals(final Object o) {
811        if (this == o) {
812            return true;
813        }
814        if (o == null || getClass() != o.getClass()) {
815            return false;
816        }
817
818        final Log4jLogEvent that = (Log4jLogEvent) o;
819
820        if (endOfBatch != that.endOfBatch) {
821            return false;
822        }
823        if (includeLocation != that.includeLocation) {
824            return false;
825        }
826        if (!instant.equals(that.instant)) {
827            return false;
828        }
829        if (nanoTime != that.nanoTime) {
830            return false;
831        }
832        if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
833            return false;
834        }
835        if (level != null ? !level.equals(that.level) : that.level != null) {
836            return false;
837        }
838        if (source != null ? !source.equals(that.source) : that.source != null) {
839            return false;
840        }
841        if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
842            return false;
843        }
844        if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) {
845            return false;
846        }
847        if (!message.equals(that.message)) {
848            return false;
849        }
850        if (!loggerName.equals(that.loggerName)) {
851            return false;
852        }
853        if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
854            return false;
855        }
856        if (threadId != that.threadId) {
857            return false;
858        }
859        if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
860            return false;
861        }
862        if (threadPriority != that.threadPriority) {
863            return false;
864        }
865        if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
866            return false;
867        }
868        if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
869            return false;
870        }
871
872        return true;
873    }
874
875    @Override
876    public int hashCode() {
877        // Check:OFF: MagicNumber
878        int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
879        result = 31 * result + (marker != null ? marker.hashCode() : 0);
880        result = 31 * result + (level != null ? level.hashCode() : 0);
881        result = 31 * result + loggerName.hashCode();
882        result = 31 * result + message.hashCode();
883        result = 31 * result + instant.hashCode();
884        result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
885        result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
886        result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
887        result = 31 * result + (contextData != null ? contextData.hashCode() : 0);
888        result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
889        result = 31 * result + (int) (threadId ^ (threadId >>> 32));
890        result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
891        result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
892        result = 31 * result + (source != null ? source.hashCode() : 0);
893        result = 31 * result + (includeLocation ? 1 : 0);
894        result = 31 * result + (endOfBatch ? 1 : 0);
895        // Check:ON: MagicNumber
896        return result;
897    }
898
899    /**
900     * Proxy pattern used to serialize the LogEvent.
901     */
902    static class LogEventProxy implements Serializable {
903
904        private static final long serialVersionUID = -8634075037355293699L;
905        private final String loggerFQCN;
906        private final Marker marker;
907        private final Level level;
908        private final String loggerName;
909        // transient since 2.8
910        private final transient Message message;
911        /** since 2.8 */
912        private MarshalledObject<Message> marshalledMessage;
913        /** since 2.8 */
914        private String messageString;
915        private final long timeMillis;
916        /** since 2.11 */
917        private final int nanoOfMillisecond;
918        private final transient Throwable thrown;
919        private final ThrowableProxy thrownProxy;
920        /** @since 2.7 */
921        private final StringMap contextData;
922        private final ThreadContext.ContextStack contextStack;
923        /** @since 2.6 */
924        private final long threadId;
925        private final String threadName;
926        /** @since 2.6 */
927        private final int threadPriority;
928        private final StackTraceElement source;
929        private final boolean isLocationRequired;
930        private final boolean isEndOfBatch;
931        /** @since 2.4 */
932        private final transient long nanoTime;
933
934        public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
935            this.loggerFQCN = event.loggerFqcn;
936            this.marker = event.marker;
937            this.level = event.level;
938            this.loggerName = event.loggerName;
939            this.message = event.message instanceof ReusableMessage
940                    ? memento((ReusableMessage) event.message)
941                    : event.message;
942            this.timeMillis = event.instant.getEpochMillisecond();
943            this.nanoOfMillisecond = event.instant.getNanoOfMillisecond();
944            this.thrown = event.thrown;
945            this.thrownProxy = event.thrownProxy;
946            this.contextData = event.contextData;
947            this.contextStack = event.contextStack;
948            this.source = includeLocation ? event.getSource() : null;
949            this.threadId = event.getThreadId();
950            this.threadName = event.getThreadName();
951            this.threadPriority = event.getThreadPriority();
952            this.isLocationRequired = includeLocation;
953            this.isEndOfBatch = event.endOfBatch;
954            this.nanoTime = event.nanoTime;
955        }
956
957        public LogEventProxy(final LogEvent event, final boolean includeLocation) {
958            this.loggerFQCN = event.getLoggerFqcn();
959            this.marker = event.getMarker();
960            this.level = event.getLevel();
961            this.loggerName = event.getLoggerName();
962
963            final Message temp = event.getMessage();
964            message = temp instanceof ReusableMessage
965                    ? memento((ReusableMessage) temp)
966                    : temp;
967            this.timeMillis = event.getInstant().getEpochMillisecond();
968            this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond();
969            this.thrown = event.getThrown();
970            this.thrownProxy = event.getThrownProxy();
971            this.contextData = memento(event.getContextData());
972            this.contextStack = event.getContextStack();
973            this.source = includeLocation ? event.getSource() : null;
974            this.threadId = event.getThreadId();
975            this.threadName = event.getThreadName();
976            this.threadPriority = event.getThreadPriority();
977            this.isLocationRequired = includeLocation;
978            this.isEndOfBatch = event.isEndOfBatch();
979            this.nanoTime = event.getNanoTime();
980        }
981
982        private static Message memento(final ReusableMessage message) {
983            return message.memento();
984        }
985
986        private static StringMap memento(final ReadOnlyStringMap data) {
987            final StringMap result = ContextDataFactory.createContextData();
988            result.putAll(data);
989            return result;
990        }
991
992        private static MarshalledObject<Message> marshall(final Message msg) {
993            try {
994                return new MarshalledObject<>(msg);
995            } catch (final Exception ex) {
996                return null;
997            }
998        }
999
1000        private void writeObject(final java.io.ObjectOutputStream s) throws IOException {
1001            this.messageString = message.getFormattedMessage();
1002            this.marshalledMessage = marshall(message);
1003            s.defaultWriteObject();
1004        }
1005
1006        /**
1007         * Returns a Log4jLogEvent using the data in the proxy.
1008         * @return Log4jLogEvent.
1009         */
1010        protected Object readResolve() {
1011            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown,
1012                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
1013                    nanoOfMillisecond, nanoTime);
1014            result.setEndOfBatch(isEndOfBatch);
1015            result.setIncludeLocation(isLocationRequired);
1016            return result;
1017        }
1018
1019        private Message message() {
1020            if (marshalledMessage != null) {
1021                try {
1022                    return marshalledMessage.get();
1023                } catch (final Exception ex) {
1024                    // ignore me
1025                }
1026            }
1027            return new SimpleMessage(messageString);
1028        }
1029    }
1030}