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