001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.impl; 018 019 import java.io.InvalidObjectException; 020 import java.io.ObjectInputStream; 021 import java.io.Serializable; 022 import java.util.Collections; 023 import java.util.HashMap; 024 import java.util.List; 025 import java.util.Map; 026 027 import org.apache.logging.log4j.Level; 028 import org.apache.logging.log4j.Marker; 029 import org.apache.logging.log4j.ThreadContext; 030 import org.apache.logging.log4j.core.LogEvent; 031 import org.apache.logging.log4j.core.config.Property; 032 import org.apache.logging.log4j.core.util.Clock; 033 import org.apache.logging.log4j.core.util.ClockFactory; 034 import org.apache.logging.log4j.message.LoggerNameAwareMessage; 035 import org.apache.logging.log4j.message.Message; 036 import org.apache.logging.log4j.message.TimestampMessage; 037 import org.apache.logging.log4j.util.Strings; 038 039 /** 040 * Implementation of a LogEvent. 041 */ 042 public class Log4jLogEvent implements LogEvent { 043 044 private static final long serialVersionUID = -1351367343806656055L; 045 private static final Clock clock = ClockFactory.getClock(); 046 private final String loggerFqcn; 047 private final Marker marker; 048 private final Level level; 049 private final String loggerName; 050 private final Message message; 051 private final long timeMillis; 052 private transient final Throwable thrown; 053 private ThrowableProxy thrownProxy; 054 private final Map<String, String> contextMap; 055 private final ThreadContext.ContextStack contextStack; 056 private String threadName = null; 057 private StackTraceElement source; 058 private boolean includeLocation; 059 private boolean endOfBatch = false; 060 061 public Log4jLogEvent() { 062 this(clock.currentTimeMillis()); 063 } 064 065 /** 066 * 067 */ 068 public Log4jLogEvent(final long timestamp) { 069 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, timestamp); 070 } 071 072 /** 073 * Constructor. 074 * @param loggerName The name of the Logger. 075 * @param marker The Marker or null. 076 * @param loggerFQCN The fully qualified class name of the caller. 077 * @param level The logging Level. 078 * @param message The Message. 079 * @param t A Throwable or null. 080 */ 081 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 082 final Message message, final Throwable t) { 083 this(loggerName, marker, loggerFQCN, level, message, null, t); 084 } 085 086 /** 087 * Constructor. 088 * @param loggerName The name of the Logger. 089 * @param marker The Marker or null. 090 * @param loggerFQCN The fully qualified class name of the caller. 091 * @param level The logging Level. 092 * @param message The Message. 093 * @param properties properties to add to the event. 094 * @param t A Throwable or null. 095 */ 096 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 097 final Message message, final List<Property> properties, final Throwable t) { 098 this(loggerName, marker, loggerFQCN, level, message, t, 099 createMap(properties), 100 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null, 101 null, clock.currentTimeMillis()); // LOG4J2-628 use log4j.Clock for timestamps 102 } 103 104 /** 105 * Constructor. 106 * @param loggerName The name of the Logger. 107 * @param marker The Marker or null. 108 * @param loggerFQCN The fully qualified class name of the caller. 109 * @param level The logging Level. 110 * @param message The Message. 111 * @param t A Throwable or null. 112 * @param mdc The mapped diagnostic context. 113 * @param ndc the nested diagnostic context. 114 * @param threadName The name of the thread. 115 * @param location The locations of the caller. 116 * @param timestamp The timestamp of the event. 117 */ 118 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 119 final Message message, final Throwable t, final Map<String, String> mdc, 120 final ThreadContext.ContextStack ndc, final String threadName, 121 final StackTraceElement location, final long timestamp) { 122 this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName, 123 location, timestamp); 124 } 125 126 /** 127 * Create a new LogEvent. 128 * @param loggerName The name of the Logger. 129 * @param marker The Marker or null. 130 * @param loggerFQCN The fully qualified class name of the caller. 131 * @param level The logging Level. 132 * @param message The Message. 133 * @param thrown A Throwable or null. 134 * @param thrownProxy A ThrowableProxy or null. 135 * @param mdc The mapped diagnostic context. 136 * @param ndc the nested diagnostic context. 137 * @param threadName The name of the thread. 138 * @param location The locations of the caller. 139 * @param timestamp The timestamp of the event. 140 */ 141 public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN, 142 final Level level, final Message message, final Throwable thrown, 143 final ThrowableProxy thrownProxy, 144 final Map<String, String> mdc, final ThreadContext.ContextStack ndc, 145 final String threadName, final StackTraceElement location, 146 final long timestamp) { 147 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 148 thrownProxy, mdc, ndc, threadName, location, timestamp); 149 return result; 150 } 151 152 /** 153 * Constructor. 154 * @param loggerName The name of the Logger. 155 * @param marker The Marker or null. 156 * @param loggerFQCN The fully qualified class name of the caller. 157 * @param level The logging Level. 158 * @param message The Message. 159 * @param thrown A Throwable or null. 160 * @param thrownProxy A ThrowableProxy or null. 161 * @param contextMap The mapped diagnostic context. 162 * @param contextStack the nested diagnostic context. 163 * @param threadName The name of the thread. 164 * @param source The locations of the caller. 165 * @param timestamp The timestamp of the event. 166 */ 167 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 168 final Message message, final Throwable thrown, ThrowableProxy thrownProxy, 169 final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack, 170 final String threadName, final StackTraceElement source, final long timestamp) { 171 this.loggerName = loggerName; 172 this.marker = marker; 173 this.loggerFqcn = loggerFQCN; 174 this.level = (level == null) ? Level.OFF : level; // LOG4J2-462, LOG4J2-465 175 this.message = message; 176 this.thrown = thrown; 177 this.thrownProxy = thrownProxy; 178 this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap; 179 this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; 180 this.timeMillis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp; 181 this.threadName = threadName; 182 this.source = source; 183 if (message != null && message instanceof LoggerNameAwareMessage) { 184 ((LoggerNameAwareMessage) message).setLoggerName(loggerName); 185 } 186 } 187 188 private static Map<String, String> createMap(final List<Property> properties) { 189 final Map<String, String> contextMap = ThreadContext.getImmutableContext(); 190 if (contextMap == null && (properties == null || properties.isEmpty())) { 191 return null; 192 } 193 if (properties == null || properties.isEmpty()) { 194 return contextMap; // contextMap is not null 195 } 196 final Map<String, String> map = new HashMap<String, String>(contextMap); 197 198 for (final Property prop : properties) { 199 if (!map.containsKey(prop.getName())) { 200 map.put(prop.getName(), prop.getValue()); 201 } 202 } 203 return Collections.unmodifiableMap(map); 204 } 205 206 /** 207 * Returns the logging Level. 208 * @return the Level associated with this event. 209 */ 210 @Override 211 public Level getLevel() { 212 return level; 213 } 214 215 /** 216 * Returns the name of the Logger used to generate the event. 217 * @return The Logger name. 218 */ 219 @Override 220 public String getLoggerName() { 221 return loggerName; 222 } 223 224 /** 225 * Returns the Message associated with the event. 226 * @return The Message. 227 */ 228 @Override 229 public Message getMessage() { 230 return message; 231 } 232 233 /** 234 * Returns the name of the Thread on which the event was generated. 235 * @return The name of the Thread. 236 */ 237 @Override 238 public String getThreadName() { 239 if (threadName == null) { 240 threadName = Thread.currentThread().getName(); 241 } 242 return threadName; 243 } 244 245 /** 246 * Returns the time in milliseconds from the epoch when the event occurred. 247 * @return The time the event occurred. 248 */ 249 @Override 250 public long getTimeMillis() { 251 return timeMillis; 252 } 253 254 /** 255 * Returns the Throwable associated with the event, or null. 256 * @return The Throwable associated with the event. 257 */ 258 @Override 259 public Throwable getThrown() { 260 return thrown; 261 } 262 263 /** 264 * Returns the ThrowableProxy associated with the event, or null. 265 * @return The ThrowableProxy associated with the event. 266 */ 267 @Override 268 public ThrowableProxy getThrownProxy() { 269 if (thrownProxy == null && thrown != null) { 270 thrownProxy = new ThrowableProxy(thrown); 271 } 272 return thrownProxy; 273 } 274 275 276 /** 277 * Returns the Marker associated with the event, or null. 278 * @return the Marker associated with the event. 279 */ 280 @Override 281 public Marker getMarker() { 282 return marker; 283 } 284 285 /** 286 * The fully qualified class name of the class that was called by the caller. 287 * @return the fully qualified class name of the class that is performing logging. 288 */ 289 @Override 290 public String getLoggerFqcn() { 291 return loggerFqcn; 292 } 293 294 /** 295 * Returns the immutable copy of the ThreadContext Map. 296 * @return The context Map. 297 */ 298 @Override 299 public Map<String, String> getContextMap() { 300 return contextMap; 301 } 302 303 /** 304 * Returns an immutable copy of the ThreadContext stack. 305 * @return The context Stack. 306 */ 307 @Override 308 public ThreadContext.ContextStack getContextStack() { 309 return contextStack; 310 } 311 312 /** 313 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 314 * before the first occurrence of FQCN as a class name. 315 * @return the StackTraceElement for the caller. 316 */ 317 @Override 318 public StackTraceElement getSource() { 319 if (source != null) { 320 return source; 321 } 322 if (loggerFqcn == null || !includeLocation) { 323 return null; 324 } 325 source = calcLocation(loggerFqcn); 326 return source; 327 } 328 329 public static StackTraceElement calcLocation(final String fqcnOfLogger) { 330 if (fqcnOfLogger == null) { 331 return null; 332 } 333 final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 334 StackTraceElement last = null; 335 for (int i = stackTrace.length - 1; i > 0; i--) { 336 final String className = stackTrace[i].getClassName(); 337 if (fqcnOfLogger.equals(className)) { 338 return last; 339 } 340 last = stackTrace[i]; 341 } 342 return null; 343 } 344 345 @Override 346 public boolean isIncludeLocation() { 347 return includeLocation; 348 } 349 350 @Override 351 public void setIncludeLocation(final boolean includeLocation) { 352 this.includeLocation = includeLocation; 353 } 354 355 @Override 356 public boolean isEndOfBatch() { 357 return endOfBatch; 358 } 359 360 @Override 361 public void setEndOfBatch(final boolean endOfBatch) { 362 this.endOfBatch = endOfBatch; 363 } 364 365 /** 366 * Creates a LogEventProxy that can be serialized. 367 * @return a LogEventProxy. 368 */ 369 protected Object writeReplace() { 370 getThrownProxy(); // ensure ThrowableProxy is initialized 371 return new LogEventProxy(this, this.includeLocation); 372 } 373 374 public static Serializable serialize(final Log4jLogEvent event, 375 final boolean includeLocation) { 376 event.getThrownProxy(); // ensure ThrowableProxy is initialized 377 return new LogEventProxy(event, includeLocation); 378 } 379 380 public static boolean canDeserialize(final Serializable event) { 381 return event instanceof LogEventProxy; 382 } 383 384 public static Log4jLogEvent deserialize(final Serializable event) { 385 if (event == null) { 386 throw new NullPointerException("Event cannot be null"); 387 } 388 if (event instanceof LogEventProxy) { 389 final LogEventProxy proxy = (LogEventProxy) event; 390 final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker, 391 proxy.loggerFQCN, proxy.level, proxy.message, 392 proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName, 393 proxy.source, proxy.timeMillis); 394 result.setEndOfBatch(proxy.isEndOfBatch); 395 result.setIncludeLocation(proxy.isLocationRequired); 396 return result; 397 } 398 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); 399 } 400 401 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 402 throw new InvalidObjectException("Proxy required"); 403 } 404 405 @Override 406 public String toString() { 407 final StringBuilder sb = new StringBuilder(); 408 final String n = loggerName.isEmpty() ? "root" : loggerName; 409 sb.append("Logger=").append(n); 410 sb.append(" Level=").append(level.name()); 411 sb.append(" Message=").append(message.getFormattedMessage()); 412 return sb.toString(); 413 } 414 415 @Override 416 public boolean equals(Object o) { 417 if (this == o) { 418 return true; 419 } 420 if (o == null || getClass() != o.getClass()) { 421 return false; 422 } 423 424 Log4jLogEvent that = (Log4jLogEvent) o; 425 426 if (endOfBatch != that.endOfBatch) { 427 return false; 428 } 429 if (includeLocation != that.includeLocation) { 430 return false; 431 } 432 if (timeMillis != that.timeMillis) { 433 return false; 434 } 435 if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) { 436 return false; 437 } 438 if (level != null ? !level.equals(that.level) : that.level != null) { 439 return false; 440 } 441 if (source != null ? !source.equals(that.source) : that.source != null) { 442 return false; 443 } 444 if (marker != null ? !marker.equals(that.marker) : that.marker != null) { 445 return false; 446 } 447 if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) { 448 return false; 449 } 450 if (!message.equals(that.message)) { 451 return false; 452 } 453 if (!loggerName.equals(that.loggerName)) { 454 return false; 455 } 456 if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) { 457 return false; 458 } 459 if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) { 460 return false; 461 } 462 if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) { 463 return false; 464 } 465 if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) { 466 return false; 467 } 468 469 return true; 470 } 471 472 @Override 473 public int hashCode() { 474 int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0; 475 result = 31 * result + (marker != null ? marker.hashCode() : 0); 476 result = 31 * result + (level != null ? level.hashCode() : 0); 477 result = 31 * result + loggerName.hashCode(); 478 result = 31 * result + message.hashCode(); 479 result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32)); 480 result = 31 * result + (thrown != null ? thrown.hashCode() : 0); 481 result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); 482 result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0); 483 result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0); 484 result = 31 * result + (threadName != null ? threadName.hashCode() : 0); 485 result = 31 * result + (source != null ? source.hashCode() : 0); 486 result = 31 * result + (includeLocation ? 1 : 0); 487 result = 31 * result + (endOfBatch ? 1 : 0); 488 return result; 489 } 490 491 /** 492 * Proxy pattern used to serialize the LogEvent. 493 */ 494 private static class LogEventProxy implements Serializable { 495 496 private static final long serialVersionUID = -7139032940312647146L; 497 private final String loggerFQCN; 498 private final Marker marker; 499 private final Level level; 500 private final String loggerName; 501 private final Message message; 502 private final long timeMillis; 503 private final transient Throwable thrown; 504 private final ThrowableProxy thrownProxy; 505 private final Map<String, String> contextMap; 506 private final ThreadContext.ContextStack contextStack; 507 private final String threadName; 508 private final StackTraceElement source; 509 private final boolean isLocationRequired; 510 private final boolean isEndOfBatch; 511 512 public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) { 513 this.loggerFQCN = event.loggerFqcn; 514 this.marker = event.marker; 515 this.level = event.level; 516 this.loggerName = event.loggerName; 517 this.message = event.message; 518 this.timeMillis = event.timeMillis; 519 this.thrown = event.thrown; 520 this.thrownProxy = event.thrownProxy; 521 this.contextMap = event.contextMap; 522 this.contextStack = event.contextStack; 523 this.source = includeLocation ? event.getSource() : null; 524 this.threadName = event.getThreadName(); 525 this.isLocationRequired = includeLocation; 526 this.isEndOfBatch = event.endOfBatch; 527 } 528 529 /** 530 * Returns a Log4jLogEvent using the data in the proxy. 531 * @return Log4jLogEvent. 532 */ 533 protected Object readResolve() { 534 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 535 thrownProxy, contextMap, contextStack, threadName, source, timeMillis); 536 result.setEndOfBatch(isEndOfBatch); 537 result.setIncludeLocation(isLocationRequired); 538 return result; 539 } 540 } 541 }