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 org.apache.logging.log4j.Level;
20  import org.apache.logging.log4j.ThreadContext;
21  import org.apache.logging.log4j.Marker;
22  import org.apache.logging.log4j.core.LogEvent;
23  import org.apache.logging.log4j.core.config.Property;
24  import org.apache.logging.log4j.message.LoggerNameAwareMessage;
25  import org.apache.logging.log4j.message.Message;
26  import org.apache.logging.log4j.message.TimestampMessage;
27  
28  import java.io.InvalidObjectException;
29  import java.io.ObjectInputStream;
30  import java.io.Serializable;
31  import java.util.List;
32  import java.util.Map;
33  
34  /**
35   * Implementation of a LogEvent.
36   */
37  public class Log4jLogEvent implements LogEvent, Serializable {
38  
39      private static final long serialVersionUID = -1351367343806656055L;
40      private static final String NOT_AVAIL = "?";
41      private final String fqcnOfLogger;
42      private final Marker marker;
43      private final Level level;
44      private final String name;
45      private final Message message;
46      private final long timestamp;
47      private final ThrowableProxy throwable;
48      private final Map<String, String> mdc;
49      private final ThreadContext.ContextStack ndc;
50      private String threadName = null;
51      private StackTraceElement location;
52      private boolean includeLocation;
53      private boolean endOfBatch = false;
54  
55      /**
56       * Constructor.
57       * @param loggerName The name of the Logger.
58       * @param marker The Marker or null.
59       * @param fqcn The fully qualified class name of the caller.
60       * @param level The logging Level.
61       * @param message The Message.
62       * @param t A Throwable or null.
63       */
64      public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level,
65                           final Message message, final Throwable t) {
66          this(loggerName, marker, fqcn, level, message, null, t);
67      }
68  
69      /**
70       * Constructor.
71       * @param loggerName The name of the Logger.
72       * @param marker The Marker or null.
73       * @param fqcn The fully qualified class name of the caller.
74       * @param level The logging Level.
75       * @param message The Message.
76       * @param properties properties to add to the event.
77       * @param t A Throwable or null.
78       */
79      public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level,
80                           final Message message, final List<Property> properties, final Throwable t) {
81          this(loggerName, marker, fqcn, level, message, t,
82              createMap(properties),
83              ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null,
84              null, System.currentTimeMillis());
85      }
86  
87      /**
88       * Constructor.
89       * @param loggerName The name of the Logger.
90       * @param marker The Marker or null.
91       * @param fqcn The fully qualified class name of the caller.
92       * @param level The logging Level.
93       * @param message The Message.
94       * @param t A Throwable or null.
95       * @param mdc The mapped diagnostic context.
96       * @param ndc the nested diagnostic context.
97       * @param threadName The name of the thread.
98       * @param location The locations of the caller.
99       * @param timestamp The timestamp of the event.
100      */
101     public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level,
102                          final Message message, final Throwable t,
103                          final Map<String, String> mdc, final ThreadContext.ContextStack ndc, final String threadName,
104                          final StackTraceElement location, final long timestamp) {
105         name = loggerName;
106         this.marker = marker;
107         this.fqcnOfLogger = fqcn;
108         this.level = level;
109         this.message = message;
110         this.throwable = t == null ? null : t instanceof ThrowableProxy ? (ThrowableProxy) t : new ThrowableProxy(t);
111         this.mdc = mdc;
112         this.ndc = ndc;
113         this.timestamp = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp;
114         this.threadName = threadName;
115         this.location = location;
116         if (message != null && message instanceof LoggerNameAwareMessage) {
117             ((LoggerNameAwareMessage) message).setLoggerName(name);
118         }
119     }
120 
121     private static Map<String, String> createMap(final List<Property> properties) {
122         if (ThreadContext.isEmpty() && (properties == null || properties.size() == 0)) {
123             return null;
124         }
125         if (properties == null || properties.size() == 0) {
126             return ThreadContext.getImmutableContext();
127         }
128         final Map<String, String> map = ThreadContext.getContext();
129 
130         for (final Property prop : properties) {
131             if (!map.containsKey(prop.getName())) {
132                 map.put(prop.getName(), prop.getValue());
133             }
134         }
135         return new ThreadContext.ImmutableMap(map);
136     }
137 
138     /**
139      * Returns the logging Level.
140      * @return the Level associated with this event.
141      */
142     public Level getLevel() {
143         return level;
144     }
145 
146     /**
147      * Returns the name of the Logger used to generate the event.
148      * @return The Logger name.
149      */
150     public String getLoggerName() {
151         return name;
152     }
153 
154     /**
155      * Returns the Message associated with the event.
156      * @return The Message.
157      */
158     public Message getMessage() {
159         return message;
160     }
161 
162     /**
163      * Returns the name of the Thread on which the event was generated.
164      * @return The name of the Thread.
165      */
166     public String getThreadName() {
167         if (threadName == null) {
168             threadName = Thread.currentThread().getName();
169         }
170         return threadName;
171     }
172 
173     /**
174      * Returns the time in milliseconds from the epoch when the event occurred.
175      * @return The time the event occurred.
176      */
177     public long getMillis() {
178         return timestamp;
179     }
180 
181     /**
182      * Returns the Throwable associated with the event, or null.
183      * @return The Throwable associated with the event.
184      */
185     public Throwable getThrown() {
186         return throwable;
187     }
188 
189     /**
190      * Returns the Marker associated with the event, or null.
191      * @return the Marker associated with the event.
192      */
193     public Marker getMarker() {
194         return marker;
195     }
196 
197     /**
198      * The fully qualified class name of the class that was called by the caller.
199      * @return the fully qualified class name of the class that is performing logging.
200      */
201     public String getFQCN() {
202         return fqcnOfLogger;
203     }
204 
205     /**
206      * Returns the immutable copy of the ThreadContext Map.
207      * @return The context Map.
208      */
209     public Map<String, String> getContextMap() {
210         return mdc == null ? ThreadContext.EMPTY_MAP : mdc;
211     }
212 
213     /**
214      * Returns an immutable copy of the ThreadContext stack.
215      * @return The context Stack.
216      */
217     public ThreadContext.ContextStack getContextStack() {
218         return ndc == null ? ThreadContext.EMPTY_STACK : ndc;
219     }
220 
221     /**
222      * Returns the StackTraceElement for the caller. This will be the entry that occurs right
223      * before the first occurrence of FQCN as a class name.
224      * @return the StackTraceElement for the caller.
225      */
226     public StackTraceElement getSource() {
227         if (location != null) {
228             return location;
229         }
230         if (fqcnOfLogger == null || !includeLocation) {
231             return null;
232         }
233         location = calcLocation(fqcnOfLogger);
234         return location;
235     }
236 
237     public static StackTraceElement calcLocation(String fqcnOfLogger) {
238         if (fqcnOfLogger == null) {
239             return null;
240         }
241         final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
242         boolean next = false;
243         for (final StackTraceElement element : stackTrace) {
244             final String className = element.getClassName();
245             if (next) {
246                 if (fqcnOfLogger.equals(className)) {
247                     continue;
248                 }
249                 return element;
250             }
251 
252             if (fqcnOfLogger.equals(className)) {
253                 next = true;
254             } else if (NOT_AVAIL.equals(className)) {
255                 break;
256             }
257         }
258         return null;
259     }
260 
261     public boolean isIncludeLocation() {
262         return includeLocation;
263     }
264 
265     public void setIncludeLocation(boolean includeLocation) {
266         this.includeLocation = includeLocation;
267     }
268 
269     public boolean isEndOfBatch() {
270         return endOfBatch;
271     }
272 
273     public void setEndOfBatch(boolean endOfBatch) {
274         this.endOfBatch = endOfBatch;
275     }
276 
277     /**
278      * Creates a LogEventProxy that can be serialized.
279      * @return a LogEventProxy.
280      */
281     protected Object writeReplace() {
282         return new LogEventProxy(this, this.includeLocation);
283     }
284 
285     public static Serializable serialize(final Log4jLogEvent event,
286             final boolean includeLocation) {
287         return new LogEventProxy(event, includeLocation);
288     }
289 
290     public static Log4jLogEvent deserialize(final Serializable event) {
291         if (event == null) {
292             throw new NullPointerException("Event cannot be null");
293         }
294         if (event instanceof LogEventProxy) {
295             final LogEventProxy proxy = (LogEventProxy) event;
296             Log4jLogEvent result = new Log4jLogEvent(proxy.name, proxy.marker,
297                     proxy.fqcnOfLogger, proxy.level, proxy.message,
298                     proxy.throwable, proxy.mdc, proxy.ndc, proxy.threadName,
299                     proxy.location, proxy.timestamp);
300             result.setEndOfBatch(proxy.isEndOfBatch);
301             result.setIncludeLocation(proxy.isLocationRequired);
302             return result;
303         }
304         throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
305     }
306 
307     private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
308         throw new InvalidObjectException("Proxy required");
309     }
310 
311     @Override
312     public String toString() {
313         final StringBuilder sb = new StringBuilder();
314         final String n = name.length() == 0 ? "root" : name;
315         sb.append("Logger=").append(n);
316         sb.append(" Level=").append(level.name());
317         sb.append(" Message=").append(message.getFormattedMessage());
318         return sb.toString();
319     }
320 
321     /**
322      * Proxy pattern used to serialize the LogEvent.
323      */
324     private static class LogEventProxy implements Serializable {
325 
326         private static final long serialVersionUID = -7139032940312647146L;
327         private final String fqcnOfLogger;
328         private final Marker marker;
329         private final Level level;
330         private final String name;
331         private final Message message;
332         private final long timestamp;
333         private final Throwable throwable;
334         private final Map<String, String> mdc;
335         private final ThreadContext.ContextStack ndc;
336         private final String threadName;
337         private final StackTraceElement location;
338         private final boolean isLocationRequired;
339         private final boolean isEndOfBatch;
340 
341         public LogEventProxy(final Log4jLogEvent event, boolean includeLocation) {
342             this.fqcnOfLogger = event.fqcnOfLogger;
343             this.marker = event.marker;
344             this.level = event.level;
345             this.name = event.name;
346             this.message = event.message;
347             this.timestamp = event.timestamp;
348             this.throwable = event.throwable;
349             this.mdc = event.mdc;
350             this.ndc = event.ndc;
351             this.location = includeLocation ? event.getSource() : null;
352             this.threadName = event.getThreadName();
353             this.isLocationRequired = includeLocation;
354             this.isEndOfBatch = event.endOfBatch;
355         }
356 
357         /**
358          * Returns a Log4jLogEvent using the data in the proxy.
359          * @return Log4jLogEvent.
360          */
361         protected Object readResolve() {
362             Log4jLogEvent result = new Log4jLogEvent(name, marker, fqcnOfLogger,
363                     level, message, throwable, mdc, ndc, threadName, location,
364                     timestamp);
365             result.setEndOfBatch(isEndOfBatch);
366             result.setIncludeLocation(isLocationRequired);
367             return result;
368         }
369     }
370 }