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