View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, 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.io.IOException;
17  import java.io.ObjectOutputStream;
18  import java.util.Collections;
19  import java.util.Map;
20  
21  import org.slf4j.MDC;
22  import org.slf4j.Marker;
23  import org.slf4j.helpers.MessageFormatter;
24  
25  import ch.qos.logback.classic.Level;
26  import ch.qos.logback.classic.Logger;
27  import ch.qos.logback.classic.LoggerContext;
28  import ch.qos.logback.classic.util.LogbackMDCAdapter;
29  
30  import org.slf4j.spi.MDCAdapter;
31  
32  /**
33   * The internal representation of logging events. When an affirmative decision
34   * is made to log then a <code>LoggingEvent</code> instance is created. This
35   * instance is passed around to the different logback-classic components.
36   * <p/>
37   * <p>
38   * Writers of logback-classic components such as appenders should be aware of
39   * that some of the LoggingEvent fields are initialized lazily. Therefore, an
40   * appender wishing to output data to be later correctly read by a receiver,
41   * must initialize "lazy" fields prior to writing them out. See the
42   * {@link #prepareForDeferredProcessing()} method for the exact list.
43   * </p>
44   *
45   * @author Ceki G&uuml;lc&uuml;
46   * @author S&eacute;bastien Pennec
47   */
48  public class LoggingEvent implements ILoggingEvent {
49  
50      /**
51       * Fully qualified name of the calling Logger class. This field does not
52       * survive serialization.
53       * <p/>
54       * <p/>
55       * Note that the getCallerInformation() method relies on this fact.
56       */
57      transient String fqnOfLoggerClass;
58  
59      /**
60       * The name of thread in which this logging event was generated.
61       */
62      private String threadName;
63  
64      private String loggerName;
65      private LoggerContext loggerContext;
66      private LoggerContextVO loggerContextVO;
67  
68      /**
69       * Level of logging event.
70       * <p/>
71       * <p>
72       * This field should not be accessed directly. You should use the
73       * {@link #getLevel} method instead.
74       * </p>
75       */
76      private transient Level level;
77  
78      private String message;
79  
80      // we gain significant space at serialization time by marking
81      // formattedMessage as transient and constructing it lazily in
82      // getFormattedMessage()
83      transient String formattedMessage;
84  
85      private transient Object[] argumentArray;
86  
87      private ThrowableProxy throwableProxy;
88  
89      private StackTraceElement[] callerDataArray;
90  
91      private Marker marker;
92  
93      private Map<String, String> mdcPropertyMap;
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     private long sequenceNumber;
102 
103     public LoggingEvent() {
104     }
105 
106     public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable, Object[] argArray) {           
107         this.fqnOfLoggerClass = fqcn;
108         this.loggerName = logger.getName();
109         this.loggerContext = logger.getLoggerContext();
110         this.loggerContextVO = loggerContext.getLoggerContextRemoteView();
111         this.level = level;
112 
113         this.message = message;
114         this.argumentArray = argArray;
115 
116         timeStamp = System.currentTimeMillis();
117        
118         if(loggerContext != null) {
119             SequenceNumberGenerator sequenceNumberGenerator = loggerContext.getSequenceNumberGenerator();
120             if(sequenceNumberGenerator != null)
121                 sequenceNumber = sequenceNumberGenerator.nextSequenceNumber();
122         }
123        
124         
125         if (throwable == null) {
126             throwable = extractThrowableAnRearrangeArguments(argArray);
127         }
128 
129         if (throwable != null) {
130             this.throwableProxy = new ThrowableProxy(throwable);
131            
132             if (loggerContext != null && loggerContext.isPackagingDataEnabled()) {
133                 this.throwableProxy.calculatePackagingData();
134             }
135         }
136 
137         
138     }
139 
140     private Throwable extractThrowableAnRearrangeArguments(Object[] argArray) {
141         Throwable extractedThrowable = EventArgUtil.extractThrowable(argArray);
142         if (EventArgUtil.successfulExtraction(extractedThrowable)) {
143             this.argumentArray = EventArgUtil.trimmedCopy(argArray);
144         }
145         return extractedThrowable;
146     }
147 
148     public void setArgumentArray(Object[] argArray) {
149         if (this.argumentArray != null) {
150             throw new IllegalStateException("argArray has been already set");
151         }
152         this.argumentArray = argArray;
153     }
154 
155     public Object[] getArgumentArray() {
156         return this.argumentArray;
157     }
158 
159     public Level getLevel() {
160         return level;
161     }
162 
163     public String getLoggerName() {
164         return loggerName;
165     }
166 
167     public void setLoggerName(String loggerName) {
168         this.loggerName = loggerName;
169     }
170 
171     public String getThreadName() {
172         if (threadName == null) {
173             threadName = (Thread.currentThread()).getName();
174         }
175         return threadName;
176     }
177 
178     /**
179      * @param threadName The threadName to set.
180      * @throws IllegalStateException If threadName has been already set.
181      */
182     public void setThreadName(String threadName) throws IllegalStateException {
183         if (this.threadName != null) {
184             throw new IllegalStateException("threadName has been already set");
185         }
186         this.threadName = threadName;
187     }
188 
189     /**
190      * Returns the throwable information contained within this event. May be
191      * <code>null</code> if there is no such information.
192      */
193     public IThrowableProxy getThrowableProxy() {
194         return throwableProxy;
195     }
196 
197     /**
198      * Set this event's throwable information.
199      */
200     public void setThrowableProxy(ThrowableProxy tp) {
201         if (throwableProxy != null) {
202             throw new IllegalStateException("ThrowableProxy has been already set.");
203         } else {
204             throwableProxy = tp;
205         }
206     }
207 
208     /**
209      * This method should be called prior to serializing an event. It should also
210      * be called when using asynchronous or deferred logging.
211      * <p/>
212      * <p/>
213      * Note that due to performance concerns, this method does NOT extract caller
214      * data. It is the responsibility of the caller to extract caller information.
215      */
216     public void prepareForDeferredProcessing() {
217         this.getFormattedMessage();
218         this.getThreadName();
219         // fixes http://jira.qos.ch/browse/LBCLASSIC-104
220         this.getMDCPropertyMap();
221     }
222 
223     public LoggerContextVO getLoggerContextVO() {
224         return loggerContextVO;
225     }
226 
227     public void setLoggerContextRemoteView(LoggerContextVO loggerContextVO) {
228         this.loggerContextVO = loggerContextVO;
229     }
230 
231     public String getMessage() {
232         return message;
233     }
234 
235     public void setMessage(String message) {
236         if (this.message != null) {
237             throw new IllegalStateException("The message for this event has been set already.");
238         }
239         this.message = message;
240     }
241 
242     public long getTimeStamp() {
243         return timeStamp;
244     }
245 
246     public void setTimeStamp(long timeStamp) {
247         this.timeStamp = timeStamp;
248     }
249 
250     @Override
251     public long getSequenceNumber() {
252         return sequenceNumber;
253     }
254     
255     public void setSquenceNumber(long sn) {
256         sequenceNumber = sn;
257     }
258     
259     public void setLevel(Level level) {
260         if (this.level != null) {
261             throw new IllegalStateException("The level has been already set for this event.");
262         }
263         this.level = level;
264     }
265 
266     /**
267      * Get the caller information for this logging event. If caller information is
268      * null at the time of its invocation, this method extracts location
269      * information. The collected information is cached for future use.
270      * <p/>
271      * <p>
272      * Note that after serialization it is impossible to correctly extract caller
273      * information.
274      * </p>
275      */
276     public StackTraceElement[] getCallerData() {
277         if (callerDataArray == null) {
278             callerDataArray = CallerData
279                             .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
280         }
281         return callerDataArray;
282     }
283 
284     public boolean hasCallerData() {
285         return (callerDataArray != null);
286     }
287 
288     public void setCallerData(StackTraceElement[] callerDataArray) {
289         this.callerDataArray = callerDataArray;
290     }
291 
292     public Marker getMarker() {
293         return marker;
294     }
295 
296     public void setMarker(Marker marker) {
297         if (this.marker != null) {
298             throw new IllegalStateException("The marker has been already set for this event.");
299         }
300         this.marker = marker;
301     }
302 
303     public long getContextBirthTime() {
304         return loggerContextVO.getBirthTime();
305     }
306 
307     // lazy computation as suggested in LOGBACK-495
308     public String getFormattedMessage() {
309         if (formattedMessage != null) {
310             return formattedMessage;
311         }
312         if (argumentArray != null) {
313             formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
314         } else {
315             formattedMessage = message;
316         }
317 
318         return formattedMessage;
319     }
320 
321     public Map<String, String> getMDCPropertyMap() {
322         // populate mdcPropertyMap if null
323         if (mdcPropertyMap == null) {
324             MDCAdapter mdc = MDC.getMDCAdapter();
325             if (mdc instanceof LogbackMDCAdapter)
326                 mdcPropertyMap = ((LogbackMDCAdapter) mdc).getPropertyMap();
327             else
328                 mdcPropertyMap = mdc.getCopyOfContextMap();
329         }
330         // mdcPropertyMap still null, use emptyMap()
331         if (mdcPropertyMap == null)
332             mdcPropertyMap = Collections.emptyMap();
333 
334         return mdcPropertyMap;
335     }
336 
337     /**
338      * Set the MDC map for this event.
339      *
340      * @param map
341      * @since 1.0.8
342      */
343     public void setMDCPropertyMap(Map<String, String> map) {
344         if (mdcPropertyMap != null) {
345             throw new IllegalStateException("The MDCPropertyMap has been already set for this event.");
346         }
347         this.mdcPropertyMap = map;
348 
349     }
350 
351     /**
352      * Synonym for [@link #getMDCPropertyMap}.
353      *
354      * @deprecated Replaced by [@link #getMDCPropertyMap}
355      */
356     public Map<String, String> getMdc() {
357         return getMDCPropertyMap();
358     }
359 
360     @Override
361     public String toString() {
362         StringBuilder sb = new StringBuilder();
363         sb.append('[');
364         sb.append(level).append("] ");
365         sb.append(getFormattedMessage());
366         return sb.toString();
367     }
368 
369     /**
370      * LoggerEventVO instances should be used for serialization. Use
371      * {@link LoggingEventVO#build(ILoggingEvent) build} method to create the LoggerEventVO instance.
372      *
373      * @since 1.0.11
374      */
375     private void writeObject(ObjectOutputStream out) throws IOException {
376         throw new UnsupportedOperationException(this.getClass() + " does not support serialization. "
377                         + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method.");
378     }
379 
380 
381 }