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.IOException; 020import java.io.InvalidObjectException; 021import java.io.ObjectInputStream; 022import java.io.Serializable; 023import java.rmi.MarshalledObject; 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.ContextDataInjector; 032import org.apache.logging.log4j.core.util.*; 033import org.apache.logging.log4j.core.time.Instant; 034import org.apache.logging.log4j.core.time.MutableInstant; 035import org.apache.logging.log4j.util.ReadOnlyStringMap; 036import org.apache.logging.log4j.core.LogEvent; 037import org.apache.logging.log4j.core.async.RingBufferLogEvent; 038import org.apache.logging.log4j.core.config.LoggerConfig; 039import org.apache.logging.log4j.core.config.Property; 040import org.apache.logging.log4j.message.LoggerNameAwareMessage; 041import org.apache.logging.log4j.message.Message; 042import org.apache.logging.log4j.message.ReusableMessage; 043import org.apache.logging.log4j.message.SimpleMessage; 044import org.apache.logging.log4j.message.TimestampMessage; 045import org.apache.logging.log4j.util.StackLocatorUtil; 046import org.apache.logging.log4j.util.StringMap; 047import org.apache.logging.log4j.status.StatusLogger; 048import org.apache.logging.log4j.util.Strings; 049 050/** 051 * Implementation of a LogEvent. 052 */ 053public class Log4jLogEvent implements LogEvent { 054 055 private static final long serialVersionUID = -8393305700508709443L; 056 private static final Clock CLOCK = ClockFactory.getClock(); 057 private static volatile NanoClock nanoClock = new DummyNanoClock(); 058 private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector(); 059 060 private final String loggerFqcn; 061 private final Marker marker; 062 private final Level level; 063 private final String loggerName; 064 private Message message; 065 private final MutableInstant instant = new MutableInstant(); 066 private final transient Throwable thrown; 067 private ThrowableProxy thrownProxy; 068 private final StringMap contextData; 069 private final ThreadContext.ContextStack contextStack; 070 private long threadId; 071 private String threadName; 072 private int threadPriority; 073 private StackTraceElement source; 074 private boolean includeLocation; 075 private boolean endOfBatch = false; 076 /** @since Log4J 2.4 */ 077 private final transient long nanoTime; 078 079 /** LogEvent Builder helper class. */ 080 public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> { 081 082 private String loggerFqcn; 083 private Marker marker; 084 private Level level; 085 private String loggerName; 086 private Message message; 087 private Throwable thrown; 088 private final MutableInstant instant = new MutableInstant(); 089 private ThrowableProxy thrownProxy; 090 private StringMap contextData = createContextData((List<Property>) null); 091 private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack(); 092 private long threadId; 093 private String threadName; 094 private int threadPriority; 095 private StackTraceElement source; 096 private boolean includeLocation; 097 private boolean endOfBatch = false; 098 private long nanoTime; 099 100 public Builder() { 101 } 102 103 public Builder(final LogEvent other) { 104 Objects.requireNonNull(other); 105 if (other instanceof RingBufferLogEvent) { 106 ((RingBufferLogEvent) other).initializeBuilder(this); 107 return; 108 } 109 if (other instanceof MutableLogEvent) { 110 ((MutableLogEvent) other).initializeBuilder(this); 111 return; 112 } 113 this.loggerFqcn = other.getLoggerFqcn(); 114 this.marker = other.getMarker(); 115 this.level = other.getLevel(); 116 this.loggerName = other.getLoggerName(); 117 this.message = other.getMessage(); 118 this.instant.initFrom(other.getInstant()); 119 this.thrown = other.getThrown(); 120 this.contextStack = other.getContextStack(); 121 this.includeLocation = other.isIncludeLocation(); 122 this.endOfBatch = other.isEndOfBatch(); 123 this.nanoTime = other.getNanoTime(); 124 125 // Avoid unnecessarily initializing thrownProxy, threadName and source if possible 126 if (other instanceof Log4jLogEvent) { 127 final Log4jLogEvent evt = (Log4jLogEvent) other; 128 this.contextData = evt.contextData; 129 this.thrownProxy = evt.thrownProxy; 130 this.source = evt.source; 131 this.threadId = evt.threadId; 132 this.threadName = evt.threadName; 133 this.threadPriority = evt.threadPriority; 134 } else { 135 if (other.getContextData() instanceof StringMap) { 136 this.contextData = (StringMap) other.getContextData(); 137 } else { 138 if (this.contextData.isFrozen()) { 139 this.contextData = ContextDataFactory.createContextData(); 140 } else { 141 this.contextData.clear(); 142 } 143 this.contextData.putAll(other.getContextData()); 144 145 } 146 this.thrownProxy = other.getThrownProxy(); 147 this.source = other.getSource(); 148 this.threadId = other.getThreadId(); 149 this.threadName = other.getThreadName(); 150 this.threadPriority = other.getThreadPriority(); 151 } 152 } 153 154 public Builder setLevel(final Level level) { 155 this.level = level; 156 return this; 157 } 158 159 public Builder setLoggerFqcn(final String loggerFqcn) { 160 this.loggerFqcn = loggerFqcn; 161 return this; 162 } 163 164 public Builder setLoggerName(final String loggerName) { 165 this.loggerName = loggerName; 166 return this; 167 } 168 169 public Builder setMarker(final Marker marker) { 170 this.marker = marker; 171 return this; 172 } 173 174 public Builder setMessage(final Message message) { 175 this.message = message; 176 return this; 177 } 178 179 public Builder setThrown(final Throwable thrown) { 180 this.thrown = thrown; 181 return this; 182 } 183 184 public Builder setTimeMillis(final long timeMillis) { 185 this.instant.initFromEpochMilli(timeMillis, 0); 186 return this; 187 } 188 189 public Builder setInstant(final Instant instant) { 190 this.instant.initFrom(instant); 191 return this; 192 } 193 194 public Builder setThrownProxy(final ThrowableProxy thrownProxy) { 195 this.thrownProxy = thrownProxy; 196 return this; 197 } 198 199 @Deprecated 200 public Builder setContextMap(final Map<String, String> contextMap) { 201 contextData = ContextDataFactory.createContextData(); // replace with new instance 202 if (contextMap != null) { 203 for (final Map.Entry<String, String> entry : contextMap.entrySet()) { 204 contextData.putValue(entry.getKey(), entry.getValue()); 205 } 206 } 207 return this; 208 } 209 210 public Builder setContextData(final StringMap contextData) { 211 this.contextData = contextData; 212 return this; 213 } 214 215 public Builder setContextStack(final ThreadContext.ContextStack contextStack) { 216 this.contextStack = contextStack; 217 return this; 218 } 219 220 public Builder setThreadId(final long threadId) { 221 this.threadId = threadId; 222 return this; 223 } 224 225 public Builder setThreadName(final String threadName) { 226 this.threadName = threadName; 227 return this; 228 } 229 230 public Builder setThreadPriority(final int threadPriority) { 231 this.threadPriority = threadPriority; 232 return this; 233 } 234 235 public Builder setSource(final StackTraceElement source) { 236 this.source = source; 237 return this; 238 } 239 240 public Builder setIncludeLocation(final boolean includeLocation) { 241 this.includeLocation = includeLocation; 242 return this; 243 } 244 245 public Builder setEndOfBatch(final boolean endOfBatch) { 246 this.endOfBatch = endOfBatch; 247 return this; 248 } 249 250 /** 251 * Sets the nano time for the event. 252 * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event 253 * was created. 254 * @return this builder 255 */ 256 public Builder setNanoTime(final long nanoTime) { 257 this.nanoTime = nanoTime; 258 return this; 259 } 260 261 @Override 262 public Log4jLogEvent build() { 263 initTimeFields(); 264 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown, 265 thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, 266 instant.getEpochMillisecond(), instant.getNanoOfMillisecond(), nanoTime); 267 result.setIncludeLocation(includeLocation); 268 result.setEndOfBatch(endOfBatch); 269 return result; 270 } 271 272 private void initTimeFields() { 273 if (instant.getEpochMillisecond() == 0) { 274 instant.initFrom(CLOCK); 275 } 276 } 277 } 278 279 /** 280 * Returns a new empty {@code Log4jLogEvent.Builder} with all fields empty. 281 * @return a new empty builder. 282 */ 283 public static Builder newBuilder() { 284 return new Builder(); 285 } 286 287 public Log4jLogEvent() { 288 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null, 289 0, null, CLOCK, nanoClock.nanoTime()); 290 } 291 292 /** 293 * 294 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 295 */ 296 @Deprecated 297 public Log4jLogEvent(final long timestamp) { 298 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null, 299 0, null, timestamp, 0, nanoClock.nanoTime()); 300 } 301 302 /** 303 * Constructor. 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 t A Throwable or null. 310 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 311 */ 312 @Deprecated 313 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 314 final Message message, final Throwable t) { 315 this(loggerName, marker, loggerFQCN, level, message, null, t); 316 } 317 318 /** 319 * Constructor. 320 * @param loggerName The name of the Logger. 321 * @param marker The Marker or null. 322 * @param loggerFQCN The fully qualified class name of the caller. 323 * @param level The logging Level. 324 * @param message The Message. 325 * @param properties the properties to be merged with ThreadContext key-value pairs into the event's ReadOnlyStringMap. 326 * @param t A Throwable or null. 327 */ 328 // This constructor is called from LogEventFactories. 329 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 330 final Message message, final List<Property> properties, final Throwable t) { 331 this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties), 332 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy 333 0, // thread id 334 null, // thread name 335 0, // thread priority 336 null, // StackTraceElement source 337 CLOCK, // 338 nanoClock.nanoTime()); 339 } 340 341 /** 342 * Constructor. 343 * @param loggerName The name of the Logger. 344 * @param marker The Marker or null. 345 * @param loggerFQCN The fully qualified class name of the caller. 346 * @param level The logging Level. 347 * @param message The Message. 348 * @param t A Throwable or null. 349 * @param mdc The mapped diagnostic context. 350 * @param ndc the nested diagnostic context. 351 * @param threadName The name of the thread. 352 * @param location The locations of the caller. 353 * @param timestampMillis The timestamp of the event. 354 * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor will be removed in an upcoming release. 355 */ 356 @Deprecated 357 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 358 final Message message, final Throwable t, final Map<String, String> mdc, 359 final ThreadContext.ContextStack ndc, final String threadName, 360 final StackTraceElement location, final long timestampMillis) { 361 this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0, 362 threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime()); 363 } 364 365 /** 366 * Create a new LogEvent. 367 * @param loggerName The name of the Logger. 368 * @param marker The Marker or null. 369 * @param loggerFQCN The fully qualified class name of the caller. 370 * @param level The logging Level. 371 * @param message The Message. 372 * @param thrown A Throwable or null. 373 * @param thrownProxy A ThrowableProxy or null. 374 * @param mdc The mapped diagnostic context. 375 * @param ndc the nested diagnostic context. 376 * @param threadName The name of the thread. 377 * @param location The locations of the caller. 378 * @param timestamp The timestamp of the event. 379 * @return a new LogEvent 380 * @deprecated use {@link Log4jLogEvent.Builder} instead. This method will be removed in an upcoming release. 381 */ 382 @Deprecated 383 public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN, 384 final Level level, final Message message, final Throwable thrown, 385 final ThrowableProxy thrownProxy, 386 final Map<String, String> mdc, final ThreadContext.ContextStack ndc, 387 final String threadName, final StackTraceElement location, 388 final long timestamp) { 389 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown, 390 thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime()); 391 return result; 392 } 393 394 /** 395 * Constructor. 396 * @param loggerName The name of the Logger. 397 * @param marker The Marker or null. 398 * @param loggerFQCN The fully qualified class name of the caller. 399 * @param level The logging Level. 400 * @param message The Message. 401 * @param thrown A Throwable or null. 402 * @param thrownProxy A ThrowableProxy or null. 403 * @param contextData The key-value pairs from the context. 404 * @param contextStack the nested diagnostic context. 405 * @param threadId the thread ID 406 * @param threadName The name of the thread. 407 * @param threadPriority the thread priority 408 * @param source The locations of the caller. 409 * @param timestampMillis The timestamp of the event. 410 * @param nanoOfMillisecond the nanoseconds within the millisecond, always positive, never exceeds {@code 999,999} 411 * @param nanoTime The value of the running Java Virtual Machine's high-resolution time source when the event was 412 * created. 413 */ 414 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 415 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 416 final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, 417 final String threadName, final int threadPriority, final StackTraceElement source, 418 final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) { 419 this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime); 420 final long millis = message instanceof TimestampMessage 421 ? ((TimestampMessage) message).getTimestamp() 422 : timestampMillis; 423 instant.initFromEpochMilli(millis, nanoOfMillisecond); 424 } 425 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 426 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 427 final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, 428 final String threadName, final int threadPriority, final StackTraceElement source, 429 final Clock clock, final long nanoTime) { 430 this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime); 431 if (message instanceof TimestampMessage) { 432 instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0); 433 } else { 434 instant.initFrom(clock); 435 } 436 } 437 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, 438 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, 439 final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, 440 final String threadName, final int threadPriority, final StackTraceElement source, 441 final long nanoTime) { 442 this.loggerName = loggerName; 443 this.marker = marker; 444 this.loggerFqcn = loggerFQCN; 445 this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465 446 this.message = message; 447 this.thrown = thrown; 448 this.thrownProxy = thrownProxy; 449 this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData; 450 this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; 451 this.threadId = threadId; 452 this.threadName = threadName; 453 this.threadPriority = threadPriority; 454 this.source = source; 455 if (message instanceof LoggerNameAwareMessage) { 456 ((LoggerNameAwareMessage) message).setLoggerName(loggerName); 457 } 458 this.nanoTime = nanoTime; 459 } 460 461 private static StringMap createContextData(final Map<String, String> contextMap) { 462 final StringMap result = ContextDataFactory.createContextData(); 463 if (contextMap != null) { 464 for (final Map.Entry<String, String> entry : contextMap.entrySet()) { 465 result.putValue(entry.getKey(), entry.getValue()); 466 } 467 } 468 return result; 469 } 470 471 private static StringMap createContextData(final List<Property> properties) { 472 final StringMap reusable = ContextDataFactory.createContextData(); 473 return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable); 474 } 475 476 /** 477 * Returns the {@code NanoClock} to use for creating the nanoTime timestamp of log events. 478 * @return the {@code NanoClock} to use for creating the nanoTime timestamp of log events 479 */ 480 public static NanoClock getNanoClock() { 481 return nanoClock; 482 } 483 484 /** 485 * Sets the {@code NanoClock} to use for creating the nanoTime timestamp of log events. 486 * <p> 487 * FOR INTERNAL USE. This method may be called with a different {@code NanoClock} implementation when the 488 * configuration changes. 489 * 490 * @param nanoClock the {@code NanoClock} to use for creating the nanoTime timestamp of log events 491 */ 492 public static void setNanoClock(final NanoClock nanoClock) { 493 Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null"); 494 StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName()); 495 } 496 497 /** 498 * Returns a new fully initialized {@code Log4jLogEvent.Builder} containing a copy of all fields of this event. 499 * @return a new fully initialized builder. 500 */ 501 public Builder asBuilder() { 502 return new Builder(this); 503 } 504 505 @Override 506 public Log4jLogEvent toImmutable() { 507 if (getMessage() instanceof ReusableMessage) { 508 makeMessageImmutable(); 509 } 510 return this; 511 } 512 513 /** 514 * Returns the logging Level. 515 * @return the Level associated with this event. 516 */ 517 @Override 518 public Level getLevel() { 519 return level; 520 } 521 522 /** 523 * Returns the name of the Logger used to generate the event. 524 * @return The Logger name. 525 */ 526 @Override 527 public String getLoggerName() { 528 return loggerName; 529 } 530 531 /** 532 * Returns the Message associated with the event. 533 * @return The Message. 534 */ 535 @Override 536 public Message getMessage() { 537 return message; 538 } 539 540 public void makeMessageImmutable() { 541 message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters()); 542 } 543 544 @Override 545 public long getThreadId() { 546 if (threadId == 0) { 547 threadId = Thread.currentThread().getId(); 548 } 549 return threadId; 550 } 551 552 /** 553 * Returns the name of the Thread on which the event was generated. 554 * @return The name of the Thread. 555 */ 556 @Override 557 public String getThreadName() { 558 if (threadName == null) { 559 threadName = Thread.currentThread().getName(); 560 } 561 return threadName; 562 } 563 564 @Override 565 public int getThreadPriority() { 566 if (threadPriority == 0) { 567 threadPriority = Thread.currentThread().getPriority(); 568 } 569 return threadPriority; 570 } 571 572 /** 573 * {@inheritDoc} 574 */ 575 @Override 576 public long getTimeMillis() { 577 return instant.getEpochMillisecond(); 578 } 579 580 /** 581 * {@inheritDoc} 582 * @since 2.11 583 */ 584 @Override 585 public Instant getInstant() { 586 return instant; 587 } 588 589 /** 590 * Returns the Throwable associated with the event, or null. 591 * @return The Throwable associated with the event. 592 */ 593 @Override 594 public Throwable getThrown() { 595 return thrown; 596 } 597 598 /** 599 * Returns the ThrowableProxy associated with the event, or null. 600 * @return The ThrowableProxy associated with the event. 601 */ 602 @Override 603 public ThrowableProxy getThrownProxy() { 604 if (thrownProxy == null && thrown != null) { 605 thrownProxy = new ThrowableProxy(thrown); 606 } 607 return thrownProxy; 608 } 609 610 611 /** 612 * Returns the Marker associated with the event, or null. 613 * @return the Marker associated with the event. 614 */ 615 @Override 616 public Marker getMarker() { 617 return marker; 618 } 619 620 /** 621 * The fully qualified class name of the class that was called by the caller. 622 * @return the fully qualified class name of the class that is performing logging. 623 */ 624 @Override 625 public String getLoggerFqcn() { 626 return loggerFqcn; 627 } 628 629 /** 630 * Returns the {@code ReadOnlyStringMap} containing context data key-value pairs. 631 * @return the {@code ReadOnlyStringMap} containing context data key-value pairs 632 * @since 2.7 633 */ 634 @Override 635 public ReadOnlyStringMap getContextData() { 636 return contextData; 637 } 638 /** 639 * Returns the immutable copy of the ThreadContext Map. 640 * @return The context Map. 641 */ 642 @Override 643 public Map<String, String> getContextMap() { 644 return contextData.toMap(); 645 } 646 647 /** 648 * Returns an immutable copy of the ThreadContext stack. 649 * @return The context Stack. 650 */ 651 @Override 652 public ThreadContext.ContextStack getContextStack() { 653 return contextStack; 654 } 655 656 /** 657 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 658 * before the first occurrence of FQCN as a class name. 659 * @return the StackTraceElement for the caller. 660 */ 661 @Override 662 public StackTraceElement getSource() { 663 if (source != null) { 664 return source; 665 } 666 if (loggerFqcn == null || !includeLocation) { 667 return null; 668 } 669 source = StackLocatorUtil.calcLocation(loggerFqcn); 670 return source; 671 } 672 673 @Override 674 public boolean isIncludeLocation() { 675 return includeLocation; 676 } 677 678 @Override 679 public void setIncludeLocation(final boolean includeLocation) { 680 this.includeLocation = includeLocation; 681 } 682 683 @Override 684 public boolean isEndOfBatch() { 685 return endOfBatch; 686 } 687 688 @Override 689 public void setEndOfBatch(final boolean endOfBatch) { 690 this.endOfBatch = endOfBatch; 691 } 692 693 @Override 694 public long getNanoTime() { 695 return nanoTime; 696 } 697 698 /** 699 * Creates a LogEventProxy that can be serialized. 700 * @return a LogEventProxy. 701 */ 702 protected Object writeReplace() { 703 getThrownProxy(); // ensure ThrowableProxy is initialized 704 return new LogEventProxy(this, this.includeLocation); 705 } 706 707 /** 708 * Take a snapshot of the specified {@code LogEvent}. 709 * 710 * @param event the event to take a snapshot of 711 * @param includeLocation if true, this method will obtain caller location information 712 * @return snapshot of the event as a {@code Serializable} object 713 * @see #deserialize(Serializable) 714 * @see #serialize(Log4jLogEvent, boolean) 715 */ 716 public static Serializable serialize(final LogEvent event, final boolean includeLocation) { 717 if (event instanceof Log4jLogEvent) { 718 event.getThrownProxy(); // ensure ThrowableProxy is initialized 719 return new LogEventProxy((Log4jLogEvent) event, includeLocation); 720 } 721 return new LogEventProxy(event, includeLocation); 722 } 723 724 /** 725 * Take a snapshot of the specified {@code Log4jLogEvent}. 726 * 727 * @param event the event to take a snapshot of 728 * @param includeLocation if true, this method will obtain caller location information 729 * @return snapshot of the event as a {@code Serializable} object 730 * @see #deserialize(Serializable) 731 * @see #serialize(LogEvent, boolean) 732 */ 733 public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) { 734 event.getThrownProxy(); // ensure ThrowableProxy is initialized 735 return new LogEventProxy(event, includeLocation); 736 } 737 738 public static boolean canDeserialize(final Serializable event) { 739 return event instanceof LogEventProxy; 740 } 741 742 public static Log4jLogEvent deserialize(final Serializable event) { 743 Objects.requireNonNull(event, "Event cannot be null"); 744 if (event instanceof LogEventProxy) { 745 final LogEventProxy proxy = (LogEventProxy) event; 746 final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker, 747 proxy.loggerFQCN, proxy.level, proxy.message, 748 proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId, 749 proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond, 750 proxy.nanoTime); 751 result.setEndOfBatch(proxy.isEndOfBatch); 752 result.setIncludeLocation(proxy.isLocationRequired); 753 return result; 754 } 755 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); 756 } 757 758 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 759 throw new InvalidObjectException("Proxy required"); 760 } 761 762 public static LogEvent createMemento(final LogEvent logEvent) { 763 return new Log4jLogEvent.Builder(logEvent).build(); 764 } 765 766 /** 767 * Creates and returns a new immutable copy of this {@code Log4jLogEvent}. 768 * 769 * @return a new immutable copy of the data in this {@code Log4jLogEvent} 770 */ 771 public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) { 772 return deserialize(serialize(event, includeLocation)); 773 } 774 775 @Override 776 public String toString() { 777 final StringBuilder sb = new StringBuilder(); 778 final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName; 779 sb.append("Logger=").append(n); 780 sb.append(" Level=").append(level.name()); 781 sb.append(" Message=").append(message == null ? null : message.getFormattedMessage()); 782 return sb.toString(); 783 } 784 785 @Override 786 public boolean equals(final Object o) { 787 if (this == o) { 788 return true; 789 } 790 if (o == null || getClass() != o.getClass()) { 791 return false; 792 } 793 794 final Log4jLogEvent that = (Log4jLogEvent) o; 795 796 if (endOfBatch != that.endOfBatch) { 797 return false; 798 } 799 if (includeLocation != that.includeLocation) { 800 return false; 801 } 802 if (!instant.equals(that.instant)) { 803 return false; 804 } 805 if (nanoTime != that.nanoTime) { 806 return false; 807 } 808 if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) { 809 return false; 810 } 811 if (level != null ? !level.equals(that.level) : that.level != null) { 812 return false; 813 } 814 if (source != null ? !source.equals(that.source) : that.source != null) { 815 return false; 816 } 817 if (marker != null ? !marker.equals(that.marker) : that.marker != null) { 818 return false; 819 } 820 if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) { 821 return false; 822 } 823 if (!message.equals(that.message)) { 824 return false; 825 } 826 if (!loggerName.equals(that.loggerName)) { 827 return false; 828 } 829 if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) { 830 return false; 831 } 832 if (threadId != that.threadId) { 833 return false; 834 } 835 if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) { 836 return false; 837 } 838 if (threadPriority != that.threadPriority) { 839 return false; 840 } 841 if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) { 842 return false; 843 } 844 if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) { 845 return false; 846 } 847 848 return true; 849 } 850 851 @Override 852 public int hashCode() { 853 // Check:OFF: MagicNumber 854 int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0; 855 result = 31 * result + (marker != null ? marker.hashCode() : 0); 856 result = 31 * result + (level != null ? level.hashCode() : 0); 857 result = 31 * result + loggerName.hashCode(); 858 result = 31 * result + message.hashCode(); 859 result = 31 * result + instant.hashCode(); 860 result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32)); 861 result = 31 * result + (thrown != null ? thrown.hashCode() : 0); 862 result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); 863 result = 31 * result + (contextData != null ? contextData.hashCode() : 0); 864 result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0); 865 result = 31 * result + (int) (threadId ^ (threadId >>> 32)); 866 result = 31 * result + (threadName != null ? threadName.hashCode() : 0); 867 result = 31 * result + (threadPriority ^ (threadPriority >>> 32)); 868 result = 31 * result + (source != null ? source.hashCode() : 0); 869 result = 31 * result + (includeLocation ? 1 : 0); 870 result = 31 * result + (endOfBatch ? 1 : 0); 871 // Check:ON: MagicNumber 872 return result; 873 } 874 875 /** 876 * Proxy pattern used to serialize the LogEvent. 877 */ 878 static class LogEventProxy implements Serializable { 879 880 private static final long serialVersionUID = -8634075037355293699L; 881 private final String loggerFQCN; 882 private final Marker marker; 883 private final Level level; 884 private final String loggerName; 885 // transient since 2.8 886 private final transient Message message; 887 /** since 2.8 */ 888 private MarshalledObject<Message> marshalledMessage; 889 /** since 2.8 */ 890 private String messageString; 891 private final long timeMillis; 892 /** since 2.11 */ 893 private final int nanoOfMillisecond; 894 private final transient Throwable thrown; 895 private final ThrowableProxy thrownProxy; 896 /** @since 2.7 */ 897 private final StringMap contextData; 898 private final ThreadContext.ContextStack contextStack; 899 /** @since 2.6 */ 900 private final long threadId; 901 private final String threadName; 902 /** @since 2.6 */ 903 private final int threadPriority; 904 private final StackTraceElement source; 905 private final boolean isLocationRequired; 906 private final boolean isEndOfBatch; 907 /** @since 2.4 */ 908 private final transient long nanoTime; 909 910 public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) { 911 this.loggerFQCN = event.loggerFqcn; 912 this.marker = event.marker; 913 this.level = event.level; 914 this.loggerName = event.loggerName; 915 this.message = event.message instanceof ReusableMessage 916 ? memento((ReusableMessage) event.message) 917 : event.message; 918 this.timeMillis = event.instant.getEpochMillisecond(); 919 this.nanoOfMillisecond = event.instant.getNanoOfMillisecond(); 920 this.thrown = event.thrown; 921 this.thrownProxy = event.thrownProxy; 922 this.contextData = event.contextData; 923 this.contextStack = event.contextStack; 924 this.source = includeLocation ? event.getSource() : null; 925 this.threadId = event.getThreadId(); 926 this.threadName = event.getThreadName(); 927 this.threadPriority = event.getThreadPriority(); 928 this.isLocationRequired = includeLocation; 929 this.isEndOfBatch = event.endOfBatch; 930 this.nanoTime = event.nanoTime; 931 } 932 933 public LogEventProxy(final LogEvent event, final boolean includeLocation) { 934 this.loggerFQCN = event.getLoggerFqcn(); 935 this.marker = event.getMarker(); 936 this.level = event.getLevel(); 937 this.loggerName = event.getLoggerName(); 938 939 final Message temp = event.getMessage(); 940 message = temp instanceof ReusableMessage 941 ? memento((ReusableMessage) temp) 942 : temp; 943 this.timeMillis = event.getInstant().getEpochMillisecond(); 944 this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond(); 945 this.thrown = event.getThrown(); 946 this.thrownProxy = event.getThrownProxy(); 947 this.contextData = memento(event.getContextData()); 948 this.contextStack = event.getContextStack(); 949 this.source = includeLocation ? event.getSource() : null; 950 this.threadId = event.getThreadId(); 951 this.threadName = event.getThreadName(); 952 this.threadPriority = event.getThreadPriority(); 953 this.isLocationRequired = includeLocation; 954 this.isEndOfBatch = event.isEndOfBatch(); 955 this.nanoTime = event.getNanoTime(); 956 } 957 958 private static Message memento(final ReusableMessage message) { 959 return message.memento(); 960 } 961 962 private static StringMap memento(final ReadOnlyStringMap data) { 963 final StringMap result = ContextDataFactory.createContextData(); 964 result.putAll(data); 965 return result; 966 } 967 968 private static MarshalledObject<Message> marshall(final Message msg) { 969 try { 970 return new MarshalledObject<>(msg); 971 } catch (final Exception ex) { 972 return null; 973 } 974 } 975 976 private void writeObject(final java.io.ObjectOutputStream s) throws IOException { 977 this.messageString = message.getFormattedMessage(); 978 this.marshalledMessage = marshall(message); 979 s.defaultWriteObject(); 980 } 981 982 /** 983 * Returns a Log4jLogEvent using the data in the proxy. 984 * @return Log4jLogEvent. 985 */ 986 protected Object readResolve() { 987 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown, 988 thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis, 989 nanoOfMillisecond, nanoTime); 990 result.setEndOfBatch(isEndOfBatch); 991 result.setIncludeLocation(isLocationRequired); 992 return result; 993 } 994 995 private Message message() { 996 if (marshalledMessage != null) { 997 try { 998 return marshalledMessage.get(); 999 } catch (final Exception ex) { 1000 // ignore me 1001 } 1002 } 1003 return new SimpleMessage(messageString); 1004 } 1005 } 1006}