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.message.LoggerNameAwareMessage;
24  import org.apache.logging.log4j.message.Message;
25  import org.apache.logging.log4j.message.TimestampMessage;
26  
27  import java.io.InvalidObjectException;
28  import java.io.ObjectInputStream;
29  import java.io.Serializable;
30  import java.util.HashMap;
31  import java.util.Map;
32  import java.util.Stack;
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 Stack<String> 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, t, ThreadContext.getContext(), ThreadContext.cloneStack(), null,
64               null, System.currentTimeMillis());
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 t A Throwable or null.
75       * @param mdc The mapped diagnostic context.
76       * @param ndc the nested diagnostic context.
77       * @param threadName The name of the thread.
78       * @param location The locations of the caller.
79       * @param timestamp The timestamp of the event.
80       */
81      public Log4jLogEvent(String loggerName, Marker marker, String fqcn, Level level, Message message, Throwable t,
82                           Map<String, String> mdc, Stack<String> ndc, String threadName, StackTraceElement location,
83                           long timestamp) {
84          name = loggerName;
85          this.marker = marker;
86          this.fqcnOfLogger = fqcn;
87          this.level = level;
88          this.message = message;
89          this.throwable = t == null ? null : new ThrowableProxy(t);
90          this.mdc = mdc;
91          this.ndc = ndc;
92          this.timestamp = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp;
93          this.threadName = threadName;
94          this.location = location;
95          if (message != null && message instanceof LoggerNameAwareMessage) {
96              ((LoggerNameAwareMessage) message).setLoggerName(name);
97          }
98      }
99  
100     /**
101      * Returns the logging Level.
102      * @return the Level associated with this event.
103      */
104     public Level getLevel() {
105         return level;
106     }
107 
108     /**
109      * Return the name of the Logger used to generate the event.
110      * @return The Logger name.
111      */
112     public String getLoggerName() {
113         return name;
114     }
115 
116     /**
117      * Return the Message associated with the event.
118      * @return The Message.
119      */
120     public Message getMessage() {
121         return message;
122     }
123 
124     /**
125      * Return the name of the Thread on which the event was generated.
126      * @return The name of the Thread.
127      */
128     public String getThreadName() {
129         if (threadName == null) {
130             threadName = Thread.currentThread().getName();
131         }
132         return threadName;
133     }
134 
135     /**
136      * Return the time in milliseconds from the epoch when the event occurred.
137      * @return The time the event occurred.
138      */
139     public long getMillis() {
140         return timestamp;
141     }
142 
143     /**
144      * Return the Throwable associated with the event, or null.
145      * @return The Throwable associated with the event.
146      */
147     public Throwable getThrown() {
148         return throwable;
149     }
150 
151     /**
152      * Return the Marker associated with the event, or null.
153      * @return the Marker associated with the event.
154      */
155     public Marker getMarker() {
156         return marker;
157     }
158 
159     /**
160      * The fully qualified class name of the class that was called by the caller.
161      * @return the fully qualified class name of the class that is performing logging.
162      */
163     public String getFQCN() {
164         return fqcnOfLogger;
165     }
166 
167     /**
168      * @doubt Allows direct access to the map passed into the constructor, would allow appender
169      * or layout to manipulate event as seen by other appenders.
170      * @return The context Map.
171      */
172     public Map<String, String> getContextMap() {
173         return mdc;
174     }
175 
176     /**
177      * @doubt Allows direct access to the map passed into the constructor, would allow appender
178      * or layout to manipulate event as seen by other appenders.
179      * @return The context Stack.
180      */
181     public Stack<String> getContextStack() {
182         return ndc;
183     }
184 
185     /**
186      * Return the StackTraceElement for the caller. This will be the entry that occurs right
187      * before the first occurrence of FQCN as a class name.
188      * @return the StackTraceElement for the caller.
189      */
190     public StackTraceElement getSource() {
191         if (fqcnOfLogger == null) {
192             return null;
193         }
194         if (location == null) {
195             StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
196             boolean next = false;
197             for (StackTraceElement element : stackTrace) {
198                 String className = element.getClassName();
199                 if (next) {
200                     if (fqcnOfLogger.equals(className)) {
201                         continue;
202                     }
203                     location = element;
204                     break;
205                 }
206 
207                 if (fqcnOfLogger.equals(className)) {
208                     next = true;
209                 } else if (NOT_AVAIL.equals(className)) {
210                     break;
211                 }
212             }
213         }
214 
215         return location;
216     }
217 
218     /**
219      * Creates a LogEventProxy that can be serialized.
220      * @return a LogEventProxy.
221      */
222     protected Object writeReplace() {
223         return new LogEventProxy(this);
224     }
225 
226     private void readObject(ObjectInputStream stream) throws InvalidObjectException {
227         throw new InvalidObjectException("Proxy required");
228     }
229 
230     @Override
231     public String toString() {
232         StringBuilder sb = new StringBuilder();
233         String n = name.length() == 0 ? "root" : name;
234         sb.append("Logger=").append(n);
235         sb.append(" Level=").append(level.name());
236         sb.append(" Message").append(message.getFormattedMessage());
237         return sb.toString();
238     }
239 
240     /**
241      * Proxy pattern used to serialize the LogEvent.
242      */
243     private static class LogEventProxy implements Serializable {
244 
245         private static final long serialVersionUID = -7139032940312647146L;
246         private final String fqcnOfLogger;
247         private final Marker marker;
248         private final Level level;
249         private final String name;
250         private final Message message;
251         private final long timestamp;
252         private final Throwable throwable;
253         private final HashMap<String, String> mdc;
254         private final Stack<String> ndc;
255         private String threadName;
256         private StackTraceElement location;
257 
258         public LogEventProxy(Log4jLogEvent event) {
259             this.fqcnOfLogger = event.fqcnOfLogger;
260             this.marker = event.marker;
261             this.level = event.level;
262             this.name = event.name;
263             this.message = event.message;
264             this.timestamp = event.timestamp;
265             this.throwable = event.throwable;
266             this.mdc = new HashMap<String, String>(event.mdc);
267             this.ndc = event.ndc;
268             this.location = event.getSource();
269             this.threadName = event.getThreadName();
270         }
271 
272         /**
273          * Return a Log4jLogEvent using the data in the proxy.
274          * @return Log4jLogEvent.
275          */
276         protected Object readResolve() {
277             return new Log4jLogEvent(name, marker, fqcnOfLogger, level, message, throwable, mdc, ndc, threadName,
278                                      location, timestamp);
279         }
280 
281     }
282 
283 }