View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  
11  package ch.qos.logback.classic.spi;
12  
13  import java.io.IOException;
14  import java.io.ObjectInputStream;
15  import java.io.ObjectOutputStream;
16  import java.io.Serializable;
17  import java.util.HashMap;
18  import java.util.Map;
19  
20  import org.slf4j.MDC;
21  import org.slf4j.Marker;
22  import org.slf4j.helpers.MessageFormatter;
23  import org.slf4j.impl.LogbackMDCAdapter;
24  
25  import ch.qos.logback.classic.Level;
26  import ch.qos.logback.classic.Logger;
27  
28  /**
29   * The internal representation of logging events. When an affirmative decision
30   * is made to log then a <code>LoggingEvent</code> instance is created. This
31   * instance is passed around to the different Logback components.
32   * 
33   * <p> Writers of Logback components such as appenders should be aware of that
34   * some of the LoggingEvent fields are initialized lazily. Therefore, an
35   * appender wishing to output data to be later correctly read by a receiver,
36   * must initialize "lazy" fields prior to writing them out. See the
37   * {@link #prepareForDeferredProcessing()} method for the exact list. </p>
38   * 
39   * @author Ceki G&uuml;lc&uuml;
40   * @author S&eacute;bastien Pennec
41   */
42  public class LoggingEvent implements Serializable {
43  
44    private static final long serialVersionUID = 3075964498087694229L;
45  
46    private static final int NULL_ARGUMENT_ARRAY = -1;
47    private static final String NULL_ARGUMENT_ARRAY_ELEMENT = "NULL_ARGUMENT_ARRAY_ELEMENT";
48  
49    /**
50     * 
51     */
52    private static long startTime = System.currentTimeMillis();
53  
54    /**
55     * Fully qualified name of the calling Logger class. This field does not
56     * survive serialization.
57     * 
58     * <p> Note that the getCallerInformation() method relies on this fact.
59     */
60    transient String fqnOfLoggerClass;
61  
62    /**
63     * The name of thread in which this logging event was generated.
64     */
65    private String threadName;
66  
67    /**
68     * Level of logging event.
69     * 
70     * <p> This field should not be accessed directly. You shoud use the {@link
71     * #getLevel} method instead. </p>
72     * 
73     */
74    private transient Level level;
75  
76    private String message;
77  
78    // we gain significant space at serialization time by marking
79    // formattedMessage as transient and constructing it lazily in
80    // getFormmatedMessage()
81    private transient String formattedMessage;
82  
83    private transient Object[] argumentArray;
84  
85    private ThrowableProxy throwableProxy;
86  
87    private CallerData[] callerDataArray;
88    private LoggerRemoteView loggerRemoteView;
89  
90    private Marker marker;
91  
92    private Map<String, String> mdcPropertyMap;
93  
94    /**
95     * The number of milliseconds elapsed from 1/1/1970 until logging event was
96     * created.
97     */
98    private long timeStamp;
99  
100   public LoggingEvent() {
101   }
102 
103   public LoggingEvent(String fqcn, Logger logger, Level level, String message,
104       Throwable throwable, Object[] argArray) {
105     this.fqnOfLoggerClass = fqcn;
106     this.loggerRemoteView = logger.getLoggerRemoteView();
107     this.level = level;
108     this.message = message;
109 
110     if (throwable != null) {
111       this.throwableProxy = new ThrowableProxy(throwable);
112     }
113 
114     // bug 85 (we previously failed to set this.argumentArray)
115     this.argumentArray = argArray;
116 
117     timeStamp = System.currentTimeMillis();
118 
119     // the case is ugly but under the circumstances acceptable
120     LogbackMDCAdapter logbackMDCAdapter = (LogbackMDCAdapter) MDC
121         .getMDCAdapter();
122     mdcPropertyMap = logbackMDCAdapter.getPropertyMap();
123   }
124 
125   public void setArgumentArray(Object[] argArray) {
126     if (this.argumentArray != null) {
127       throw new IllegalStateException("argArray has been already set");
128     }
129     this.argumentArray = argArray;
130   }
131 
132   public Object[] getArgumentArray() {
133     return this.argumentArray;
134   }
135 
136   public Level getLevel() {
137     return level;
138   }
139 
140   public String getThreadName() {
141     if (threadName == null) {
142       threadName = (Thread.currentThread()).getName();
143     }
144     return threadName;
145   }
146 
147   /**
148    * @param threadName
149    *                The threadName to set.
150    * @throws IllegalStateException
151    *                 If threadName has been already set.
152    */
153   public void setThreadName(String threadName) throws IllegalStateException {
154     if (this.threadName != null) {
155       throw new IllegalStateException("threadName has been already set");
156     }
157     this.threadName = threadName;
158   }
159 
160   /**
161    * Returns the throwable information contained within this event. May be
162    * <code>null</code> if there is no such information.
163    */
164   public ThrowableProxy getThrowableProxy() {
165     return throwableProxy;
166   }
167 
168   /**
169    * Set this event's throwable information.
170    */
171   public void setThrowableProxy(ThrowableProxy tp) {
172     if (throwableProxy != null) {
173       throw new IllegalStateException("ThrowableProxy has been already set.");
174     } else {
175       throwableProxy = tp;
176     }
177   }
178 
179   /**
180    * This method should be called prior to serializing an event. It should also
181    * be called when using asynchronous or deferred logging.
182    * 
183    * <p> Note that due to performance concerns, this method does NOT extract
184    * caller data. It is the responsibility of the caller to extract caller
185    * information.
186    */
187   public void prepareForDeferredProcessing() {
188     this.getThreadName();
189     // fixes http://jira.qos.ch/browse/LBCLASSIC-104
190     if (mdcPropertyMap != null) {
191       mdcPropertyMap = new HashMap<String, String>(mdcPropertyMap);
192     }
193   }
194 
195   public LoggerRemoteView getLoggerRemoteView() {
196     return loggerRemoteView;
197   }
198 
199   public void setLoggerRemoteView(LoggerRemoteView loggerRemoteView) {
200     this.loggerRemoteView = loggerRemoteView;
201   }
202 
203   public String getMessage() {
204     return message;
205   }
206 
207   public void setMessage(String message) {
208     if (this.message != null) {
209       throw new IllegalStateException(
210           "The message for this event has been set already.");
211     }
212     this.message = message;
213   }
214 
215   public long getTimeStamp() {
216     return timeStamp;
217   }
218 
219   public void setTimeStamp(long timeStamp) {
220     this.timeStamp = timeStamp;
221   }
222 
223   public void setLevel(Level level) {
224     if (this.level != null) {
225       throw new IllegalStateException(
226           "The level has been already set for this event.");
227     }
228     this.level = level;
229   }
230 
231   /**
232    * The time at which this class was loaded into memory, expressed in
233    * millisecond elapsed since the epoch (1.1.1970).
234    * 
235    * @return The time as measured when this class was loaded into memory.
236    */
237   public static final long getStartTime() {
238     return startTime;
239   }
240 
241   /**
242    * Get the caller information for this logging event. If caller information is
243    * null at the time of its invocation, this method extracts location
244    * information. The collected information is cached for future use.
245    * 
246    * <p> Note that after serialization it is impossible to correctly extract
247    * caller information. </p>
248    */
249   public CallerData[] getCallerData() {
250     // we rely on the fact that fqnOfLoggerClass does not survive
251     // serialization
252     if (callerDataArray == null && fqnOfLoggerClass != null) {
253       callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass);
254     }
255     return callerDataArray;
256   }
257 
258   public void setCallerInformation(CallerData[] callerDataArray) {
259     this.callerDataArray = callerDataArray;
260   }
261 
262   public Marker getMarker() {
263     return marker;
264   }
265 
266   public void setMarker(Marker marker) {
267     if (this.marker != null) {
268       throw new IllegalStateException(
269           "The marker has been already set for this event.");
270     }
271     this.marker = marker;
272   }
273 
274   // computer formatted lazy as suggested in
275   // http://jira.qos.ch/browse/LBCLASSIC-47
276   public String getFormattedMessage() {
277     if (formattedMessage != null) {
278       return formattedMessage;
279     }
280 
281     if (argumentArray != null) {
282       formattedMessage = MessageFormatter.arrayFormat(message, argumentArray);
283     } else {
284       formattedMessage = message;
285     }
286 
287     return formattedMessage;
288   }
289 
290   public Map<String, String> getMDCPropertyMap() {
291     return mdcPropertyMap;
292   }
293 
294   private void writeObject(ObjectOutputStream out) throws IOException {
295     out.defaultWriteObject();
296     out.writeInt(level.levelInt);
297     if (argumentArray != null) {
298       int len = argumentArray.length;
299       out.writeInt(len);
300       for (int i = 0; i < argumentArray.length; i++) {
301         if (argumentArray[i] != null) {
302           out.writeObject(argumentArray[i].toString());
303         } else {
304           out.writeObject(NULL_ARGUMENT_ARRAY_ELEMENT);
305         }
306       }
307     } else {
308       out.writeInt(NULL_ARGUMENT_ARRAY);
309     }
310 
311   }
312 
313   private void readObject(ObjectInputStream in) throws IOException,
314       ClassNotFoundException {
315     in.defaultReadObject();
316     int levelInt = in.readInt();
317     level = Level.toLevel(levelInt);
318 
319     int argArrayLen = in.readInt();
320     if (argArrayLen != NULL_ARGUMENT_ARRAY) {
321       argumentArray = new String[argArrayLen];
322       for (int i = 0; i < argArrayLen; i++) {
323         Object val = in.readObject();
324         if (!NULL_ARGUMENT_ARRAY_ELEMENT.equals(val)) {
325           argumentArray[i] = val;
326         }
327       }
328     }
329   }
330 
331   @Override
332   public String toString() {
333     StringBuilder sb = new StringBuilder();
334     sb.append('[');
335     sb.append(level).append("] ");
336     sb.append(getFormattedMessage());
337     return sb.toString();
338   }
339 }