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