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}