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 */ 017package org.apache.logging.log4j.core.impl; 018 019import java.io.InvalidObjectException; 020import java.io.ObjectInputStream; 021import java.io.Serializable; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.Objects; 027 028import org.apache.logging.log4j.Level; 029import org.apache.logging.log4j.Marker; 030import org.apache.logging.log4j.ThreadContext; 031import org.apache.logging.log4j.core.LogEvent; 032import org.apache.logging.log4j.core.async.RingBufferLogEvent; 033import org.apache.logging.log4j.core.config.LoggerConfig; 034import org.apache.logging.log4j.core.config.Property; 035import org.apache.logging.log4j.core.util.Clock; 036import org.apache.logging.log4j.core.util.ClockFactory; 037import org.apache.logging.log4j.core.util.DummyNanoClock; 038import org.apache.logging.log4j.core.util.NanoClock; 039import org.apache.logging.log4j.message.LoggerNameAwareMessage; 040import org.apache.logging.log4j.message.Message; 041import org.apache.logging.log4j.message.TimestampMessage; 042import org.apache.logging.log4j.status.StatusLogger; 043import org.apache.logging.log4j.util.Strings; 044 045/** 046 * Implementation of a LogEvent. 047 */ 048public class Log4jLogEvent implements LogEvent { 049 050 private static final long serialVersionUID = -1351367343806656055L; 051 private static final Clock CLOCK = ClockFactory.getClock(); 052 private static volatile NanoClock nanoClock = new DummyNanoClock(); 053 private final String loggerFqcn; 054 private final Marker marker; 055 private final Level level; 056 private final String loggerName; 057 private final Message message; 058 private final long timeMillis; 059 private final transient Throwable thrown; 060 private ThrowableProxy thrownProxy; 061 private final Map<String, String> contextMap; 062 private final ThreadContext.ContextStack contextStack; 063 private String threadName; 064 private StackTraceElement source; 065 private boolean includeLocation; 066 private boolean endOfBatch = false; 067 /** @since Log4J 2.4 */ 068 private final transient long nanoTime; 069 070 /** LogEvent Builder helper class. */ 071 public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> { 072 073 private String loggerFqcn; 074 private Marker marker; 075 private Level level; 076 private String loggerName; 077 private Message message; 078 private Throwable thrown; 079 private long timeMillis = CLOCK.currentTimeMillis(); 080 private ThrowableProxy thrownProxy; 081 private Map<String, String> contextMap = ThreadContext.getImmutableContext(); 082 private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack(); 083 private String threadName = null; 084 private StackTraceElement source; 085 private boolean includeLocation; 086 private boolean endOfBatch = false; 087 private long nanoTime; 088 089 public Builder() { 090 } 091 092 public Builder(LogEvent other) { 093 Objects.requireNonNull(other); 094 if (other instanceof RingBufferLogEvent) { 095 RingBufferLogEvent evt = (RingBufferLogEvent) other; 096 evt.initializeBuilder(this); 097 return; 098 } 099 this.loggerFqcn = other.getLoggerFqcn(); 100 this.marker = other.getMarker(); 101 this.level = other.getLevel(); 102 this.loggerName = other.getLoggerName(); 103 this.message = other.getMessage(); 104 this.timeMillis = other.getTimeMillis(); 105 this.thrown = other.getThrown(); 106 this.contextMap = other.getContextMap(); 107 this.contextStack = other.getContextStack(); 108 this.includeLocation = other.isIncludeLocation(); 109 this.endOfBatch = other.isEndOfBatch(); 110 this.nanoTime = other.getNanoTime(); 111 112 // Avoid unnecessarily initializing thrownProxy, threadName and source if possible 113 if (other instanceof Log4jLogEvent) { 114 Log4jLogEvent evt = (Log4jLogEvent) other; 115 this.thrownProxy = evt.thrownProxy; 116 this.source = evt.source; 117 this.threadName = evt.threadName; 118 } else { 119 this.thrownProxy = other.getThrownProxy(); 120 this.source = other.getSource(); 121 this.threadName = other.getThreadName(); 122 } 123 } 124 125 public Builder setLevel(final Level level) { 126 this.level = level; 127 return this; 128 } 129 130 public Builder setLoggerFqcn(final String loggerFqcn) { 131 this.loggerFqcn = loggerFqcn; 132 return this; 133 } 134 135 public Builder setLoggerName(final String loggerName) { 136 this.loggerName = loggerName; 137 return this; 138 } 139 140 public Builder setMarker(final Marker marker) { 141 this.marker = marker; 142 return this; 143 } 144 145 public Builder setMessage(final Message message) { 146 this.message = message; 147 return this; 148 } 149 150 public Builder setThrown(final Throwable thrown) { 151 this.thrown = thrown; 152 return this; 153 } 154 155 public Builder setTimeMillis(long timeMillis) { 156 this.timeMillis = timeMillis; 157 return this; 158 } 159 160 public Builder setThrownProxy(ThrowableProxy thrownProxy) { 161 this.thrownProxy = thrownProxy; 162 return this; 163 } 164 165 public Builder setContextMap(Map<String, String> contextMap) { 166 this.contextMap = contextMap; 167 return this; 168 } 169 170 public Builder setContextStack(ThreadContext.ContextStack contextStack) { 171 this.contextStack = contextStack; 172 return this; 173 } 174 175 public Builder setThreadName(String threadName) { 176 this.threadName = threadName; 177 return this; 178 } 179 180 public Builder setSource(StackTraceElement source) { 181 this.source = source; 182 return this; 183 } 184 185 public Builder setIncludeLocation(boolean includeLocation) { 186 this.includeLocation = includeLocation; 187 return this; 188 } 189 190 public Builder setEndOfBatch(boolean endOfBatch) { 191 this.endOfBatch = endOfBatch; 192 return this; 193 } 194 195 /** 196 * Sets the nano time for the event. 197 * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event 198 * was created. 199 * @return this builder 200 */ 201 public Builder setNanoTime(long nanoTime) { 202 this.nanoTime = nanoTime; 203 return this; 204 } 205 206 @Override 207 public Log4jLogEvent build() { 208 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown, 209 thrownProxy, contextMap, contextStack, threadName, source, timeMillis, nanoTime); 210 result.setIncludeLocation(includeLocation); 211 result.setEndOfBatch(endOfBatch); 212 return result; 213 } 214 } 215 216 /** 217 * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty. 218 * @return a new empty builder. 219 */ 220 public static Builder newBuilder() { 221 return new Builder(); 222 } 223 224 public Log4jLogEvent() { 225 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, null, 226 CLOCK.currentTimeMillis(), nanoClock.nanoTime()); 227 } 228 229 /** 230 * 231 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 232 */ 233 @Deprecated 234 public Log4jLogEvent(final long timestamp) { 235 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, null, 236 timestamp, nanoClock.nanoTime()); 237 } 238 239 /** 240 * Constructor. 241 * @param loggerName The name of the Logger. 242 * @param marker The Marker or null. 243 * @param loggerFQCN The fully qualified class name of the caller. 244 * @param level The logging Level. 245 * @param message The Message. 246 * @param t A Throwable or null. 247 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 248 */ 249 @Deprecated 250 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 251 final Message message, final Throwable t) { 252 this(loggerName, marker, loggerFQCN, level, message, null, t); 253 } 254 255 /** 256 * Constructor. 257 * @param loggerName The name of the Logger. 258 * @param marker The Marker or null. 259 * @param loggerFQCN The fully qualified class name of the caller. 260 * @param level The logging Level. 261 * @param message The Message. 262 * @param properties properties to add to the event. 263 * @param t A Throwable or null. 264 */ 265 // This constructor is called from LogEventFactories. 266 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 267 final Message message, final List<Property> properties, final Throwable t) { 268 this(loggerName, marker, loggerFQCN, level, message, t, null, 269 createMap(properties), 270 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy 271 null, // thread name 272 null, // stack trace element 273 // LOG4J2-628 use log4j.Clock for timestamps 274 // LOG4J2-744 unless TimestampMessage already has one 275 message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : 276 CLOCK.currentTimeMillis(), 277 nanoClock.nanoTime()); 278 } 279 280 /** 281 * Constructor. 282 * @param loggerName The name of the Logger. 283 * @param marker The Marker or null. 284 * @param loggerFQCN The fully qualified class name of the caller. 285 * @param level The logging Level. 286 * @param message The Message. 287 * @param t A Throwable or null. 288 * @param mdc The mapped diagnostic context. 289 * @param ndc the nested diagnostic context. 290 * @param threadName The name of the thread. 291 * @param location The locations of the caller. 292 * @param timestampMillis The timestamp of the event. 293 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 294 */ 295 @Deprecated 296public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 297 final Message message, final Throwable t, final Map<String, String> mdc, 298 final ThreadContext.ContextStack ndc, final String threadName, 299 final StackTraceElement location, final long timestampMillis) { 300 this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName, 301 location, timestampMillis, nanoClock.nanoTime()); 302 } 303 304 /** 305 * Create a new LogEvent. 306 * @param loggerName The name of the Logger. 307 * @param marker The Marker or null. 308 * @param loggerFQCN The fully qualified class name of the caller. 309 * @param level The logging Level. 310 * @param message The Message. 311 * @param thrown A Throwable or null. 312 * @param thrownProxy A ThrowableProxy or null. 313 * @param mdc The mapped diagnostic context. 314 * @param ndc the nested diagnostic context. 315 * @param threadName The name of the thread. 316 * @param location The locations of the caller. 317 * @param timestamp The timestamp of the event. 318 * @return a new LogEvent 319 * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release. 320 */ 321 @Deprecated 322 public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN, 323 final Level level, final Message message, final Throwable thrown, 324 final ThrowableProxy thrownProxy, 325 final Map<String, String> mdc, final ThreadContext.ContextStack ndc, 326 final String threadName, final StackTraceElement location, 327 final long timestamp) { 328 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 329 thrownProxy, mdc, ndc, threadName, location, timestamp, nanoClock.nanoTime()); 330 return result; 331 } 332 333 /** 334 * Constructor. 335 * @param loggerName The name of the Logger. 336 * @param marker The Marker or null. 337 * @param loggerFQCN The fully qualified class name of the caller. 338 * @param level The logging Level. 339 * @param message The Message. 340 * @param thrown A Throwable or null. 341 * @param thrownProxy A ThrowableProxy or null. 342 * @param contextMap The mapped diagnostic context. 343 * @param contextStack the nested diagnostic context. 344 * @param threadName The name of the thread. 345 * @param source The locations of the caller. 346 * @param timestamp The timestamp of the event. 347 * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was 348 * created. 349 */ 350 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 351 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 352 final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack, 353 final String threadName, final StackTraceElement source, final long timestampMillis, final long nanoTime) { 354 this.loggerName = loggerName; 355 this.marker = marker; 356 this.loggerFqcn = loggerFQCN; 357 this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465 358 this.message = message; 359 this.thrown = thrown; 360 this.thrownProxy = thrownProxy; 361 this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap; 362 this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; 363 this.timeMillis = message instanceof TimestampMessage 364 ? ((TimestampMessage) message).getTimestamp() 365 : timestampMillis; 366 this.threadName = threadName; 367 this.source = source; 368 if (message != null && message instanceof LoggerNameAwareMessage) { 369 ((LoggerNameAwareMessage) message).setLoggerName(loggerName); 370 } 371 this.nanoTime = nanoTime; 372 } 373 374 private static Map<String, String> createMap(final List<Property> properties) { 375 final Map<String, String> contextMap = ThreadContext.getImmutableContext(); 376 if (properties == null || properties.isEmpty()) { 377 return contextMap; // may be ThreadContext.EMPTY_MAP but not null 378 } 379 final Map<String, String> map = new HashMap<>(contextMap); 380 381 for (final Property prop : properties) { 382 if (!map.containsKey(prop.getName())) { 383 map.put(prop.getName(), prop.getValue()); 384 } 385 } 386 return Collections.unmodifiableMap(map); 387 } 388 389 /** 390 * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events. 391 * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events 392 */ 393 public static NanoClock getNanoClock() { 394 return nanoClock; 395 } 396 397 /** 398 * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events. 399 * <p> 400 * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the 401 * configuration changes. 402 * 403 * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events 404 */ 405 public static void setNanoClock(NanoClock nanoClock) { 406 Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null"); 407 StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName()); 408 } 409 410 /** 411 * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event. 412 * @return a new fully initialized builder. 413 */ 414 public Builder asBuilder() { 415 return new Builder(this); 416 } 417 418 /** 419 * Returns the logging Level. 420 * @return the Level associated with this event. 421 */ 422 @Override 423 public Level getLevel() { 424 return level; 425 } 426 427 /** 428 * Returns the name of the Logger used to generate the event. 429 * @return The Logger name. 430 */ 431 @Override 432 public String getLoggerName() { 433 return loggerName; 434 } 435 436 /** 437 * Returns the Message associated with the event. 438 * @return The Message. 439 */ 440 @Override 441 public Message getMessage() { 442 return message; 443 } 444 445 /** 446 * Returns the name of the Thread on which the event was generated. 447 * @return The name of the Thread. 448 */ 449 @Override 450 public String getThreadName() { 451 if (threadName == null) { 452 threadName = Thread.currentThread().getName(); 453 } 454 return threadName; 455 } 456 457 /** 458 * Returns the time in milliseconds from the epoch when the event occurred. 459 * @return The time the event occurred. 460 */ 461 @Override 462 public long getTimeMillis() { 463 return timeMillis; 464 } 465 466 /** 467 * Returns the Throwable associated with the event, or null. 468 * @return The Throwable associated with the event. 469 */ 470 @Override 471 public Throwable getThrown() { 472 return thrown; 473 } 474 475 /** 476 * Returns the ThrowableProxy associated with the event, or null. 477 * @return The ThrowableProxy associated with the event. 478 */ 479 @Override 480 public ThrowableProxy getThrownProxy() { 481 if (thrownProxy == null && thrown != null) { 482 thrownProxy = new ThrowableProxy(thrown); 483 } 484 return thrownProxy; 485 } 486 487 488 /** 489 * Returns the Marker associated with the event, or null. 490 * @return the Marker associated with the event. 491 */ 492 @Override 493 public Marker getMarker() { 494 return marker; 495 } 496 497 /** 498 * The fully qualified class name of the class that was called by the caller. 499 * @return the fully qualified class name of the class that is performing logging. 500 */ 501 @Override 502 public String getLoggerFqcn() { 503 return loggerFqcn; 504 } 505 506 /** 507 * Returns the immutable copy of the ThreadContext Map. 508 * @return The context Map. 509 */ 510 @Override 511 public Map<String, String> getContextMap() { 512 return contextMap; 513 } 514 515 /** 516 * Returns an immutable copy of the ThreadContext stack. 517 * @return The context Stack. 518 */ 519 @Override 520 public ThreadContext.ContextStack getContextStack() { 521 return contextStack; 522 } 523 524 /** 525 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 526 * before the first occurrence of FQCN as a class name. 527 * @return the StackTraceElement for the caller. 528 */ 529 @Override 530 public StackTraceElement getSource() { 531 if (source != null) { 532 return source; 533 } 534 if (loggerFqcn == null || !includeLocation) { 535 return null; 536 } 537 source = calcLocation(loggerFqcn); 538 return source; 539 } 540 541 public static StackTraceElement calcLocation(final String fqcnOfLogger) { 542 if (fqcnOfLogger == null) { 543 return null; 544 } 545 // LOG4J2-1029 new Throwable().getStackTrace is faster than Thread.currentThread().getStackTrace(). 546 final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 547 StackTraceElement last = null; 548 for (int i = stackTrace.length - 1; i > 0; i--) { 549 final String className = stackTrace[i].getClassName(); 550 if (fqcnOfLogger.equals(className)) { 551 return last; 552 } 553 last = stackTrace[i]; 554 } 555 return null; 556 } 557 558 @Override 559 public boolean isIncludeLocation() { 560 return includeLocation; 561 } 562 563 @Override 564 public void setIncludeLocation(final boolean includeLocation) { 565 this.includeLocation = includeLocation; 566 } 567 568 @Override 569 public boolean isEndOfBatch() { 570 return endOfBatch; 571 } 572 573 @Override 574 public void setEndOfBatch(final boolean endOfBatch) { 575 this.endOfBatch = endOfBatch; 576 } 577 578 @Override 579 public long getNanoTime() { 580 return nanoTime; 581 } 582 583 /** 584 * Creates a LogEventProxy that can be serialized. 585 * @return a LogEventProxy. 586 */ 587 protected Object writeReplace() { 588 getThrownProxy(); // ensure ThrowableProxy is initialized 589 return new LogEventProxy(this, this.includeLocation); 590 } 591 592 public static Serializable serialize(final Log4jLogEvent event, 593 final boolean includeLocation) { 594 event.getThrownProxy(); // ensure ThrowableProxy is initialized 595 return new LogEventProxy(event, includeLocation); 596 } 597 598 public static boolean canDeserialize(final Serializable event) { 599 return event instanceof LogEventProxy; 600 } 601 602 public static Log4jLogEvent deserialize(final Serializable event) { 603 Objects.requireNonNull(event, "Event cannot be null"); 604 if (event instanceof LogEventProxy) { 605 final LogEventProxy proxy = (LogEventProxy) event; 606 final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker, 607 proxy.loggerFQCN, proxy.level, proxy.message, 608 proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName, 609 proxy.source, proxy.timeMillis, proxy.nanoTime); 610 result.setEndOfBatch(proxy.isEndOfBatch); 611 result.setIncludeLocation(proxy.isLocationRequired); 612 return result; 613 } 614 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); 615 } 616 617 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 618 throw new InvalidObjectException("Proxy required"); 619 } 620 621 @Override 622 public String toString() { 623 final StringBuilder sb = new StringBuilder(); 624 final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName; 625 sb.append("Logger=").append(n); 626 sb.append(" Level=").append(level.name()); 627 sb.append(" Message=").append(message.getFormattedMessage()); 628 return sb.toString(); 629 } 630 631 @Override 632 public boolean equals(final Object o) { 633 if (this == o) { 634 return true; 635 } 636 if (o == null || getClass() != o.getClass()) { 637 return false; 638 } 639 640 final Log4jLogEvent that = (Log4jLogEvent) o; 641 642 if (endOfBatch != that.endOfBatch) { 643 return false; 644 } 645 if (includeLocation != that.includeLocation) { 646 return false; 647 } 648 if (timeMillis != that.timeMillis) { 649 return false; 650 } 651 if (nanoTime != that.nanoTime) { 652 return false; 653 } 654 if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) { 655 return false; 656 } 657 if (level != null ? !level.equals(that.level) : that.level != null) { 658 return false; 659 } 660 if (source != null ? !source.equals(that.source) : that.source != null) { 661 return false; 662 } 663 if (marker != null ? !marker.equals(that.marker) : that.marker != null) { 664 return false; 665 } 666 if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) { 667 return false; 668 } 669 if (!message.equals(that.message)) { 670 return false; 671 } 672 if (!loggerName.equals(that.loggerName)) { 673 return false; 674 } 675 if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) { 676 return false; 677 } 678 if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) { 679 return false; 680 } 681 if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) { 682 return false; 683 } 684 if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) { 685 return false; 686 } 687 688 return true; 689 } 690 691 @Override 692 public int hashCode() { 693 // Check:OFF: MagicNumber 694 int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0; 695 result = 31 * result + (marker != null ? marker.hashCode() : 0); 696 result = 31 * result + (level != null ? level.hashCode() : 0); 697 result = 31 * result + loggerName.hashCode(); 698 result = 31 * result + message.hashCode(); 699 result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32)); 700 result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32)); 701 result = 31 * result + (thrown != null ? thrown.hashCode() : 0); 702 result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); 703 result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0); 704 result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0); 705 result = 31 * result + (threadName != null ? threadName.hashCode() : 0); 706 result = 31 * result + (source != null ? source.hashCode() : 0); 707 result = 31 * result + (includeLocation ? 1 : 0); 708 result = 31 * result + (endOfBatch ? 1 : 0); 709 // Check:ON: MagicNumber 710 return result; 711 } 712 713 /** 714 * Proxy pattern used to serialize the LogEvent. 715 */ 716 private static class LogEventProxy implements Serializable { 717 718 private static final long serialVersionUID = -7139032940312647146L; 719 private final String loggerFQCN; 720 private final Marker marker; 721 private final Level level; 722 private final String loggerName; 723 private final Message message; 724 private final long timeMillis; 725 private final transient Throwable thrown; 726 private final ThrowableProxy thrownProxy; 727 private final Map<String, String> contextMap; 728 private final ThreadContext.ContextStack contextStack; 729 private final String threadName; 730 private final StackTraceElement source; 731 private final boolean isLocationRequired; 732 private final boolean isEndOfBatch; 733 /** @since Log4J 2.4 */ 734 private final transient long nanoTime; 735 736 public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) { 737 this.loggerFQCN = event.loggerFqcn; 738 this.marker = event.marker; 739 this.level = event.level; 740 this.loggerName = event.loggerName; 741 this.message = event.message; 742 this.timeMillis = event.timeMillis; 743 this.thrown = event.thrown; 744 this.thrownProxy = event.thrownProxy; 745 this.contextMap = event.contextMap; 746 this.contextStack = event.contextStack; 747 this.source = includeLocation ? event.getSource() : null; 748 this.threadName = event.getThreadName(); 749 this.isLocationRequired = includeLocation; 750 this.isEndOfBatch = event.endOfBatch; 751 this.nanoTime = event.nanoTime; 752 } 753 754 /** 755 * Returns a Log4jLogEvent using the data in the proxy. 756 * @return Log4jLogEvent. 757 */ 758 protected Object readResolve() { 759 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 760 thrownProxy, contextMap, contextStack, threadName, source, timeMillis, nanoTime); 761 result.setEndOfBatch(isEndOfBatch); 762 result.setIncludeLocation(isLocationRequired); 763 return result; 764 } 765 } 766}