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