View Javadoc

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