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