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