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.InvalidObjectException;
18  import java.io.ObjectInputStream;
19  import java.io.ObjectOutputStream;
20  import java.io.Serializable;
21  import java.time.Instant;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.slf4j.Marker;
26  import org.slf4j.event.KeyValuePair;
27  import org.slf4j.helpers.MessageFormatter;
28  
29  import ch.qos.logback.classic.Level;
30  
31  // http://www.riehle.org/computer-science/research/1998/ubilab-tr-1998-10-1.html
32  // See also the paper https://www.riehle.org/computer-science/research/1998/ubilab-tr-1998-10-1.pdf
33  
34  /**
35   * A read-only and serializable implementation of {@link ILoggingEvent}.
36   * 
37   * @author Ceki Gülcü
38   * @since 0.9.16
39   */
40  public class LoggingEventVO implements ILoggingEvent, Serializable {
41  
42      private static final long serialVersionUID = 6553722650255690312L;
43  
44      private static final int NULL_ARGUMENT_ARRAY = -1;
45      private static final String NULL_ARGUMENT_ARRAY_ELEMENT = "NULL_ARGUMENT_ARRAY_ELEMENT";
46      private static final int ARGUMENT_ARRAY_DESERIALIZATION_LIMIT = 128;
47  
48      private String threadName;
49      private String loggerName;
50      private LoggerContextVO loggerContextVO;
51  
52      private transient Level level;
53      private String message;
54  
55      // we gain significant space at serialization time by marking
56      // formattedMessage as transient and constructing it lazily in
57      // getFormattedMessage()
58      private transient String formattedMessage;
59  
60      private transient Object[] argumentArray;
61  
62      private ThrowableProxyVO throwableProxy;
63      private StackTraceElement[] callerDataArray;
64      private List<Marker> markerList;
65      private List<KeyValuePair> keyValuePairList;
66      private Map<String, String> mdcPropertyMap;
67  
68      private long timestamp;
69      private int nanoseconds;
70  
71      private long sequenceNumber;
72  
73      public static LoggingEventVO build(ILoggingEvent le) {
74          LoggingEventVO ledo = new LoggingEventVO();
75          ledo.loggerName = le.getLoggerName();
76          ledo.loggerContextVO = le.getLoggerContextVO();
77          ledo.threadName = le.getThreadName();
78          ledo.level = (le.getLevel());
79          ledo.message = (le.getMessage());
80          ledo.argumentArray = (le.getArgumentArray());
81          ledo.markerList = le.getMarkerList();
82          ledo.keyValuePairList = le.getKeyValuePairs();
83          ledo.mdcPropertyMap = le.getMDCPropertyMap();
84          ledo.timestamp = le.getTimeStamp();
85          ledo.nanoseconds = le.getNanoseconds();
86          ledo.sequenceNumber = le.getSequenceNumber();
87          ledo.throwableProxy = ThrowableProxyVO.build(le.getThrowableProxy());
88          // add caller data only if it is there already
89          // fixes http://jira.qos.ch/browse/LBCLASSIC-145
90          if (le.hasCallerData()) {
91              ledo.callerDataArray = le.getCallerData();
92          }
93          return ledo;
94      }
95  
96      public String getThreadName() {
97          return threadName;
98      }
99  
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 }