001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.impl;
018
019import java.io.InvalidObjectException;
020import java.io.ObjectInputStream;
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Objects;
027
028import org.apache.logging.log4j.Level;
029import org.apache.logging.log4j.Marker;
030import org.apache.logging.log4j.ThreadContext;
031import org.apache.logging.log4j.core.LogEvent;
032import org.apache.logging.log4j.core.async.RingBufferLogEvent;
033import org.apache.logging.log4j.core.config.LoggerConfig;
034import org.apache.logging.log4j.core.config.Property;
035import org.apache.logging.log4j.core.util.Clock;
036import org.apache.logging.log4j.core.util.ClockFactory;
037import org.apache.logging.log4j.core.util.DummyNanoClock;
038import org.apache.logging.log4j.core.util.NanoClock;
039import org.apache.logging.log4j.message.LoggerNameAwareMessage;
040import org.apache.logging.log4j.message.Message;
041import org.apache.logging.log4j.message.ReusableMessage;
042import org.apache.logging.log4j.message.SimpleMessage;
043import org.apache.logging.log4j.message.TimestampMessage;
044import org.apache.logging.log4j.status.StatusLogger;
045import org.apache.logging.log4j.util.Strings;
046
047/**
048 * Implementation of a LogEvent.
049 */
050public class Log4jLogEvent implements LogEvent {
051
052    private static final long serialVersionUID = -8393305700508709443L;
053    private static final Clock CLOCK = ClockFactory.getClock();
054    private static volatile NanoClock nanoClock = new DummyNanoClock();
055    private final String loggerFqcn;
056    private final Marker marker;
057    private final Level level;
058    private final String loggerName;
059    private Message message;
060    private final long timeMillis;
061    private final transient Throwable thrown;
062    private ThrowableProxy thrownProxy;
063    private final Map<String, String> contextMap;
064    private final ThreadContext.ContextStack contextStack;
065    private long threadId;
066    private String threadName;
067    private int threadPriority;
068    private StackTraceElement source;
069    private boolean includeLocation;
070    private boolean endOfBatch = false;
071    /** @since Log4J 2.4 */
072    private final transient long nanoTime;
073
074    /** LogEvent Builder helper class. */
075    public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
076
077        private String loggerFqcn;
078        private Marker marker;
079        private Level level;
080        private String loggerName;
081        private Message message;
082        private Throwable thrown;
083        private long timeMillis = CLOCK.currentTimeMillis();
084        private ThrowableProxy thrownProxy;
085        private Map<String, String> contextMap = ThreadContext.getImmutableContext();
086        private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
087        private long threadId;
088        private String threadName;
089        private int threadPriority;
090        private StackTraceElement source;
091        private boolean includeLocation;
092        private boolean endOfBatch = false;
093        private long nanoTime;
094
095        public Builder() {
096        }
097
098        public Builder(LogEvent other) {
099            Objects.requireNonNull(other);
100            if (other instanceof RingBufferLogEvent) {
101                ((RingBufferLogEvent) other).initializeBuilder(this);
102                return;
103            }
104            if (other instanceof MutableLogEvent) {
105                ((MutableLogEvent) other).initializeBuilder(this);
106                return;
107            }
108            this.loggerFqcn = other.getLoggerFqcn();
109            this.marker = other.getMarker();
110            this.level = other.getLevel();
111            this.loggerName = other.getLoggerName();
112            this.message = other.getMessage();
113            this.timeMillis = other.getTimeMillis();
114            this.thrown = other.getThrown();
115            this.contextMap = other.getContextMap();
116            this.contextStack = other.getContextStack();
117            this.includeLocation = other.isIncludeLocation();
118            this.endOfBatch = other.isEndOfBatch();
119            this.nanoTime = other.getNanoTime();
120
121            // Avoid unnecessarily initializing thrownProxy, threadName and source if possible
122            if (other instanceof Log4jLogEvent) {
123                Log4jLogEvent evt = (Log4jLogEvent) other;
124                this.thrownProxy = evt.thrownProxy;
125                this.source = evt.source;
126                this.threadId = evt.threadId;
127                this.threadName = evt.threadName;
128                this.threadPriority = evt.threadPriority;
129            } else {
130                this.thrownProxy = other.getThrownProxy();
131                this.source = other.getSource();
132                this.threadId = other.getThreadId();
133                this.threadName = other.getThreadName();
134                this.threadPriority = other.getThreadPriority();
135            }
136        }
137
138        public Builder setLevel(final Level level) {
139            this.level = level;
140            return this;
141        }
142
143        public Builder setLoggerFqcn(final String loggerFqcn) {
144            this.loggerFqcn = loggerFqcn;
145            return this;
146        }
147
148        public Builder setLoggerName(final String loggerName) {
149            this.loggerName = loggerName;
150            return this;
151        }
152
153        public Builder setMarker(final Marker marker) {
154            this.marker = marker;
155            return this;
156        }
157
158        public Builder setMessage(final Message message) {
159            this.message = message;
160            return this;
161        }
162
163        public Builder setThrown(final Throwable thrown) {
164            this.thrown = thrown;
165            return this;
166        }
167
168        public Builder setTimeMillis(long timeMillis) {
169            this.timeMillis = timeMillis;
170            return this;
171        }
172
173        public Builder setThrownProxy(ThrowableProxy thrownProxy) {
174            this.thrownProxy = thrownProxy;
175            return this;
176        }
177
178        public Builder setContextMap(Map<String, String> contextMap) {
179            this.contextMap = contextMap;
180            return this;
181        }
182
183        public Builder setContextStack(ThreadContext.ContextStack contextStack) {
184            this.contextStack = contextStack;
185            return this;
186        }
187
188        public Builder setThreadId(long threadId) {
189            this.threadId = threadId;
190            return this;
191        }
192
193        public Builder setThreadName(String threadName) {
194            this.threadName = threadName;
195            return this;
196        }
197
198        public Builder setThreadPriority(int threadPriority) {
199            this.threadPriority = threadPriority;
200            return this;
201        }
202
203        public Builder setSource(StackTraceElement source) {
204            this.source = source;
205            return this;
206        }
207
208        public Builder setIncludeLocation(boolean includeLocation) {
209            this.includeLocation = includeLocation;
210            return this;
211        }
212
213        public Builder setEndOfBatch(boolean endOfBatch) {
214            this.endOfBatch = endOfBatch;
215            return this;
216        }
217
218        /**
219         * Sets the nano time for the event.
220         * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event
221         *          was created.
222         * @return this builder
223         */
224        public Builder setNanoTime(long nanoTime) {
225            this.nanoTime = nanoTime;
226            return this;
227        }
228
229        @Override
230        public Log4jLogEvent build() {
231            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
232                    thrownProxy, contextMap, contextStack, threadId, threadName, threadPriority, source, timeMillis, nanoTime);
233            result.setIncludeLocation(includeLocation);
234            result.setEndOfBatch(endOfBatch);
235            return result;
236        }
237    }
238
239    /**
240     * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty.
241     * @return a new empty builder.
242     */
243    public static Builder newBuilder() {
244        return new Builder();
245    }
246
247    public Log4jLogEvent() {
248        this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
249                0, null, CLOCK.currentTimeMillis(), nanoClock.nanoTime());
250    }
251
252    /**
253    *
254    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
255    */
256   @Deprecated
257   public Log4jLogEvent(final long timestamp) {
258       this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
259               0, null, timestamp, nanoClock.nanoTime());
260   }
261
262   /**
263    * Constructor.
264    * @param loggerName The name of the Logger.
265    * @param marker The Marker or null.
266    * @param loggerFQCN The fully qualified class name of the caller.
267    * @param level The logging Level.
268    * @param message The Message.
269    * @param t A Throwable or null.
270    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
271    */
272   @Deprecated
273   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
274                        final Message message, final Throwable t) {
275       this(loggerName, marker, loggerFQCN, level, message, null, t);
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 properties properties to add to the event.
286    * @param t A Throwable or null.
287    */
288   // This constructor is called from LogEventFactories.
289   public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
290                        final Message message, final List<Property> properties, final Throwable t) {
291       this(loggerName, marker, loggerFQCN, level, message, t, null,
292           createMap(properties),
293           ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy
294           0, // thread name
295           null, // stack trace element
296           0,
297           null, // LOG4J2-628 use log4j.Clock for timestamps
298           // LOG4J2-744 unless TimestampMessage already has one
299           message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :
300               CLOCK.currentTimeMillis(), nanoClock.nanoTime());
301   }
302
303   /**
304    * Constructor.
305    * @param loggerName The name of the Logger.
306    * @param marker The Marker or null.
307    * @param loggerFQCN The fully qualified class name of the caller.
308    * @param level The logging Level.
309    * @param message The Message.
310    * @param t A Throwable 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 timestampMillis The timestamp of the event.
316    * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release.
317    */
318   @Deprecated
319public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
320                        final Message message, final Throwable t, final Map<String, String> mdc,
321                        final ThreadContext.ContextStack ndc, final String threadName,
322                        final StackTraceElement location, final long timestampMillis) {
323       this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, 0,
324               threadName, 0, location, timestampMillis, nanoClock.nanoTime());
325   }
326
327   /**
328    * Create a new LogEvent.
329    * @param loggerName The name of the Logger.
330    * @param marker The Marker or null.
331    * @param loggerFQCN The fully qualified class name of the caller.
332    * @param level The logging Level.
333    * @param message The Message.
334    * @param thrown A Throwable or null.
335    * @param thrownProxy A ThrowableProxy or null.
336    * @param mdc The mapped diagnostic context.
337    * @param ndc the nested diagnostic context.
338    * @param threadName The name of the thread.
339    * @param location The locations of the caller.
340    * @param timestamp The timestamp of the event.
341    * @return a new LogEvent
342    * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release.
343    */
344    @Deprecated
345    public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
346                                            final Level level, final Message message, final Throwable thrown,
347                                            final ThrowableProxy thrownProxy,
348                                            final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
349                                            final String threadName, final StackTraceElement location,
350                                            final long timestamp) {
351        final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
352                thrownProxy, mdc, ndc, 0, threadName, 0, location, timestamp, nanoClock.nanoTime());
353        return result;
354    }
355
356    /**
357     * Constructor.
358     * @param loggerName The name of the Logger.
359     * @param marker The Marker or null.
360     * @param loggerFQCN The fully qualified class name of the caller.
361     * @param level The logging Level.
362     * @param message The Message.
363     * @param thrown A Throwable or null.
364     * @param thrownProxy A ThrowableProxy or null.
365     * @param contextMap The mapped diagnostic context.
366     * @param contextStack the nested diagnostic context.
367     * @param threadId the thread ID
368     * @param threadName The name of the thread.
369     * @param threadPriority the thread priority
370     * @param source The locations of the caller.
371     * @param timestampMillis The timestamp of the event.
372     * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was
373     *          created.
374     */
375    private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
376            final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
377            final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack, long threadId,
378            final String threadName, int threadPriority, final StackTraceElement source, final long timestampMillis,
379            final long nanoTime) {
380        this.loggerName = loggerName;
381        this.marker = marker;
382        this.loggerFqcn = loggerFQCN;
383        this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
384        this.message = message;
385        this.thrown = thrown;
386        this.thrownProxy = thrownProxy;
387        this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap;
388        this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
389        this.timeMillis = message instanceof TimestampMessage
390                ? ((TimestampMessage) message).getTimestamp()
391                : timestampMillis;
392        this.threadId = threadId;
393        this.threadName = threadName;
394        this.threadPriority = threadPriority;
395        this.source = source;
396        if (message != null && message instanceof LoggerNameAwareMessage) {
397            ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
398        }
399        this.nanoTime = nanoTime;
400    }
401
402    static Map<String, String> createMap(final List<Property> properties) {
403        final Map<String, String> contextMap = ThreadContext.getImmutableContext();
404        if (properties == null || properties.isEmpty()) {
405            return contextMap; // may be ThreadContext.EMPTY_MAP but not null
406        }
407        final Map<String, String> map = new HashMap<>(contextMap);
408
409        for (final Property prop : properties) {
410            if (!map.containsKey(prop.getName())) {
411                map.put(prop.getName(), prop.getValue());
412            }
413        }
414        return Collections.unmodifiableMap(map);
415    }
416
417    /**
418     * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
419     * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events
420     */
421    public static NanoClock getNanoClock() {
422        return nanoClock;
423    }
424
425    /**
426     * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events.
427     * <p>
428     * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the
429     * configuration changes.
430     *
431     * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events
432     */
433    public static void setNanoClock(NanoClock nanoClock) {
434        Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
435        StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
436    }
437
438    /**
439     * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event.
440     * @return a new fully initialized builder.
441     */
442    public Builder asBuilder() {
443        return new Builder(this);
444    }
445
446    /**
447     * Returns the logging Level.
448     * @return the Level associated with this event.
449     */
450    @Override
451    public Level getLevel() {
452        return level;
453    }
454
455    /**
456     * Returns the name of the Logger used to generate the event.
457     * @return The Logger name.
458     */
459    @Override
460    public String getLoggerName() {
461        return loggerName;
462    }
463
464    /**
465     * Returns the Message associated with the event.
466     * @return The Message.
467     */
468    @Override
469    public Message getMessage() {
470        return message;
471    }
472
473    public void makeMessageImmutable() {
474        message = new SimpleMessage(message.getFormattedMessage());
475    }
476
477    @Override
478    public long getThreadId() {
479        if (threadId == 0) {
480            threadId = Thread.currentThread().getId();
481        }
482        return threadId;
483    }
484
485    /**
486     * Returns the name of the Thread on which the event was generated.
487     * @return The name of the Thread.
488     */
489    @Override
490    public String getThreadName() {
491        if (threadName == null) {
492            threadName = Thread.currentThread().getName();
493        }
494        return threadName;
495    }
496
497    @Override
498    public int getThreadPriority() {
499        if (threadPriority == 0) {
500            threadPriority = Thread.currentThread().getPriority();
501        }
502        return threadPriority;
503    }
504
505    /**
506     * Returns the time in milliseconds from the epoch when the event occurred.
507     * @return The time the event occurred.
508     */
509    @Override
510    public long getTimeMillis() {
511        return timeMillis;
512    }
513
514    /**
515     * Returns the Throwable associated with the event, or null.
516     * @return The Throwable associated with the event.
517     */
518    @Override
519    public Throwable getThrown() {
520        return thrown;
521    }
522
523    /**
524     * Returns the ThrowableProxy associated with the event, or null.
525     * @return The ThrowableProxy associated with the event.
526     */
527    @Override
528    public ThrowableProxy getThrownProxy() {
529        if (thrownProxy == null && thrown != null) {
530            thrownProxy = new ThrowableProxy(thrown);
531        }
532        return thrownProxy;
533    }
534
535
536    /**
537     * Returns the Marker associated with the event, or null.
538     * @return the Marker associated with the event.
539     */
540    @Override
541    public Marker getMarker() {
542        return marker;
543    }
544
545    /**
546     * The fully qualified class name of the class that was called by the caller.
547     * @return the fully qualified class name of the class that is performing logging.
548     */
549    @Override
550    public String getLoggerFqcn() {
551        return loggerFqcn;
552    }
553
554    /**
555     * Returns the immutable copy of the ThreadContext Map.
556     * @return The context Map.
557     */
558    @Override
559    public Map<String, String> getContextMap() {
560        return contextMap;
561    }
562
563    /**
564     * Returns an immutable copy of the ThreadContext stack.
565     * @return The context Stack.
566     */
567    @Override
568    public ThreadContext.ContextStack getContextStack() {
569        return contextStack;
570    }
571
572    /**
573     * Returns the StackTraceElement for the caller. This will be the entry that occurs right
574     * before the first occurrence of FQCN as a class name.
575     * @return the StackTraceElement for the caller.
576     */
577    @Override
578    public StackTraceElement getSource() {
579        if (source != null) {
580            return source;
581        }
582        if (loggerFqcn == null || !includeLocation) {
583            return null;
584        }
585        source = calcLocation(loggerFqcn);
586        return source;
587    }
588
589    public static StackTraceElement calcLocation(final String fqcnOfLogger) {
590        if (fqcnOfLogger == null) {
591            return null;
592        }
593        // LOG4J2-1029 new Throwable().getStackTrace is faster than Thread.currentThread().getStackTrace().
594        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
595        StackTraceElement last = null;
596        for (int i = stackTrace.length - 1; i > 0; i--) {
597            final String className = stackTrace[i].getClassName();
598            if (fqcnOfLogger.equals(className)) {
599                return last;
600            }
601            last = stackTrace[i];
602        }
603        return null;
604    }
605
606    @Override
607    public boolean isIncludeLocation() {
608        return includeLocation;
609    }
610
611    @Override
612    public void setIncludeLocation(final boolean includeLocation) {
613        this.includeLocation = includeLocation;
614    }
615
616    @Override
617    public boolean isEndOfBatch() {
618        return endOfBatch;
619    }
620
621    @Override
622    public void setEndOfBatch(final boolean endOfBatch) {
623        this.endOfBatch = endOfBatch;
624    }
625
626    @Override
627    public long getNanoTime() {
628        return nanoTime;
629    }
630
631    /**
632     * Creates a LogEventProxy that can be serialized.
633     * @return a LogEventProxy.
634     */
635    protected Object writeReplace() {
636        getThrownProxy(); // ensure ThrowableProxy is initialized
637        return new LogEventProxy(this, this.includeLocation);
638    }
639
640    /**
641     * Take a snapshot of the specified {@code LogEvent}.
642     *
643     * @param event the event to take a snapshot of
644     * @param includeLocation if true, this method will obtain caller location information
645     * @return snapshot of the event as a {@code Serializable} object
646     * @see #deserialize(Serializable)
647     * @see #serialize(Log4jLogEvent, boolean)
648     */
649    public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
650        if (event instanceof Log4jLogEvent) {
651            event.getThrownProxy(); // ensure ThrowableProxy is initialized
652            return new LogEventProxy((Log4jLogEvent) event, includeLocation);
653        }
654        return new LogEventProxy(event, includeLocation);
655    }
656
657    /**
658     * Take a snapshot of the specified {@code Log4jLogEvent}.
659     *
660     * @param event the event to take a snapshot of
661     * @param includeLocation if true, this method will obtain caller location information
662     * @return snapshot of the event as a {@code Serializable} object
663     * @see #deserialize(Serializable)
664     * @see #serialize(LogEvent, boolean)
665     */
666    public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
667        event.getThrownProxy(); // ensure ThrowableProxy is initialized
668        return new LogEventProxy(event, includeLocation);
669    }
670
671    public static boolean canDeserialize(final Serializable event) {
672        return event instanceof LogEventProxy;
673    }
674
675    public static Log4jLogEvent deserialize(final Serializable event) {
676        Objects.requireNonNull(event, "Event cannot be null");
677        if (event instanceof LogEventProxy) {
678            final LogEventProxy proxy = (LogEventProxy) event;
679            final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
680                    proxy.loggerFQCN, proxy.level, proxy.message,
681                    proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadId,
682                    proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoTime);
683            result.setEndOfBatch(proxy.isEndOfBatch);
684            result.setIncludeLocation(proxy.isLocationRequired);
685            return result;
686        }
687        throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
688    }
689
690    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
691        throw new InvalidObjectException("Proxy required");
692    }
693
694    /**
695     * Creates and returns a new immutable copy of this {@code Log4jLogEvent}.
696     *
697     * @return a new immutable copy of the data in this {@code Log4jLogEvent}
698     */
699    public static Log4jLogEvent createMemento(LogEvent event, final boolean includeLocation) {
700        // TODO implement Log4jLogEvent.createMemento()
701        return deserialize(serialize(event, includeLocation));
702    }
703
704    @Override
705    public String toString() {
706        final StringBuilder sb = new StringBuilder();
707        final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
708        sb.append("Logger=").append(n);
709        sb.append(" Level=").append(level.name());
710        sb.append(" Message=").append(message.getFormattedMessage());
711        return sb.toString();
712    }
713
714    @Override
715    public boolean equals(final Object o) {
716        if (this == o) {
717            return true;
718        }
719        if (o == null || getClass() != o.getClass()) {
720            return false;
721        }
722
723        final Log4jLogEvent that = (Log4jLogEvent) o;
724
725        if (endOfBatch != that.endOfBatch) {
726            return false;
727        }
728        if (includeLocation != that.includeLocation) {
729            return false;
730        }
731        if (timeMillis != that.timeMillis) {
732            return false;
733        }
734        if (nanoTime != that.nanoTime) {
735            return false;
736        }
737        if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
738            return false;
739        }
740        if (level != null ? !level.equals(that.level) : that.level != null) {
741            return false;
742        }
743        if (source != null ? !source.equals(that.source) : that.source != null) {
744            return false;
745        }
746        if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
747            return false;
748        }
749        if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) {
750            return false;
751        }
752        if (!message.equals(that.message)) {
753            return false;
754        }
755        if (!loggerName.equals(that.loggerName)) {
756            return false;
757        }
758        if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
759            return false;
760        }
761        if (threadId != that.threadId) {
762            return false;
763        }
764        if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
765            return false;
766        }
767        if (threadPriority != that.threadPriority) {
768            return false;
769        }
770        if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
771            return false;
772        }
773        if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
774            return false;
775        }
776
777        return true;
778    }
779
780    @Override
781    public int hashCode() {
782        // Check:OFF: MagicNumber
783        int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
784        result = 31 * result + (marker != null ? marker.hashCode() : 0);
785        result = 31 * result + (level != null ? level.hashCode() : 0);
786        result = 31 * result + loggerName.hashCode();
787        result = 31 * result + message.hashCode();
788        result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32));
789        result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
790        result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
791        result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
792        result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0);
793        result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
794        result = 31 * result + (int) (threadId ^ (threadId >>> 32));
795        result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
796        result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
797        result = 31 * result + (source != null ? source.hashCode() : 0);
798        result = 31 * result + (includeLocation ? 1 : 0);
799        result = 31 * result + (endOfBatch ? 1 : 0);
800        // Check:ON: MagicNumber
801        return result;
802    }
803
804    /**
805     * Proxy pattern used to serialize the LogEvent.
806     */
807    static class LogEventProxy implements Serializable {
808
809        private static final long serialVersionUID = -8634075037355293699L;
810        private final String loggerFQCN;
811        private final Marker marker;
812        private final Level level;
813        private final String loggerName;
814        private final Message message;
815        private final long timeMillis;
816        private final transient Throwable thrown;
817        private final ThrowableProxy thrownProxy;
818        private final Map<String, String> contextMap;
819        private final ThreadContext.ContextStack contextStack;
820        /** @since 2.6 */
821        private final long threadId;
822        private final String threadName;
823        /** @since 2.6 */
824        private final int threadPriority;
825        private final StackTraceElement source;
826        private final boolean isLocationRequired;
827        private final boolean isEndOfBatch;
828        /** @since 2.4 */
829        private final transient long nanoTime;
830
831        public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
832            this.loggerFQCN = event.loggerFqcn;
833            this.marker = event.marker;
834            this.level = event.level;
835            this.loggerName = event.loggerName;
836            this.message = event.message instanceof ReusableMessage
837                    ? memento((ReusableMessage) event.message)
838                    : event.message;
839            this.timeMillis = event.timeMillis;
840            this.thrown = event.thrown;
841            this.thrownProxy = event.thrownProxy;
842            this.contextMap = event.contextMap;
843            this.contextStack = event.contextStack;
844            this.source = includeLocation ? event.getSource() : null;
845            this.threadId = event.getThreadId();
846            this.threadName = event.getThreadName();
847            this.threadPriority = event.getThreadPriority();
848            this.isLocationRequired = includeLocation;
849            this.isEndOfBatch = event.endOfBatch;
850            this.nanoTime = event.nanoTime;
851        }
852
853        public LogEventProxy(final LogEvent event, final boolean includeLocation) {
854            this.loggerFQCN = event.getLoggerFqcn();
855            this.marker = event.getMarker();
856            this.level = event.getLevel();
857            this.loggerName = event.getLoggerName();
858
859            final Message msg = event.getMessage();
860            this.message = msg instanceof ReusableMessage
861                    ? memento((ReusableMessage) msg)
862                    : msg;
863            this.timeMillis = event.getTimeMillis();
864            this.thrown = event.getThrown();
865            this.thrownProxy = event.getThrownProxy();
866            this.contextMap = event.getContextMap();
867            this.contextStack = event.getContextStack();
868            this.source = includeLocation ? event.getSource() : null;
869            this.threadId = event.getThreadId();
870            this.threadName = event.getThreadName();
871            this.threadPriority = event.getThreadPriority();
872            this.isLocationRequired = includeLocation;
873            this.isEndOfBatch = event.isEndOfBatch();
874            this.nanoTime = event.getNanoTime();
875        }
876
877        private Message memento(final ReusableMessage message) {
878            return message.memento();
879        }
880
881        /**
882         * Returns a Log4jLogEvent using the data in the proxy.
883         * @return Log4jLogEvent.
884         */
885        protected Object readResolve() {
886            final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
887                    thrownProxy, contextMap, contextStack, threadId, threadName, threadPriority, source, timeMillis, nanoTime);
888            result.setEndOfBatch(isEndOfBatch);
889            result.setIncludeLocation(isLocationRequired);
890            return result;
891        }
892    }
893
894}