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     public LoggingEvent() {
102     }
103
104     public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable, Object[] argArray) {
105         this.fqnOfLoggerClass = fqcn;
106         this.loggerName = logger.getName();
107         this.loggerContext = logger.getLoggerContext();
108         this.loggerContextVO = loggerContext.getLoggerContextRemoteView();
109         this.level = level;
110
111         this.message = message;
112         this.argumentArray = argArray;
113
114         if (throwable == null) {
115             throwable = extractThrowableAnRearrangeArguments(argArray);
116         }
117
118         if (throwable != null) {
119             this.throwableProxy = new ThrowableProxy(throwable);
120             LoggerContext lc = logger.getLoggerContext();
121             if (lc.isPackagingDataEnabled()) {
122                 this.throwableProxy.calculatePackagingData();
123             }
124         }
125
126         timeStamp = System.currentTimeMillis();
127     }
128
129     private Throwable extractThrowableAnRearrangeArguments(Object[] argArray) {
130         Throwable extractedThrowable = EventArgUtil.extractThrowable(argArray);
131         if (EventArgUtil.successfulExtraction(extractedThrowable)) {
132             this.argumentArray = EventArgUtil.trimmedCopy(argArray);
133         }
134         return extractedThrowable;
135     }
136
137     public void setArgumentArray(Object[] argArray) {
138         if (this.argumentArray != null) {
139             throw new IllegalStateException("argArray has been already set");
140         }
141         this.argumentArray = argArray;
142     }
143
144     public Object[] getArgumentArray() {
145         return this.argumentArray;
146     }
147
148     public Level getLevel() {
149         return level;
150     }
151
152     public String getLoggerName() {
153         return loggerName;
154     }
155
156     public void setLoggerName(String loggerName) {
157         this.loggerName = loggerName;
158     }
159
160     public String getThreadName() {
161         if (threadName == null) {
162             threadName = (Thread.currentThread()).getName();
163         }
164         return threadName;
165     }
166
167     /**
168      * @param threadName The threadName to set.
169      * @throws IllegalStateException 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      * <p/>
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("The message for this event has been set already.");
227         }
228         this.message = message;
229     }
230
231     public long getTimeStamp() {
232         return timeStamp;
233     }
234
235     public void setTimeStamp(long timeStamp) {
236         this.timeStamp = timeStamp;
237     }
238
239     public void setLevel(Level level) {
240         if (this.level != null) {
241             throw new IllegalStateException("The level has been already set for this event.");
242         }
243         this.level = level;
244     }
245
246     /**
247      * Get the caller information for this logging event. If caller information is
248      * null at the time of its invocation, this method extracts location
249      * information. The collected information is cached for future use.
250      * <p/>
251      * <p>
252      * Note that after serialization it is impossible to correctly extract caller
253      * information.
254      * </p>
255      */

256     public StackTraceElement[] getCallerData() {
257         if (callerDataArray == null) {
258             callerDataArray = CallerData
259                             .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
260         }
261         return callerDataArray;
262     }
263
264     public boolean hasCallerData() {
265         return (callerDataArray != null);
266     }
267
268     public void setCallerData(StackTraceElement[] callerDataArray) {
269         this.callerDataArray = callerDataArray;
270     }
271
272     public Marker getMarker() {
273         return marker;
274     }
275
276     public void setMarker(Marker marker) {
277         if (this.marker != null) {
278             throw new IllegalStateException("The marker has been already set for this event.");
279         }
280         this.marker = marker;
281     }
282
283     public long getContextBirthTime() {
284         return loggerContextVO.getBirthTime();
285     }
286
287     // lazy computation as suggested in LOGBACK-495
288     public String getFormattedMessage() {
289         if (formattedMessage != null) {
290             return formattedMessage;
291         }
292         if (argumentArray != null) {
293             formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
294         } else {
295             formattedMessage = message;
296         }
297
298         return formattedMessage;
299     }
300
301     public Map<String, String> getMDCPropertyMap() {
302         // populate mdcPropertyMap if null
303         if (mdcPropertyMap == null) {
304             MDCAdapter mdc = MDC.getMDCAdapter();
305             if (mdc instanceof LogbackMDCAdapter)
306                 mdcPropertyMap = ((LogbackMDCAdapter) mdc).getPropertyMap();
307             else
308                 mdcPropertyMap = mdc.getCopyOfContextMap();
309         }
310         // mdcPropertyMap still null, use emptyMap()
311         if (mdcPropertyMap == null)
312             mdcPropertyMap = Collections.emptyMap();
313
314         return mdcPropertyMap;
315     }
316
317     /**
318      * Set the MDC map for this event.
319      *
320      * @param map
321      * @since 1.0.8
322      */

323     public void setMDCPropertyMap(Map<String, String> map) {
324         if (mdcPropertyMap != null) {
325             throw new IllegalStateException("The MDCPropertyMap has been already set for this event.");
326         }
327         this.mdcPropertyMap = map;
328
329     }
330
331     /**
332      * Synonym for [@link #getMDCPropertyMap}.
333      *
334      * @deprecated Replaced by [@link #getMDCPropertyMap}
335      */

336     public Map<String, String> getMdc() {
337         return getMDCPropertyMap();
338     }
339
340     @Override
341     public String toString() {
342         StringBuilder sb = new StringBuilder();
343         sb.append('[');
344         sb.append(level).append("] ");
345         sb.append(getFormattedMessage());
346         return sb.toString();
347     }
348
349     /**
350      * LoggerEventVO instances should be used for serialization. Use
351      * {@link LoggingEventVO#build(ILoggingEvent) build} method to create the LoggerEventVO instance.
352      *
353      * @since 1.0.11
354      */

355     private void writeObject(ObjectOutputStream out) throws IOException {
356         throw new UnsupportedOperationException(this.getClass() + " does not support serialization. "
357                         + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method.");
358     }
359
360 }
361