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