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.ObjectInputStream;
018import java.io.ObjectOutputStream;
019import java.io.Serializable;
020import java.time.Instant;
021import java.util.List;
022import java.util.Map;
023
024import org.slf4j.Marker;
025import org.slf4j.event.KeyValuePair;
026import org.slf4j.helpers.MessageFormatter;
027
028import ch.qos.logback.classic.Level;
029
030// http://www.riehle.org/computer-science/research/1998/ubilab-tr-1998-10-1.html
031
032/**
033 * A read-only and serializable implementation of {@link ILoggingEvent}.
034 * 
035 * @author Ceki Gülcü
036 * @since 0.9.16
037 */
038public class LoggingEventVO implements ILoggingEvent, Serializable {
039
040    private static final long serialVersionUID = 6553722650255690312L;
041
042    private static final int NULL_ARGUMENT_ARRAY = -1;
043    private static final String NULL_ARGUMENT_ARRAY_ELEMENT = "NULL_ARGUMENT_ARRAY_ELEMENT";
044
045    private String threadName;
046    private String loggerName;
047    private LoggerContextVO loggerContextVO;
048
049    private transient Level level;
050    private String message;
051
052    // we gain significant space at serialization time by marking
053    // formattedMessage as transient and constructing it lazily in
054    // getFormattedMessage()
055    private transient String formattedMessage;
056
057    private transient Object[] argumentArray;
058
059    private ThrowableProxyVO throwableProxy;
060    private StackTraceElement[] callerDataArray;
061    private List<Marker> markerList;
062    private List<KeyValuePair> keyValuePairList;
063    private Map<String, String> mdcPropertyMap;
064
065    private long timestamp;
066    private int nanoseconds;
067
068    private long sequenceNumber;
069
070    public static LoggingEventVO build(ILoggingEvent le) {
071        LoggingEventVO ledo = new LoggingEventVO();
072        ledo.loggerName = le.getLoggerName();
073        ledo.loggerContextVO = le.getLoggerContextVO();
074        ledo.threadName = le.getThreadName();
075        ledo.level = (le.getLevel());
076        ledo.message = (le.getMessage());
077        ledo.argumentArray = (le.getArgumentArray());
078        ledo.markerList = le.getMarkerList();
079        ledo.keyValuePairList = le.getKeyValuePairs();
080        ledo.mdcPropertyMap = le.getMDCPropertyMap();
081        ledo.timestamp = le.getTimeStamp();
082        ledo.nanoseconds = le.getNanoseconds();
083        ledo.sequenceNumber = le.getSequenceNumber();
084        ledo.throwableProxy = ThrowableProxyVO.build(le.getThrowableProxy());
085        // add caller data only if it is there already
086        // fixes http://jira.qos.ch/browse/LBCLASSIC-145
087        if (le.hasCallerData()) {
088            ledo.callerDataArray = le.getCallerData();
089        }
090        return ledo;
091    }
092
093    public String getThreadName() {
094        return threadName;
095    }
096
097    public LoggerContextVO getLoggerContextVO() {
098        return loggerContextVO;
099    }
100
101    public String getLoggerName() {
102        return loggerName;
103    }
104
105    public Level getLevel() {
106        return level;
107    }
108
109    public String getMessage() {
110        return message;
111    }
112
113    public String getFormattedMessage() {
114        if (formattedMessage != null) {
115            return formattedMessage;
116        }
117
118        if (argumentArray != null) {
119            formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
120        } else {
121            formattedMessage = message;
122        }
123
124        return formattedMessage;
125    }
126
127    public Object[] getArgumentArray() {
128        return argumentArray;
129    }
130
131    public IThrowableProxy getThrowableProxy() {
132        return throwableProxy;
133    }
134
135    public StackTraceElement[] getCallerData() {
136        return callerDataArray;
137    }
138
139    public boolean hasCallerData() {
140        return callerDataArray != null;
141    }
142
143    public List<Marker> getMarkerList() {
144        return markerList;
145    }
146
147    @Override
148    public long getTimeStamp() { return timestamp; }
149
150    @Override
151    public int getNanoseconds() {  return nanoseconds; }
152
153    public long getSequenceNumber() {
154        return sequenceNumber;
155    }
156
157    public long getContextBirthTime() {
158        return loggerContextVO.getBirthTime();
159    }
160
161    public LoggerContextVO getContextLoggerRemoteView() {
162        return loggerContextVO;
163    }
164
165    public Map<String, String> getMDCPropertyMap() {
166        return mdcPropertyMap;
167    }
168
169    public Map<String, String> getMdc() {
170        return mdcPropertyMap;
171    }
172
173    @Override
174    public List<KeyValuePair> getKeyValuePairs() {
175        return this.keyValuePairList;
176    }
177
178    public void prepareForDeferredProcessing() {
179    }
180
181    private void writeObject(ObjectOutputStream out) throws IOException {
182        out.defaultWriteObject();
183        out.writeInt(level.levelInt);
184        if (argumentArray != null) {
185            int len = argumentArray.length;
186            out.writeInt(len);
187            for (int i = 0; i < argumentArray.length; i++) {
188                if (argumentArray[i] != null) {
189                    out.writeObject(argumentArray[i].toString());
190                } else {
191                    out.writeObject(NULL_ARGUMENT_ARRAY_ELEMENT);
192                }
193            }
194        } else {
195            out.writeInt(NULL_ARGUMENT_ARRAY);
196        }
197
198    }
199
200    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
201        in.defaultReadObject();
202        int levelInt = in.readInt();
203        level = Level.toLevel(levelInt);
204
205        int argArrayLen = in.readInt();
206        if (argArrayLen != NULL_ARGUMENT_ARRAY) {
207            argumentArray = new String[argArrayLen];
208            for (int i = 0; i < argArrayLen; i++) {
209                Object val = in.readObject();
210                if (!NULL_ARGUMENT_ARRAY_ELEMENT.equals(val)) {
211                    argumentArray[i] = val;
212                }
213            }
214        }
215    }
216
217    @Override
218    public int hashCode() {
219        final int prime = 31;
220        long millis = getTimeStamp();
221
222        int result = 1;
223        result = prime * result + ((message == null) ? 0 : message.hashCode());
224        result = prime * result + ((threadName == null) ? 0 : threadName.hashCode());
225        result = prime * result + (int) (millis ^ (millis >>> 32));
226        return result;
227    }
228
229    @Override
230    public boolean equals(Object obj) {
231        if (this == obj)
232            return true;
233        if (obj == null)
234            return false;
235        if (getClass() != obj.getClass())
236            return false;
237        final LoggingEventVO other = (LoggingEventVO) obj;
238        if (message == null) {
239            if (other.message != null)
240                return false;
241        } else if (!message.equals(other.message))
242            return false;
243
244        if (loggerName == null) {
245            if (other.loggerName != null)
246                return false;
247        } else if (!loggerName.equals(other.loggerName))
248            return false;
249
250        if (threadName == null) {
251            if (other.threadName != null)
252                return false;
253        } else if (!threadName.equals(other.threadName))
254            return false;
255        if (getTimeStamp() != other.getTimeStamp())
256            return false;
257
258        if (markerList == null) {
259            if (other.markerList != null)
260                return false;
261        } else if (!markerList.equals(other.markerList))
262            return false;
263
264        if (mdcPropertyMap == null) {
265            if (other.mdcPropertyMap != null)
266                return false;
267        } else if (!mdcPropertyMap.equals(other.mdcPropertyMap))
268            return false;
269        return true;
270    }
271
272}