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 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 t A Throwable or null.
349    * @param mdc The mapped diagnostic context.
350    * @param ndc the nested diagnostic context.
351    * @param threadName The name of the thread.
352    * @param location The locations of the caller.
353    * @param timestampMillis The timestamp of the event.
354    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
355    */
356   @Deprecated
357   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
358                        final Message message, final Throwable t, final Map<String, String> mdc,
359                        final ThreadContext.ContextStack ndc, final String threadName,
360                        final StackTraceElement location, final long timestampMillis) {
361       this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0,
362               threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime());
363   }
364
365   /**
366    * Create a new LogEvent.
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 thrown A Throwable or null.
373    * @param thrownProxy A ThrowableProxy or null.
374    * @param mdc The mapped diagnostic context.
375    * @param ndc the nested diagnostic context.
376    * @param threadName The name of the thread.
377    * @param location The locations of the caller.
378    * @param timestamp The timestamp of the event.
379    * @return a new LogEvent
380    * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release.
381    */
382    @Deprecated
383    public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
384                                            final Level level, final Message message, final Throwable thrown,
385                                            final ThrowableProxy thrownProxy,
386                                            final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
387                                            final String threadName, final StackTraceElement location,
388                                            final long timestamp) {
389        final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
390                thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime());
391        return result;
392    }
393
394    /**
395     * Constructor.
396     * @param loggerName The name of the Logger.
397     * @param marker The Marker or null.
398     * @param loggerFQCN The fully qualified class name of the caller.
399     * @param level The logging Level.
400     * @param message The Message.
401     * @param thrown A Throwable or null.
402     * @param thrownProxy A ThrowableProxy or null.
403     * @param contextData The key-value pairs from the context.
404     * @param contextStack the nested diagnostic context.
405     * @param threadId the thread ID
406     * @param threadName The name of the thread.
407     * @param threadPriority the thread priority
408     * @param source The locations of the caller.
409     * @param timestampMillis The timestamp of the event.
410     * @param nanoOfMillisecond the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999}
411     * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
412     *          created.
413     */
414    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
415            final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
416            final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
417            final String threadName, final int threadPriority, final StackTraceElement source,
418            final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) {
419        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
420        long millis = message instanceof TimestampMessage
421                ? ((TimestampMessage) message).getTimestamp()
422                : timestampMillis;
423        instant.initFromEpochMilli(millis, nanoOfMillisecond);
424    }
425    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
426                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
427                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
428                          final String threadName, final int threadPriority, final StackTraceElement source,
429                          final Clock clock, final long nanoTime) {
430        this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
431        if (message instanceof TimestampMessage) {
432            instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
433        } else {
434            instant.initFrom(clock);
435        }
436    }
437    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
438                          final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
439                          final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
440                          final String threadName, final int threadPriority, final StackTraceElement source,
441                          final long nanoTime) {
442        this.loggerName = loggerName;
443        this.marker = marker;
444        this.loggerFqcn = loggerFQCN;
445        this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
446        this.message = message;
447        this.thrown = thrown;
448        this.thrownProxy = thrownProxy;
449        this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
450        this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
451        this.threadId = threadId;
452        this.threadName = threadName;
453        this.threadPriority = threadPriority;
454        this.source = source;
455        if (message instanceof LoggerNameAwareMessage) {
456            ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
457        }
458        this.nanoTime = nanoTime;
459    }
460
461    private static StringMap createContextData(final Map<String, String> contextMap) {
462        final StringMap result = ContextDataFactory.createContextData();
463        if (contextMap != null) {
464            for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
465                result.putValue(entry.getKey(), entry.getValue());
466            }
467        }
468        return result;
469    }
470
471    private static StringMap createContextData(final List<Property> properties) {
472        final StringMap reusable = ContextDataFactory.createContextData();
473        return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
474    }
475
476    /**
477     * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
478     * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
479     */
480    public static NanoClock getNanoClock() {
481        return nanoClock;
482    }
483
484    /**
485     * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
486     * <p>
487     * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
488     * configuration changes.
489     *
490     * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
491     */
492    public static void setNanoClock(final NanoClock nanoClock) {
493        Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
494        StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
495    }
496
497    /**
498     * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event.
499     * @return a new fully initialized builder.
500     */
501    public Builder asBuilder() {
502        return new Builder(this);
503    }
504
505    @Override
506    public Log4jLogEvent toImmutable() {
507        if (getMessage() instanceof ReusableMessage) {
508            makeMessageImmutable();
509        }
510        return this;
511    }
512
513    /**
514     * Returns the logging Level.
515     * @return the Level associated with this event.
516     */
517    @Override
518    public Level getLevel() {
519        return level;
520    }
521
522    /**
523     * Returns the name of the Logger used to generate the event.
524     * @return The Logger name.
525     */
526    @Override
527    public String getLoggerName() {
528        return loggerName;
529    }
530
531    /**
532     * Returns the Message associated with the event.
533     * @return The Message.
534     */
535    @Override
536    public Message getMessage() {
537        return message;
538    }
539
540    public void makeMessageImmutable() {
541        message = new SimpleMessage(message.getFormattedMessage());
542    }
543
544    @Override
545    public long getThreadId() {
546        if (threadId == 0) {
547            threadId = Thread.currentThread().getId();
548        }
549        return threadId;
550    }
551
552    /**
553     * Returns the name of the Thread on which the event was generated.
554     * @return The name of the Thread.
555     */
556    @Override
557    public String getThreadName() {
558        if (threadName == null) {
559            threadName = Thread.currentThread().getName();
560        }
561        return threadName;
562    }
563
564    @Override
565    public int getThreadPriority() {
566        if (threadPriority == 0) {
567            threadPriority = Thread.currentThread().getPriority();
568        }
569        return threadPriority;
570    }
571
572    /**
573     * {@inheritDoc}
574     */
575    @Override
576    public long getTimeMillis() {
577        return instant.getEpochMillisecond();
578    }
579
580    /**
581     * {@inheritDoc}
582     * @since 2.11
583     */
584    @Override
585    public Instant getInstant() {
586        return instant;
587    }
588
589    /**
590     * Returns the Throwable associated with the event, or null.
591     * @return The Throwable associated with the event.
592     */
593    @Override
594    public Throwable getThrown() {
595        return thrown;
596    }
597
598    /**
599     * Returns the ThrowableProxy associated with the event, or null.
600     * @return The ThrowableProxy associated with the event.
601     */
602    @Override
603    public ThrowableProxy getThrownProxy() {
604        if (thrownProxy == null && thrown != null) {
605            thrownProxy = new ThrowableProxy(thrown);
606        }
607        return thrownProxy;
608    }
609
610
611    /**
612     * Returns the Marker associated with the event, or null.
613     * @return the Marker associated with the event.
614     */
615    @Override
616    public Marker getMarker() {
617        return marker;
618    }
619
620    /**
621     * The fully qualified class name of the class that was called by the caller.
622     * @return the fully qualified class name of the class that is performing logging.
623     */
624    @Override
625    public String getLoggerFqcn() {
626        return loggerFqcn;
627    }
628
629    /**
630     * Returns the {@code ReadOnlyStringMap} containing context data key-value pairs.
631     * @return the {@code ReadOnlyStringMap} containing context data key-value pairs
632     * @since 2.7
633     */
634    @Override
635    public ReadOnlyStringMap getContextData() {
636        return contextData;
637    }
638    /**
639     * Returns the immutable copy of the ThreadContext Map.
640     * @return The context Map.
641     */
642    @Override
643    public Map<String, String> getContextMap() {
644        return contextData.toMap();
645    }
646
647    /**
648     * Returns an immutable copy of the ThreadContext stack.
649     * @return The context Stack.
650     */
651    @Override
652    public ThreadContext.ContextStack getContextStack() {
653        return contextStack;
654    }
655
656    /**
657     * Returns the StackTraceElement for the caller. This will be the entry that occurs right
658     * before the first occurrence of FQCN as a class name.
659     * @return the StackTraceElement for the caller.
660     */
661    @Override
662    public StackTraceElement getSource() {
663        if (source != null) {
664            return source;
665        }
666        if (loggerFqcn == null || !includeLocation) {
667            return null;
668        }
669        source = StackLocatorUtil.calcLocation(loggerFqcn);
670        return source;
671    }
672
673    @Override
674    public boolean isIncludeLocation() {
675        return includeLocation;
676    }
677
678    @Override
679    public void setIncludeLocation(final boolean includeLocation) {
680        this.includeLocation = includeLocation;
681    }
682
683    @Override
684    public boolean isEndOfBatch() {
685        return endOfBatch;
686    }
687
688    @Override
689    public void setEndOfBatch(final boolean endOfBatch) {
690        this.endOfBatch = endOfBatch;
691    }
692
693    @Override
694    public long getNanoTime() {
695        return nanoTime;
696    }
697
698    /**
699     * Creates a LogEventProxy that can be serialized.
700     * @return a LogEventProxy.
701     */
702    protected Object writeReplace() {
703        getThrownProxy(); // ensure ThrowableProxy is initialized
704        return new LogEventProxy(this, this.includeLocation);
705    }
706
707    /**
708     * Take a snapshot of the specified {@code LogEvent}.
709     *
710     * @param event the event to take a snapshot of
711     * @param includeLocation if true, this method will obtain caller location information
712     * @return snapshot of the event as a {@code Serializable} object
713     * @see #deserialize(Serializable)
714     * @see #serialize(Log4jLogEvent, boolean)
715     */
716    public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
717        if (event instanceof Log4jLogEvent) {
718            event.getThrownProxy(); // ensure ThrowableProxy is initialized
719            return new LogEventProxy((Log4jLogEvent) event, includeLocation);
720        }
721        return new LogEventProxy(event, includeLocation);
722    }
723
724    /**
725     * Take a snapshot of the specified {@code Log4jLogEvent}.
726     *
727     * @param event the event to take a snapshot of
728     * @param includeLocation if true, this method will obtain caller location information
729     * @return snapshot of the event as a {@code Serializable} object
730     * @see #deserialize(Serializable)
731     * @see #serialize(LogEvent, boolean)
732     */
733    public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
734        event.getThrownProxy(); // ensure ThrowableProxy is initialized
735        return new LogEventProxy(event, includeLocation);
736    }
737
738    public static boolean canDeserialize(final Serializable event) {
739        return event instanceof LogEventProxy;
740    }
741
742    public static Log4jLogEvent deserialize(final Serializable event) {
743        Objects.requireNonNull(event, "Event cannot be null");
744        if (event instanceof LogEventProxy) {
745            final LogEventProxy proxy = (LogEventProxy) event;
746            final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
747                    proxy.loggerFQCN, proxy.level, proxy.message,
748                    proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId,
749                    proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond,
750                    proxy.nanoTime);
751            result.setEndOfBatch(proxy.isEndOfBatch);
752            result.setIncludeLocation(proxy.isLocationRequired);
753            return result;
754        }
755        throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
756    }
757
758    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
759        throw new InvalidObjectException("Proxy required");
760    }
761
762    public static LogEvent createMemento(final LogEvent logEvent) {
763        return new Log4jLogEvent.Builder(logEvent).build();
764    }
765    
766    /**
767     * Creates and returns a new immutable copy of this {@code Log4jLogEvent}.
768     *
769     * @return a new immutable copy of the data in this {@code Log4jLogEvent}
770     */
771    public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
772        return deserialize(serialize(event, includeLocation));
773    }
774
775    @Override
776    public String toString() {
777        final StringBuilder sb = new StringBuilder();
778        final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
779        sb.append("Logger=").append(n);
780        sb.append(" Level=").append(level.name());
781        sb.append(" Message=").append(message == null ? null : message.getFormattedMessage());
782        return sb.toString();
783    }
784
785    @Override
786    public boolean equals(final Object o) {
787        if (this == o) {
788            return true;
789        }
790        if (o == null || getClass() != o.getClass()) {
791            return false;
792        }
793
794        final Log4jLogEvent that = (Log4jLogEvent) o;
795
796        if (endOfBatch != that.endOfBatch) {
797            return false;
798        }
799        if (includeLocation != that.includeLocation) {
800            return false;
801        }
802        if (!instant.equals(that.instant)) {
803            return false;
804        }
805        if (nanoTime != that.nanoTime) {
806            return false;
807        }
808        if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
809            return false;
810        }
811        if (level != null ? !level.equals(that.level) : that.level != null) {
812            return false;
813        }
814        if (source != null ? !source.equals(that.source) : that.source != null) {
815            return false;
816        }
817        if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
818            return false;
819        }
820        if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) {
821            return false;
822        }
823        if (!message.equals(that.message)) {
824            return false;
825        }
826        if (!loggerName.equals(that.loggerName)) {
827            return false;
828        }
829        if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
830            return false;
831        }
832        if (threadId != that.threadId) {
833            return false;
834        }
835        if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
836            return false;
837        }
838        if (threadPriority != that.threadPriority) {
839            return false;
840        }
841        if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
842            return false;
843        }
844        if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
845            return false;
846        }
847
848        return true;
849    }
850
851    @Override
852    public int hashCode() {
853        // Check:OFF: MagicNumber
854        int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
855        result = 31 * result + (marker != null ? marker.hashCode() : 0);
856        result = 31 * result + (level != null ? level.hashCode() : 0);
857        result = 31 * result + loggerName.hashCode();
858        result = 31 * result + message.hashCode();
859        result = 31 * result + instant.hashCode();
860        result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
861        result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
862        result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
863        result = 31 * result + (contextData != null ? contextData.hashCode() : 0);
864        result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
865        result = 31 * result + (int) (threadId ^ (threadId >>> 32));
866        result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
867        result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
868        result = 31 * result + (source != null ? source.hashCode() : 0);
869        result = 31 * result + (includeLocation ? 1 : 0);
870        result = 31 * result + (endOfBatch ? 1 : 0);
871        // Check:ON: MagicNumber
872        return result;
873    }
874
875    /**
876     * Proxy pattern used to serialize the LogEvent.
877     */
878    static class LogEventProxy implements Serializable {
879
880        private static final long serialVersionUID = -8634075037355293699L;
881        private final String loggerFQCN;
882        private final Marker marker;
883        private final Level level;
884        private final String loggerName;
885        // transient since 2.8
886        private final transient Message message;
887        /** since 2.8 */
888        private MarshalledObject<Message> marshalledMessage;
889        /** since 2.8 */
890        private String messageString;
891        private final long timeMillis;
892        /** since 2.11 */
893        private final int nanoOfMillisecond;
894        private final transient Throwable thrown;
895        private final ThrowableProxy thrownProxy;
896        /** @since 2.7 */
897        private final StringMap contextData;
898        private final ThreadContext.ContextStack contextStack;
899        /** @since 2.6 */
900        private final long threadId;
901        private final String threadName;
902        /** @since 2.6 */
903        private final int threadPriority;
904        private final StackTraceElement source;
905        private final boolean isLocationRequired;
906        private final boolean isEndOfBatch;
907        /** @since 2.4 */
908        private final transient long nanoTime;
909
910        public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
911            this.loggerFQCN = event.loggerFqcn;
912            this.marker = event.marker;
913            this.level = event.level;
914            this.loggerName = event.loggerName;
915            this.message = event.message instanceof ReusableMessage
916                    ? memento((ReusableMessage) event.message)
917                    : event.message;
918            this.timeMillis = event.instant.getEpochMillisecond();
919            this.nanoOfMillisecond = event.instant.getNanoOfMillisecond();
920            this.thrown = event.thrown;
921            this.thrownProxy = event.thrownProxy;
922            this.contextData = event.contextData;
923            this.contextStack = event.contextStack;
924            this.source = includeLocation ? event.getSource() : null;
925            this.threadId = event.getThreadId();
926            this.threadName = event.getThreadName();
927            this.threadPriority = event.getThreadPriority();
928            this.isLocationRequired = includeLocation;
929            this.isEndOfBatch = event.endOfBatch;
930            this.nanoTime = event.nanoTime;
931        }
932
933        public LogEventProxy(final LogEvent event, final boolean includeLocation) {
934            this.loggerFQCN = event.getLoggerFqcn();
935            this.marker = event.getMarker();
936            this.level = event.getLevel();
937            this.loggerName = event.getLoggerName();
938
939            final Message temp = event.getMessage();
940            message = temp instanceof ReusableMessage
941                    ? memento((ReusableMessage) temp)
942                    : temp;
943            this.timeMillis = event.getInstant().getEpochMillisecond();
944            this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond();
945            this.thrown = event.getThrown();
946            this.thrownProxy = event.getThrownProxy();
947            this.contextData = memento(event.getContextData());
948            this.contextStack = event.getContextStack();
949            this.source = includeLocation ? event.getSource() : null;
950            this.threadId = event.getThreadId();
951            this.threadName = event.getThreadName();
952            this.threadPriority = event.getThreadPriority();
953            this.isLocationRequired = includeLocation;
954            this.isEndOfBatch = event.isEndOfBatch();
955            this.nanoTime = event.getNanoTime();
956        }
957
958        private static Message memento(final ReusableMessage message) {
959            return message.memento();
960        }
961
962        private static StringMap memento(final ReadOnlyStringMap data) {
963            final StringMap result = ContextDataFactory.createContextData();
964            result.putAll(data);
965            return result;
966        }
967
968        private static MarshalledObject<Message> marshall(final Message msg) {
969            try {
970                return new MarshalledObject<>(msg);
971            } catch (final Exception ex) {
972                return null;
973            }
974        }
975
976        private void writeObject(final java.io.ObjectOutputStream s) throws IOException {
977            this.messageString = message.getFormattedMessage();
978            this.marshalledMessage = marshall(message);
979            s.defaultWriteObject();
980        }
981
982        /**
983         * Returns a Log4jLogEvent using the data in the proxy.
984         * @return Log4jLogEvent.
985         */
986        protected Object readResolve() {
987            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown,
988                    thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
989                    nanoOfMillisecond, nanoTime);
990            result.setEndOfBatch(isEndOfBatch);
991            result.setIncludeLocation(isLocationRequired);
992            return result;
993        }
994
995        private Message message() {
996            if (marshalledMessage != null) {
997                try {
998                    return marshalledMessage.get();
999                } catch (final Exception ex) {
1000                    // ignore me
1001                }
1002            }
1003            return new SimpleMessage(messageString);
1004        }
1005    }
1006}