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.util.Arrays; 022import java.util.Map; 023 024import org.apache.logging.log4j.Level; 025import org.apache.logging.log4j.Marker; 026import org.apache.logging.log4j.ThreadContext; 027import org.apache.logging.log4j.core.LogEvent; 028import org.apache.logging.log4j.core.async.InternalAsyncUtil; 029import org.apache.logging.log4j.core.util.*; 030import org.apache.logging.log4j.core.time.Instant; 031import org.apache.logging.log4j.core.time.MutableInstant; 032import org.apache.logging.log4j.message.*; 033import org.apache.logging.log4j.util.ReadOnlyStringMap; 034import org.apache.logging.log4j.util.StackLocatorUtil; 035import org.apache.logging.log4j.util.StringBuilders; 036import org.apache.logging.log4j.util.StringMap; 037import org.apache.logging.log4j.util.Strings; 038 039/** 040 * Mutable implementation of the {@code LogEvent} interface. 041 * @since 2.6 042 */ 043public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisitable { 044 private static final Message EMPTY = new SimpleMessage(Strings.EMPTY); 045 046 private int threadPriority; 047 private long threadId; 048 private final MutableInstant instant = new MutableInstant(); 049 private long nanoTime; 050 private short parameterCount; 051 private boolean includeLocation; 052 private boolean endOfBatch = false; 053 private Level level; 054 private String threadName; 055 private String loggerName; 056 private Message message; 057 private String messageFormat; 058 private StringBuilder messageText; 059 private Object[] parameters; 060 private Throwable thrown; 061 private ThrowableProxy thrownProxy; 062 private StringMap contextData = ContextDataFactory.createContextData(); 063 private Marker marker; 064 private String loggerFqcn; 065 private StackTraceElement source; 066 private ThreadContext.ContextStack contextStack; 067 transient boolean reserved = false; 068 069 public MutableLogEvent() { 070 this(new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE), new Object[10]); 071 } 072 073 public MutableLogEvent(final StringBuilder msgText, final Object[] replacementParameters) { 074 this.messageText = msgText; 075 this.parameters = replacementParameters; 076 } 077 078 @Override 079 public Log4jLogEvent toImmutable() { 080 return createMemento(); 081 } 082 083 /** 084 * Initialize the fields of this {@code MutableLogEvent} from another event. 085 * Similar in purpose and usage as {@link org.apache.logging.log4j.core.impl.Log4jLogEvent.LogEventProxy}, 086 * but a mutable version. 087 * <p> 088 * This method is used on async logger ringbuffer slots holding MutableLogEvent objects in each slot. 089 * </p> 090 * 091 * @param event the event to copy data from 092 */ 093 public void initFrom(final LogEvent event) { 094 this.loggerFqcn = event.getLoggerFqcn(); 095 this.marker = event.getMarker(); 096 this.level = event.getLevel(); 097 this.loggerName = event.getLoggerName(); 098 this.thrown = event.getThrown(); 099 this.thrownProxy = event.getThrownProxy(); 100 101 this.instant.initFrom(event.getInstant()); 102 103 // NOTE: this ringbuffer event SHOULD NOT keep a reference to the specified 104 // thread-local MutableLogEvent's context data, because then two threads would call 105 // ReadOnlyStringMap.clear() on the same shared instance, resulting in data corruption. 106 this.contextData.putAll(event.getContextData()); 107 108 this.contextStack = event.getContextStack(); 109 this.source = event.isIncludeLocation() ? event.getSource() : null; 110 this.threadId = event.getThreadId(); 111 this.threadName = event.getThreadName(); 112 this.threadPriority = event.getThreadPriority(); 113 this.endOfBatch = event.isEndOfBatch(); 114 this.includeLocation = event.isIncludeLocation(); 115 this.nanoTime = event.getNanoTime(); 116 setMessage(event.getMessage()); 117 } 118 119 /** 120 * Clears all references this event has to other objects. 121 */ 122 public void clear() { 123 loggerFqcn = null; 124 marker = null; 125 level = null; 126 loggerName = null; 127 message = null; 128 messageFormat = null; 129 thrown = null; 130 thrownProxy = null; 131 source = null; 132 if (contextData != null) { 133 if (contextData.isFrozen()) { // came from CopyOnWrite thread context 134 contextData = null; 135 } else { 136 contextData.clear(); 137 } 138 } 139 contextStack = null; 140 141 // ThreadName should not be cleared: this field is set in the ReusableLogEventFactory 142 // where this instance is kept in a ThreadLocal, so it usually does not change. 143 // threadName = null; // no need to clear threadName 144 145 // ensure that excessively long char[] arrays are not kept in memory forever 146 StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE); 147 148 if (parameters != null) { 149 for (int i = 0; i < parameters.length; i++) { 150 parameters[i] = null; 151 } 152 } 153 154 // primitive fields that cannot be cleared: 155 //timeMillis; 156 //threadId; 157 //threadPriority; 158 //includeLocation; 159 //endOfBatch; 160 //nanoTime; 161 } 162 163 @Override 164 public String getLoggerFqcn() { 165 return loggerFqcn; 166 } 167 168 public void setLoggerFqcn(final String loggerFqcn) { 169 this.loggerFqcn = loggerFqcn; 170 } 171 172 @Override 173 public Marker getMarker() { 174 return marker; 175 } 176 177 public void setMarker(final Marker marker) { 178 this.marker = marker; 179 } 180 181 @Override 182 public Level getLevel() { 183 if (level == null) { 184 level = Level.OFF; // LOG4J2-462, LOG4J2-465 185 } 186 return level; 187 } 188 189 public void setLevel(final Level level) { 190 this.level = level; 191 } 192 193 @Override 194 public String getLoggerName() { 195 return loggerName; 196 } 197 198 public void setLoggerName(final String loggerName) { 199 this.loggerName = loggerName; 200 } 201 202 @Override 203 public Message getMessage() { 204 if (message == null) { 205 return messageText == null ? EMPTY : this; 206 } 207 return message; 208 } 209 210 public void setMessage(final Message msg) { 211 if (msg instanceof ReusableMessage) { 212 final ReusableMessage reusable = (ReusableMessage) msg; 213 reusable.formatTo(getMessageTextForWriting()); 214 this.messageFormat = msg.getFormat(); 215 if (parameters != null) { 216 parameters = reusable.swapParameters(parameters); 217 parameterCount = reusable.getParameterCount(); 218 } 219 } else { 220 this.message = InternalAsyncUtil.makeMessageImmutable(msg); 221 } 222 } 223 224 private StringBuilder getMessageTextForWriting() { 225 if (messageText == null) { 226 // Should never happen: 227 // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false 228 messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE); 229 } 230 messageText.setLength(0); 231 return messageText; 232 } 233 234 /** 235 * @see ReusableMessage#getFormattedMessage() 236 */ 237 @Override 238 public String getFormattedMessage() { 239 return messageText.toString(); 240 } 241 242 /** 243 * @see ReusableMessage#getFormat() 244 */ 245 @Override 246 public String getFormat() { 247 return messageFormat; 248 } 249 250 /** 251 * @see ReusableMessage#getParameters() 252 */ 253 @Override 254 public Object[] getParameters() { 255 return parameters == null ? null : Arrays.copyOf(parameters, parameterCount); 256 } 257 258 @Override 259 public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) { 260 if (parameters != null) { 261 for (short i = 0; i < parameterCount; i++) { 262 action.accept(parameters[i], i, state); 263 } 264 } 265 } 266 267 /** 268 * @see ReusableMessage#getThrowable() 269 */ 270 @Override 271 public Throwable getThrowable() { 272 return getThrown(); 273 } 274 275 /** 276 * @see ReusableMessage#formatTo(StringBuilder) 277 */ 278 @Override 279 public void formatTo(final StringBuilder buffer) { 280 buffer.append(messageText); 281 } 282 283 /** 284 * Replaces this ReusableMessage's parameter array with the specified value and return the original array 285 * @param emptyReplacement the parameter array that can be used for subsequent uses of this reusable message 286 * @return the original parameter array 287 * @see ReusableMessage#swapParameters(Object[]) 288 */ 289 @Override 290 public Object[] swapParameters(final Object[] emptyReplacement) { 291 final Object[] result = this.parameters; 292 this.parameters = emptyReplacement; 293 return result; 294 } 295 296 /* 297 * @see ReusableMessage#getParameterCount 298 */ 299 @Override 300 public short getParameterCount() { 301 return parameterCount; 302 } 303 304 @Override 305 public Message memento() { 306 if (message == null) { 307 message = new MementoMessage(String.valueOf(messageText), messageFormat, getParameters()); 308 } 309 return message; 310 } 311 312 @Override 313 public Throwable getThrown() { 314 return thrown; 315 } 316 317 public void setThrown(final Throwable thrown) { 318 this.thrown = thrown; 319 } 320 321 void initTime(final Clock clock, final NanoClock nanoClock) { 322 if (message instanceof TimestampMessage) { 323 instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0); 324 } else { 325 instant.initFrom(clock); 326 } 327 nanoTime = nanoClock.nanoTime(); 328 } 329 330 @Override 331 public long getTimeMillis() { 332 return instant.getEpochMillisecond(); 333 } 334 335 public void setTimeMillis(final long timeMillis) { 336 this.instant.initFromEpochMilli(timeMillis, 0); 337 } 338 339 @Override 340 public Instant getInstant() { 341 return instant; 342 } 343 344 /** 345 * Returns the ThrowableProxy associated with the event, or null. 346 * @return The ThrowableProxy associated with the event. 347 */ 348 @Override 349 public ThrowableProxy getThrownProxy() { 350 if (thrownProxy == null && thrown != null) { 351 thrownProxy = new ThrowableProxy(thrown); 352 } 353 return thrownProxy; 354 } 355 356 public void setSource(StackTraceElement source) { 357 this.source = source; 358 } 359 360 /** 361 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 362 * before the first occurrence of FQCN as a class name. 363 * @return the StackTraceElement for the caller. 364 */ 365 @Override 366 public StackTraceElement getSource() { 367 if (source != null) { 368 return source; 369 } 370 if (loggerFqcn == null || !includeLocation) { 371 return null; 372 } 373 source = StackLocatorUtil.calcLocation(loggerFqcn); 374 return source; 375 } 376 377 @SuppressWarnings("unchecked") 378 @Override 379 public ReadOnlyStringMap getContextData() { 380 return contextData; 381 } 382 383 @Override 384 public Map<String, String> getContextMap() { 385 return contextData.toMap(); 386 } 387 388 public void setContextData(final StringMap mutableContextData) { 389 this.contextData = mutableContextData; 390 } 391 392 @Override 393 public ThreadContext.ContextStack getContextStack() { 394 return contextStack; 395 } 396 397 public void setContextStack(final ThreadContext.ContextStack contextStack) { 398 this.contextStack = contextStack; 399 } 400 401 @Override 402 public long getThreadId() { 403 return threadId; 404 } 405 406 public void setThreadId(final long threadId) { 407 this.threadId = threadId; 408 } 409 410 @Override 411 public String getThreadName() { 412 return threadName; 413 } 414 415 public void setThreadName(final String threadName) { 416 this.threadName = threadName; 417 } 418 419 @Override 420 public int getThreadPriority() { 421 return threadPriority; 422 } 423 424 public void setThreadPriority(final int threadPriority) { 425 this.threadPriority = threadPriority; 426 } 427 428 @Override 429 public boolean isIncludeLocation() { 430 return includeLocation; 431 } 432 433 @Override 434 public void setIncludeLocation(final boolean includeLocation) { 435 this.includeLocation = includeLocation; 436 } 437 438 @Override 439 public boolean isEndOfBatch() { 440 return endOfBatch; 441 } 442 443 @Override 444 public void setEndOfBatch(final boolean endOfBatch) { 445 this.endOfBatch = endOfBatch; 446 } 447 448 @Override 449 public long getNanoTime() { 450 return nanoTime; 451 } 452 453 public void setNanoTime(final long nanoTime) { 454 this.nanoTime = nanoTime; 455 } 456 457 /** 458 * Creates a LogEventProxy that can be serialized. 459 * @return a LogEventProxy. 460 */ 461 protected Object writeReplace() { 462 return new Log4jLogEvent.LogEventProxy(this, this.includeLocation); 463 } 464 465 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 466 throw new InvalidObjectException("Proxy required"); 467 } 468 469 /** 470 * Creates and returns a new immutable copy of this {@code MutableLogEvent}. 471 * If {@link #isIncludeLocation()} is true, this will obtain caller location information. 472 * 473 * @return a new immutable copy of the data in this {@code MutableLogEvent} 474 */ 475 public Log4jLogEvent createMemento() { 476 return Log4jLogEvent.deserialize(Log4jLogEvent.serialize(this, includeLocation)); 477 } 478 479 /** 480 * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code MutableLogEvent}. 481 * @param builder the builder whose fields to populate 482 */ 483 public void initializeBuilder(final Log4jLogEvent.Builder builder) { 484 builder.setContextData(contextData) // 485 .setContextStack(contextStack) // 486 .setEndOfBatch(endOfBatch) // 487 .setIncludeLocation(includeLocation) // 488 .setLevel(getLevel()) // ensure non-null 489 .setLoggerFqcn(loggerFqcn) // 490 .setLoggerName(loggerName) // 491 .setMarker(marker) // 492 .setMessage(memento()) // ensure non-null & immutable 493 .setNanoTime(nanoTime) // 494 .setSource(source) // 495 .setThreadId(threadId) // 496 .setThreadName(threadName) // 497 .setThreadPriority(threadPriority) // 498 .setThrown(getThrown()) // may deserialize from thrownProxy 499 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy 500 .setInstant(instant) // 501 ; 502 } 503}