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 static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> { 062 063 private String loggerFqcn; 064 private Marker marker; 065 private Level level; 066 private String loggerName; 067 private Message message; 068 private Throwable thrown; 069 070 public Builder setLoggerFqcn(final String loggerFqcn) { 071 this.loggerFqcn = loggerFqcn; 072 return this; 073 } 074 075 public Builder setMarker(final Marker marker) { 076 this.marker = marker; 077 return this; 078 } 079 080 public Builder setLevel(final Level level) { 081 this.level = level; 082 return this; 083 } 084 085 public Builder setLoggerName(final String loggerName) { 086 this.loggerName = loggerName; 087 return this; 088 } 089 090 public Builder setMessage(final Message message) { 091 this.message = message; 092 return this; 093 } 094 095 public Builder setThrown(final Throwable thrown) { 096 this.thrown = thrown; 097 return this; 098 } 099 100 @Override 101 public Log4jLogEvent build() { 102 return new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown); 103 } 104 } 105 106 public static Builder newBuilder() { 107 return new Builder(); 108 } 109 110 public Log4jLogEvent() { 111 this(clock.currentTimeMillis()); 112 } 113 114 /** 115 * 116 */ 117 public Log4jLogEvent(final long timestamp) { 118 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, timestamp); 119 } 120 121 /** 122 * Constructor. 123 * @param loggerName The name of the Logger. 124 * @param marker The Marker or null. 125 * @param loggerFQCN The fully qualified class name of the caller. 126 * @param level The logging Level. 127 * @param message The Message. 128 * @param t A Throwable or null. 129 */ 130 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 131 final Message message, final Throwable t) { 132 this(loggerName, marker, loggerFQCN, level, message, null, t); 133 } 134 135 /** 136 * Constructor. 137 * @param loggerName The name of the Logger. 138 * @param marker The Marker or null. 139 * @param loggerFQCN The fully qualified class name of the caller. 140 * @param level The logging Level. 141 * @param message The Message. 142 * @param properties properties to add to the event. 143 * @param t A Throwable or null. 144 */ 145 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 146 final Message message, final List<Property> properties, final Throwable t) { 147 this(loggerName, marker, loggerFQCN, level, message, t, 148 createMap(properties), 149 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null, 150 null, 151 // LOG4J2-628 use log4j.Clock for timestamps 152 // LOG4J2-744 unless TimestampMessage already has one 153 message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : 154 clock.currentTimeMillis()); 155 } 156 157 /** 158 * Constructor. 159 * @param loggerName The name of the Logger. 160 * @param marker The Marker or null. 161 * @param loggerFQCN The fully qualified class name of the caller. 162 * @param level The logging Level. 163 * @param message The Message. 164 * @param t A Throwable or null. 165 * @param mdc The mapped diagnostic context. 166 * @param ndc the nested diagnostic context. 167 * @param threadName The name of the thread. 168 * @param location The locations of the caller. 169 * @param timestamp The timestamp of the event. 170 */ 171 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 172 final Message message, final Throwable t, final Map<String, String> mdc, 173 final ThreadContext.ContextStack ndc, final String threadName, 174 final StackTraceElement location, final long timestamp) { 175 this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName, 176 location, timestamp); 177 } 178 179 /** 180 * Create a new LogEvent. 181 * @param loggerName The name of the Logger. 182 * @param marker The Marker or null. 183 * @param loggerFQCN The fully qualified class name of the caller. 184 * @param level The logging Level. 185 * @param message The Message. 186 * @param thrown A Throwable or null. 187 * @param thrownProxy A ThrowableProxy or null. 188 * @param mdc The mapped diagnostic context. 189 * @param ndc the nested diagnostic context. 190 * @param threadName The name of the thread. 191 * @param location The locations of the caller. 192 * @param timestamp The timestamp of the event. 193 */ 194 public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN, 195 final Level level, final Message message, final Throwable thrown, 196 final ThrowableProxy thrownProxy, 197 final Map<String, String> mdc, final ThreadContext.ContextStack ndc, 198 final String threadName, final StackTraceElement location, 199 final long timestamp) { 200 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 201 thrownProxy, mdc, ndc, threadName, location, timestamp); 202 return result; 203 } 204 205 /** 206 * Constructor. 207 * @param loggerName The name of the Logger. 208 * @param marker The Marker or null. 209 * @param loggerFQCN The fully qualified class name of the caller. 210 * @param level The logging Level. 211 * @param message The Message. 212 * @param thrown A Throwable or null. 213 * @param thrownProxy A ThrowableProxy or null. 214 * @param contextMap The mapped diagnostic context. 215 * @param contextStack the nested diagnostic context. 216 * @param threadName The name of the thread. 217 * @param source The locations of the caller. 218 * @param timestamp The timestamp of the event. 219 */ 220 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 221 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 222 final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack, 223 final String threadName, final StackTraceElement source, final long timestamp) { 224 this.loggerName = loggerName; 225 this.marker = marker; 226 this.loggerFqcn = loggerFQCN; 227 this.level = (level == null) ? Level.OFF : level; // LOG4J2-462, LOG4J2-465 228 this.message = message; 229 this.thrown = thrown; 230 this.thrownProxy = thrownProxy; 231 this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap; 232 this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; 233 this.timeMillis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp; 234 this.threadName = threadName; 235 this.source = source; 236 if (message != null && message instanceof LoggerNameAwareMessage) { 237 ((LoggerNameAwareMessage) message).setLoggerName(loggerName); 238 } 239 } 240 241 private static Map<String, String> createMap(final List<Property> properties) { 242 final Map<String, String> contextMap = ThreadContext.getImmutableContext(); 243 if (contextMap == null && (properties == null || properties.isEmpty())) { 244 return null; 245 } 246 if (properties == null || properties.isEmpty()) { 247 return contextMap; // contextMap is not null 248 } 249 final Map<String, String> map = new HashMap<String, String>(contextMap); 250 251 for (final Property prop : properties) { 252 if (!map.containsKey(prop.getName())) { 253 map.put(prop.getName(), prop.getValue()); 254 } 255 } 256 return Collections.unmodifiableMap(map); 257 } 258 259 /** 260 * Returns the logging Level. 261 * @return the Level associated with this event. 262 */ 263 @Override 264 public Level getLevel() { 265 return level; 266 } 267 268 /** 269 * Returns the name of the Logger used to generate the event. 270 * @return The Logger name. 271 */ 272 @Override 273 public String getLoggerName() { 274 return loggerName; 275 } 276 277 /** 278 * Returns the Message associated with the event. 279 * @return The Message. 280 */ 281 @Override 282 public Message getMessage() { 283 return message; 284 } 285 286 /** 287 * Returns the name of the Thread on which the event was generated. 288 * @return The name of the Thread. 289 */ 290 @Override 291 public String getThreadName() { 292 if (threadName == null) { 293 threadName = Thread.currentThread().getName(); 294 } 295 return threadName; 296 } 297 298 /** 299 * Returns the time in milliseconds from the epoch when the event occurred. 300 * @return The time the event occurred. 301 */ 302 @Override 303 public long getTimeMillis() { 304 return timeMillis; 305 } 306 307 /** 308 * Returns the Throwable associated with the event, or null. 309 * @return The Throwable associated with the event. 310 */ 311 @Override 312 public Throwable getThrown() { 313 return thrown; 314 } 315 316 /** 317 * Returns the ThrowableProxy associated with the event, or null. 318 * @return The ThrowableProxy associated with the event. 319 */ 320 @Override 321 public ThrowableProxy getThrownProxy() { 322 if (thrownProxy == null && thrown != null) { 323 thrownProxy = new ThrowableProxy(thrown); 324 } 325 return thrownProxy; 326 } 327 328 329 /** 330 * Returns the Marker associated with the event, or null. 331 * @return the Marker associated with the event. 332 */ 333 @Override 334 public Marker getMarker() { 335 return marker; 336 } 337 338 /** 339 * The fully qualified class name of the class that was called by the caller. 340 * @return the fully qualified class name of the class that is performing logging. 341 */ 342 @Override 343 public String getLoggerFqcn() { 344 return loggerFqcn; 345 } 346 347 /** 348 * Returns the immutable copy of the ThreadContext Map. 349 * @return The context Map. 350 */ 351 @Override 352 public Map<String, String> getContextMap() { 353 return contextMap; 354 } 355 356 /** 357 * Returns an immutable copy of the ThreadContext stack. 358 * @return The context Stack. 359 */ 360 @Override 361 public ThreadContext.ContextStack getContextStack() { 362 return contextStack; 363 } 364 365 /** 366 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 367 * before the first occurrence of FQCN as a class name. 368 * @return the StackTraceElement for the caller. 369 */ 370 @Override 371 public StackTraceElement getSource() { 372 if (source != null) { 373 return source; 374 } 375 if (loggerFqcn == null || !includeLocation) { 376 return null; 377 } 378 source = calcLocation(loggerFqcn); 379 return source; 380 } 381 382 public static StackTraceElement calcLocation(final String fqcnOfLogger) { 383 if (fqcnOfLogger == null) { 384 return null; 385 } 386 final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 387 StackTraceElement last = null; 388 for (int i = stackTrace.length - 1; i > 0; i--) { 389 final String className = stackTrace[i].getClassName(); 390 if (fqcnOfLogger.equals(className)) { 391 return last; 392 } 393 last = stackTrace[i]; 394 } 395 return null; 396 } 397 398 @Override 399 public boolean isIncludeLocation() { 400 return includeLocation; 401 } 402 403 @Override 404 public void setIncludeLocation(final boolean includeLocation) { 405 this.includeLocation = includeLocation; 406 } 407 408 @Override 409 public boolean isEndOfBatch() { 410 return endOfBatch; 411 } 412 413 @Override 414 public void setEndOfBatch(final boolean endOfBatch) { 415 this.endOfBatch = endOfBatch; 416 } 417 418 /** 419 * Creates a LogEventProxy that can be serialized. 420 * @return a LogEventProxy. 421 */ 422 protected Object writeReplace() { 423 getThrownProxy(); // ensure ThrowableProxy is initialized 424 return new LogEventProxy(this, this.includeLocation); 425 } 426 427 public static Serializable serialize(final Log4jLogEvent event, 428 final boolean includeLocation) { 429 event.getThrownProxy(); // ensure ThrowableProxy is initialized 430 return new LogEventProxy(event, includeLocation); 431 } 432 433 public static boolean canDeserialize(final Serializable event) { 434 return event instanceof LogEventProxy; 435 } 436 437 public static Log4jLogEvent deserialize(final Serializable event) { 438 if (event == null) { 439 throw new NullPointerException("Event cannot be null"); 440 } 441 if (event instanceof LogEventProxy) { 442 final LogEventProxy proxy = (LogEventProxy) event; 443 final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker, 444 proxy.loggerFQCN, proxy.level, proxy.message, 445 proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName, 446 proxy.source, proxy.timeMillis); 447 result.setEndOfBatch(proxy.isEndOfBatch); 448 result.setIncludeLocation(proxy.isLocationRequired); 449 return result; 450 } 451 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); 452 } 453 454 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 455 throw new InvalidObjectException("Proxy required"); 456 } 457 458 @Override 459 public String toString() { 460 final StringBuilder sb = new StringBuilder(); 461 final String n = loggerName.isEmpty() ? "root" : loggerName; 462 sb.append("Logger=").append(n); 463 sb.append(" Level=").append(level.name()); 464 sb.append(" Message=").append(message.getFormattedMessage()); 465 return sb.toString(); 466 } 467 468 @Override 469 public boolean equals(final Object o) { 470 if (this == o) { 471 return true; 472 } 473 if (o == null || getClass() != o.getClass()) { 474 return false; 475 } 476 477 final Log4jLogEvent that = (Log4jLogEvent) o; 478 479 if (endOfBatch != that.endOfBatch) { 480 return false; 481 } 482 if (includeLocation != that.includeLocation) { 483 return false; 484 } 485 if (timeMillis != that.timeMillis) { 486 return false; 487 } 488 if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) { 489 return false; 490 } 491 if (level != null ? !level.equals(that.level) : that.level != null) { 492 return false; 493 } 494 if (source != null ? !source.equals(that.source) : that.source != null) { 495 return false; 496 } 497 if (marker != null ? !marker.equals(that.marker) : that.marker != null) { 498 return false; 499 } 500 if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) { 501 return false; 502 } 503 if (!message.equals(that.message)) { 504 return false; 505 } 506 if (!loggerName.equals(that.loggerName)) { 507 return false; 508 } 509 if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) { 510 return false; 511 } 512 if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) { 513 return false; 514 } 515 if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) { 516 return false; 517 } 518 if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) { 519 return false; 520 } 521 522 return true; 523 } 524 525 @Override 526 public int hashCode() { 527 int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0; 528 result = 31 * result + (marker != null ? marker.hashCode() : 0); 529 result = 31 * result + (level != null ? level.hashCode() : 0); 530 result = 31 * result + loggerName.hashCode(); 531 result = 31 * result + message.hashCode(); 532 result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32)); 533 result = 31 * result + (thrown != null ? thrown.hashCode() : 0); 534 result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); 535 result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0); 536 result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0); 537 result = 31 * result + (threadName != null ? threadName.hashCode() : 0); 538 result = 31 * result + (source != null ? source.hashCode() : 0); 539 result = 31 * result + (includeLocation ? 1 : 0); 540 result = 31 * result + (endOfBatch ? 1 : 0); 541 return result; 542 } 543 544 /** 545 * Proxy pattern used to serialize the LogEvent. 546 */ 547 private static class LogEventProxy implements Serializable { 548 549 private static final long serialVersionUID = -7139032940312647146L; 550 private final String loggerFQCN; 551 private final Marker marker; 552 private final Level level; 553 private final String loggerName; 554 private final Message message; 555 private final long timeMillis; 556 private final transient Throwable thrown; 557 private final ThrowableProxy thrownProxy; 558 private final Map<String, String> contextMap; 559 private final ThreadContext.ContextStack contextStack; 560 private final String threadName; 561 private final StackTraceElement source; 562 private final boolean isLocationRequired; 563 private final boolean isEndOfBatch; 564 565 public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) { 566 this.loggerFQCN = event.loggerFqcn; 567 this.marker = event.marker; 568 this.level = event.level; 569 this.loggerName = event.loggerName; 570 this.message = event.message; 571 this.timeMillis = event.timeMillis; 572 this.thrown = event.thrown; 573 this.thrownProxy = event.thrownProxy; 574 this.contextMap = event.contextMap; 575 this.contextStack = event.contextStack; 576 this.source = includeLocation ? event.getSource() : null; 577 this.threadName = event.getThreadName(); 578 this.isLocationRequired = includeLocation; 579 this.isEndOfBatch = event.endOfBatch; 580 } 581 582 /** 583 * Returns a Log4jLogEvent using the data in the proxy. 584 * @return Log4jLogEvent. 585 */ 586 protected Object readResolve() { 587 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 588 thrownProxy, contextMap, contextStack, threadName, source, timeMillis); 589 result.setEndOfBatch(isEndOfBatch); 590 result.setIncludeLocation(isLocationRequired); 591 return result; 592 } 593 } 594 }