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     /**
310      * Return the number of nanoseconds past the {@link #getTimeStamp() timestamp in seconds}.
311      * @since 1.3.0
312      */
313     @Override
314     public int getNanoseconds() {
315         return nanoseconds;
316     }
317 
318     /**
319      * Set the number of elapsed milliseconds since epoch in UTC.
320      * 
321      * Setting the timestamp will override the value contained in
322      * {@link #getInstant}. Nanoseconds value will be computed form the provided
323      * millisecond value.
324      * 
325      */
326     public void setTimeStamp(long timeStamp) {
327         Instant instant = Instant.ofEpochMilli(timeStamp);
328         setInstant(instant);
329     }
330 
331     @Override
332     public long getSequenceNumber() {
333         return sequenceNumber;
334     }
335 
336     public void setSquenceNumber(long sn) {
337         sequenceNumber = sn;
338     }
339 
340     public void setLevel(Level level) {
341         if (this.level != null) {
342             throw new IllegalStateException("The level has been already set for this event.");
343         }
344         this.level = level;
345     }
346 
347     /**
348      * Get the caller information for this logging event. If caller information is
349      * null at the time of its invocation, this method extracts location
350      * information. The collected information is cached for future use.
351      * <p/>
352      * <p>
353      * Note that after serialization it is impossible to correctly extract caller
354      * information.
355      * </p>
356      */
357     public StackTraceElement[] getCallerData() {
358         if (callerDataArray == null) {
359             callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass,
360                     loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
361         }
362         return callerDataArray;
363     }
364 
365     public boolean hasCallerData() {
366         return (callerDataArray != null);
367     }
368 
369     public void setCallerData(StackTraceElement[] callerDataArray) {
370         this.callerDataArray = callerDataArray;
371     }
372 
373     public List<Marker> getMarkerList() {
374         return markerList;
375     }
376 
377     public void addMarker(Marker marker) {
378         if (marker == null) {
379             return;
380         }
381         if (markerList == null) {
382             markerList = new ArrayList<>(4);
383         }
384         markerList.add(marker);
385     }
386 
387     public long getContextBirthTime() {
388         return loggerContextVO.getBirthTime();
389     }
390 
391     // lazy computation as suggested in LOGBACK-495
392     public String getFormattedMessage() {
393         if (formattedMessage != null) {
394             return formattedMessage;
395         }
396         if (argumentArray != null) {
397             formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
398         } else {
399             formattedMessage = message;
400         }
401 
402         return formattedMessage;
403     }
404 
405     public Map<String, String> getMDCPropertyMap() {
406         // populate mdcPropertyMap if null
407         if (mdcPropertyMap == null) {
408             MDCAdapter mdc = MDC.getMDCAdapter();
409             if (mdc instanceof LogbackMDCAdapter)
410                 mdcPropertyMap = ((LogbackMDCAdapter) mdc).getPropertyMap();
411             else
412                 mdcPropertyMap = mdc.getCopyOfContextMap();
413         }
414         // mdcPropertyMap still null, use emptyMap()
415         if (mdcPropertyMap == null)
416             mdcPropertyMap = Collections.emptyMap();
417 
418         return mdcPropertyMap;
419     }
420 
421     /**
422      * Set the MDC map for this event.
423      *
424      * @param map
425      * @since 1.0.8
426      */
427     public void setMDCPropertyMap(Map<String, String> map) {
428         if (mdcPropertyMap != null) {
429             throw new IllegalStateException("The MDCPropertyMap has been already set for this event.");
430         }
431         this.mdcPropertyMap = map;
432 
433     }
434 
435     /**
436      * Synonym for [@link #getMDCPropertyMap}.
437      *
438      * @deprecated Replaced by [@link #getMDCPropertyMap}
439      */
440     public Map<String, String> getMdc() {
441         return getMDCPropertyMap();
442     }
443 
444     @Override
445     public String toString() {
446         StringBuilder sb = new StringBuilder();
447         sb.append('[');
448         sb.append(level).append("] ");
449         sb.append(getFormattedMessage());
450         return sb.toString();
451     }
452 
453     /**
454      * LoggerEventVO instances should be used for serialization. Use
455      * {@link LoggingEventVO#build(ILoggingEvent) build} method to create the
456      * LoggerEventVO instance.
457      *
458      * @since 1.0.11
459      */
460     private void writeObject(ObjectOutputStream out) throws IOException {
461         throw new UnsupportedOperationException(this.getClass() + " does not support serialization. "
462                 + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method.");
463     }
464 
465 }