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ülcü 051 * @author Sé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}