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.Collections;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.logging.log4j.Level;
28  import org.apache.logging.log4j.Marker;
29  import org.apache.logging.log4j.ThreadContext;
30  import org.apache.logging.log4j.core.LogEvent;
31  import org.apache.logging.log4j.core.config.Property;
32  import org.apache.logging.log4j.core.util.Clock;
33  import org.apache.logging.log4j.core.util.ClockFactory;
34  import org.apache.logging.log4j.message.LoggerNameAwareMessage;
35  import org.apache.logging.log4j.message.Message;
36  import org.apache.logging.log4j.message.TimestampMessage;
37  import org.apache.logging.log4j.util.Strings;
38  
39  /**
40   * Implementation of a LogEvent.
41   */
42  public class Log4jLogEvent implements LogEvent {
43  
44      private static final long serialVersionUID = -1351367343806656055L;
45      private static final Clock clock = ClockFactory.getClock();
46      private final String loggerFqcn;
47      private final Marker marker;
48      private final Level level;
49      private final String loggerName;
50      private final Message message;
51      private final long timeMillis;
52      private transient final Throwable thrown;
53      private ThrowableProxy thrownProxy;
54      private final Map<String, String> contextMap;
55      private final ThreadContext.ContextStack contextStack;
56      private String threadName = null;
57      private StackTraceElement source;
58      private boolean includeLocation;
59      private boolean endOfBatch = false;
60  
61      public Log4jLogEvent() {
62          this(clock.currentTimeMillis());
63      }
64  
65      /**
66       *
67       */
68      public Log4jLogEvent(final long timestamp) {
69          this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, timestamp);
70      }
71  
72      /**
73       * Constructor.
74       * @param loggerName The name of the Logger.
75       * @param marker The Marker or null.
76       * @param loggerFQCN The fully qualified class name of the caller.
77       * @param level The logging Level.
78       * @param message The Message.
79       * @param t A Throwable or null.
80       */
81      public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
82                           final Message message, final Throwable t) {
83          this(loggerName, marker, loggerFQCN, level, message, null, t);
84      }
85  
86      /**
87       * Constructor.
88       * @param loggerName The name of the Logger.
89       * @param marker The Marker or null.
90       * @param loggerFQCN The fully qualified class name of the caller.
91       * @param level The logging Level.
92       * @param message The Message.
93       * @param properties properties to add to the event.
94       * @param t A Throwable or null.
95       */
96      public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
97                           final Message message, final List<Property> properties, final Throwable t) {
98          this(loggerName, marker, loggerFQCN, level, message, t,
99              createMap(properties),
100             ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null,
101             null, clock.currentTimeMillis()); // LOG4J2-628 use log4j.Clock for timestamps
102     }
103 
104     /**
105      * Constructor.
106      * @param loggerName The name of the Logger.
107      * @param marker The Marker or null.
108      * @param loggerFQCN The fully qualified class name of the caller.
109      * @param level The logging Level.
110      * @param message The Message.
111      * @param t A Throwable or null.
112      * @param mdc The mapped diagnostic context.
113      * @param ndc the nested diagnostic context.
114      * @param threadName The name of the thread.
115      * @param location The locations of the caller.
116      * @param timestamp The timestamp of the event.
117      */
118     public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
119                          final Message message, final Throwable t, final Map<String, String> mdc,
120                          final ThreadContext.ContextStack ndc, final String threadName,
121                          final StackTraceElement location, final long timestamp) {
122         this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName,
123                 location, timestamp);
124     }
125 
126     /**
127      * Create a new LogEvent.
128      * @param loggerName The name of the Logger.
129      * @param marker The Marker or null.
130      * @param loggerFQCN The fully qualified class name of the caller.
131      * @param level The logging Level.
132      * @param message The Message.
133      * @param thrown A Throwable or null.
134      * @param thrownProxy A ThrowableProxy or null.
135      * @param mdc The mapped diagnostic context.
136      * @param ndc the nested diagnostic context.
137      * @param threadName The name of the thread.
138      * @param location The locations of the caller.
139      * @param timestamp The timestamp of the event.
140      */
141     public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
142                                             final Level level, final Message message, final Throwable thrown, 
143                                             final ThrowableProxy thrownProxy,
144                                             final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
145                                             final String threadName, final StackTraceElement location,
146                                             final long timestamp) {
147         final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 
148                 thrownProxy, mdc, ndc, threadName, location, timestamp);
149         return result;
150     }
151 
152     /**
153      * Constructor.
154      * @param loggerName The name of the Logger.
155      * @param marker The Marker or null.
156      * @param loggerFQCN The fully qualified class name of the caller.
157      * @param level The logging Level.
158      * @param message The Message.
159      * @param thrown A Throwable or null.
160      * @param thrownProxy A ThrowableProxy or null.
161      * @param contextMap The mapped diagnostic context.
162      * @param contextStack the nested diagnostic context.
163      * @param threadName The name of the thread.
164      * @param source The locations of the caller.
165      * @param timestamp The timestamp of the event.
166      */
167     private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
168             final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 
169             final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack, 
170             final String threadName, final StackTraceElement source, final long timestamp) {
171         this.loggerName = loggerName;
172         this.marker = marker;
173         this.loggerFqcn = loggerFQCN;
174         this.level = (level == null) ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
175         this.message = message;
176         this.thrown = thrown;
177         this.thrownProxy = thrownProxy;
178         this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap;
179         this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
180         this.timeMillis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp;
181         this.threadName = threadName;
182         this.source = source;
183         if (message != null && message instanceof LoggerNameAwareMessage) {
184             ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
185         }
186     }
187 
188     private static Map<String, String> createMap(final List<Property> properties) {
189         final Map<String, String> contextMap = ThreadContext.getImmutableContext();
190         if (contextMap == null && (properties == null || properties.isEmpty())) {
191             return null;
192         }
193         if (properties == null || properties.isEmpty()) {
194             return contextMap; // contextMap is not null
195         }
196         final Map<String, String> map = new HashMap<String, String>(contextMap);
197 
198         for (final Property prop : properties) {
199             if (!map.containsKey(prop.getName())) {
200                 map.put(prop.getName(), prop.getValue());
201             }
202         }
203         return Collections.unmodifiableMap(map);
204     }
205 
206     /**
207      * Returns the logging Level.
208      * @return the Level associated with this event.
209      */
210     @Override
211     public Level getLevel() {
212         return level;
213     }
214 
215     /**
216      * Returns the name of the Logger used to generate the event.
217      * @return The Logger name.
218      */
219     @Override
220     public String getLoggerName() {
221         return loggerName;
222     }
223 
224     /**
225      * Returns the Message associated with the event.
226      * @return The Message.
227      */
228     @Override
229     public Message getMessage() {
230         return message;
231     }
232 
233     /**
234      * Returns the name of the Thread on which the event was generated.
235      * @return The name of the Thread.
236      */
237     @Override
238     public String getThreadName() {
239         if (threadName == null) {
240             threadName = Thread.currentThread().getName();
241         }
242         return threadName;
243     }
244 
245     /**
246      * Returns the time in milliseconds from the epoch when the event occurred.
247      * @return The time the event occurred.
248      */
249     @Override
250     public long getTimeMillis() {
251         return timeMillis;
252     }
253 
254     /**
255      * Returns the Throwable associated with the event, or null.
256      * @return The Throwable associated with the event.
257      */
258     @Override
259     public Throwable getThrown() {
260         return thrown;
261     }
262 
263     /**
264      * Returns the ThrowableProxy associated with the event, or null.
265      * @return The ThrowableProxy associated with the event.
266      */
267     @Override
268     public ThrowableProxy getThrownProxy() {
269         if (thrownProxy == null && thrown != null) {
270             thrownProxy = new ThrowableProxy(thrown);
271         }
272         return thrownProxy;
273     }
274 
275 
276     /**
277      * Returns the Marker associated with the event, or null.
278      * @return the Marker associated with the event.
279      */
280     @Override
281     public Marker getMarker() {
282         return marker;
283     }
284 
285     /**
286      * The fully qualified class name of the class that was called by the caller.
287      * @return the fully qualified class name of the class that is performing logging.
288      */
289     @Override
290     public String getLoggerFqcn() {
291         return loggerFqcn;
292     }
293 
294     /**
295      * Returns the immutable copy of the ThreadContext Map.
296      * @return The context Map.
297      */
298     @Override
299     public Map<String, String> getContextMap() {
300         return contextMap;
301     }
302 
303     /**
304      * Returns an immutable copy of the ThreadContext stack.
305      * @return The context Stack.
306      */
307     @Override
308     public ThreadContext.ContextStack getContextStack() {
309         return contextStack;
310     }
311 
312     /**
313      * Returns the StackTraceElement for the caller. This will be the entry that occurs right
314      * before the first occurrence of FQCN as a class name.
315      * @return the StackTraceElement for the caller.
316      */
317     @Override
318     public StackTraceElement getSource() {
319         if (source != null) {
320             return source;
321         }
322         if (loggerFqcn == null || !includeLocation) {
323             return null;
324         }
325         source = calcLocation(loggerFqcn);
326         return source;
327     }
328 
329     public static StackTraceElement calcLocation(final String fqcnOfLogger) {
330         if (fqcnOfLogger == null) {
331             return null;
332         }
333         final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
334         StackTraceElement last = null;
335         for (int i = stackTrace.length - 1; i > 0; i--) {
336             final String className = stackTrace[i].getClassName();
337             if (fqcnOfLogger.equals(className)) {
338                 return last;
339             }
340             last = stackTrace[i];
341         }
342         return null;
343     }
344 
345     @Override
346     public boolean isIncludeLocation() {
347         return includeLocation;
348     }
349 
350     @Override
351     public void setIncludeLocation(final boolean includeLocation) {
352         this.includeLocation = includeLocation;
353     }
354 
355     @Override
356     public boolean isEndOfBatch() {
357         return endOfBatch;
358     }
359 
360     @Override
361     public void setEndOfBatch(final boolean endOfBatch) {
362         this.endOfBatch = endOfBatch;
363     }
364 
365     /**
366      * Creates a LogEventProxy that can be serialized.
367      * @return a LogEventProxy.
368      */
369     protected Object writeReplace() {
370         getThrownProxy(); // ensure ThrowableProxy is initialized
371         return new LogEventProxy(this, this.includeLocation);
372     }
373 
374     public static Serializable serialize(final Log4jLogEvent event,
375             final boolean includeLocation) {
376         event.getThrownProxy(); // ensure ThrowableProxy is initialized
377         return new LogEventProxy(event, includeLocation);
378     }
379 
380     public static boolean canDeserialize(final Serializable event) {
381         return event instanceof LogEventProxy;
382     }
383 
384     public static Log4jLogEvent deserialize(final Serializable event) {
385         if (event == null) {
386             throw new NullPointerException("Event cannot be null");
387         }
388         if (event instanceof LogEventProxy) {
389             final LogEventProxy proxy = (LogEventProxy) event;
390             final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
391                     proxy.loggerFQCN, proxy.level, proxy.message,
392                     proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName,
393                     proxy.source, proxy.timeMillis);
394             result.setEndOfBatch(proxy.isEndOfBatch);
395             result.setIncludeLocation(proxy.isLocationRequired);
396             return result;
397         }
398         throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
399     }
400 
401     private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
402         throw new InvalidObjectException("Proxy required");
403     }
404 
405     @Override
406     public String toString() {
407         final StringBuilder sb = new StringBuilder();
408         final String n = loggerName.isEmpty() ? "root" : loggerName;
409         sb.append("Logger=").append(n);
410         sb.append(" Level=").append(level.name());
411         sb.append(" Message=").append(message.getFormattedMessage());
412         return sb.toString();
413     }
414 
415     @Override
416     public boolean equals(final Object o) {
417         if (this == o) {
418             return true;
419         }
420         if (o == null || getClass() != o.getClass()) {
421             return false;
422         }
423 
424         final Log4jLogEvent that = (Log4jLogEvent) o;
425 
426         if (endOfBatch != that.endOfBatch) {
427             return false;
428         }
429         if (includeLocation != that.includeLocation) {
430             return false;
431         }
432         if (timeMillis != that.timeMillis) {
433             return false;
434         }
435         if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
436             return false;
437         }
438         if (level != null ? !level.equals(that.level) : that.level != null) {
439             return false;
440         }
441         if (source != null ? !source.equals(that.source) : that.source != null) {
442             return false;
443         }
444         if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
445             return false;
446         }
447         if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) {
448             return false;
449         }
450         if (!message.equals(that.message)) {
451             return false;
452         }
453         if (!loggerName.equals(that.loggerName)) {
454             return false;
455         }
456         if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
457             return false;
458         }
459         if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
460             return false;
461         }
462         if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
463             return false;
464         }
465         if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
466             return false;
467         }
468 
469         return true;
470     }
471 
472     @Override
473     public int hashCode() {
474         int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
475         result = 31 * result + (marker != null ? marker.hashCode() : 0);
476         result = 31 * result + (level != null ? level.hashCode() : 0);
477         result = 31 * result + loggerName.hashCode();
478         result = 31 * result + message.hashCode();
479         result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32));
480         result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
481         result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
482         result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0);
483         result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
484         result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
485         result = 31 * result + (source != null ? source.hashCode() : 0);
486         result = 31 * result + (includeLocation ? 1 : 0);
487         result = 31 * result + (endOfBatch ? 1 : 0);
488         return result;
489     }
490 
491     /**
492      * Proxy pattern used to serialize the LogEvent.
493      */
494     private static class LogEventProxy implements Serializable {
495 
496         private static final long serialVersionUID = -7139032940312647146L;
497         private final String loggerFQCN;
498         private final Marker marker;
499         private final Level level;
500         private final String loggerName;
501         private final Message message;
502         private final long timeMillis;
503         private final transient Throwable thrown;
504         private final ThrowableProxy thrownProxy;
505         private final Map<String, String> contextMap;
506         private final ThreadContext.ContextStack contextStack;
507         private final String threadName;
508         private final StackTraceElement source;
509         private final boolean isLocationRequired;
510         private final boolean isEndOfBatch;
511 
512         public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
513             this.loggerFQCN = event.loggerFqcn;
514             this.marker = event.marker;
515             this.level = event.level;
516             this.loggerName = event.loggerName;
517             this.message = event.message;
518             this.timeMillis = event.timeMillis;
519             this.thrown = event.thrown;
520             this.thrownProxy = event.thrownProxy;
521             this.contextMap = event.contextMap;
522             this.contextStack = event.contextStack;
523             this.source = includeLocation ? event.getSource() : null;
524             this.threadName = event.getThreadName();
525             this.isLocationRequired = includeLocation;
526             this.isEndOfBatch = event.endOfBatch;
527         }
528 
529         /**
530          * Returns a Log4jLogEvent using the data in the proxy.
531          * @return Log4jLogEvent.
532          */
533         protected Object readResolve() {
534             final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
535                     thrownProxy, contextMap, contextStack, threadName, source, timeMillis);
536             result.setEndOfBatch(isEndOfBatch);
537             result.setIncludeLocation(isLocationRequired);
538             return result;
539         }
540     }
541 }