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