001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.spi;
015
016import java.io.IOException;
017import java.io.ObjectOutputStream;
018import java.time.Clock;
019import java.time.Instant;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.Map;
024
025import org.slf4j.MDC;
026import org.slf4j.Marker;
027import org.slf4j.event.KeyValuePair;
028import org.slf4j.helpers.MessageFormatter;
029import org.slf4j.spi.MDCAdapter;
030
031import ch.qos.logback.classic.Level;
032import ch.qos.logback.classic.Logger;
033import ch.qos.logback.classic.LoggerContext;
034import ch.qos.logback.classic.util.LogbackMDCAdapter;
035import ch.qos.logback.core.spi.SequenceNumberGenerator;
036
037/**
038 * The internal representation of logging events. When an affirmative decision
039 * is made to log then a <code>LoggingEvent</code> instance is created. This
040 * instance is passed around to the different logback-classic components.
041 * <p/>
042 * <p>
043 * Writers of logback-classic components such as appenders should be aware of
044 * that some of the LoggingEvent fields are initialized lazily. Therefore, an
045 * appender wishing to output data to be later correctly read by a receiver,
046 * must initialize "lazy" fields prior to writing them out. See the
047 * {@link #prepareForDeferredProcessing()} method for the exact list.
048 * </p>
049 *
050 * @author Ceki G&uuml;lc&uuml;
051 * @author S&eacute;bastien Pennec
052 */
053public class LoggingEvent implements ILoggingEvent {
054
055    /**
056     * Fully qualified name of the calling Logger class. This field does not survive
057     * serialization.
058     * <p/>
059     * <p/>
060     * Note that the getCallerInformation() method relies on this fact.
061     */
062    transient String fqnOfLoggerClass;
063
064    /**
065     * The name of thread in which this logging event was generated.
066     */
067    private String threadName;
068
069    private String loggerName;
070    private LoggerContext loggerContext;
071    private LoggerContextVO loggerContextVO;
072
073    /**
074     * Level of logging event.
075     * <p/>
076     * <p>
077     * This field should not be accessed directly. You should use the
078     * {@link #getLevel} method instead.
079     * </p>
080     */
081    private transient Level level;
082
083    private String message;
084
085    // we gain significant space at serialization time by marking
086    // formattedMessage as transient and constructing it lazily in
087    // getFormattedMessage()
088    transient String formattedMessage;
089
090    private transient Object[] argumentArray;
091
092    private ThrowableProxy throwableProxy;
093
094    private StackTraceElement[] callerDataArray;
095
096    private List<Marker> markerList;
097
098    private Map<String, String> mdcPropertyMap;
099
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}