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.async; 018 019import java.io.IOException; 020import java.util.Arrays; 021import java.util.Map; 022 023import org.apache.logging.log4j.Level; 024import org.apache.logging.log4j.Marker; 025import org.apache.logging.log4j.ThreadContext.ContextStack; 026import org.apache.logging.log4j.core.LogEvent; 027import org.apache.logging.log4j.core.impl.ContextDataFactory; 028import org.apache.logging.log4j.core.impl.Log4jLogEvent; 029import org.apache.logging.log4j.core.impl.ThrowableProxy; 030import org.apache.logging.log4j.core.util.*; 031import org.apache.logging.log4j.core.time.Instant; 032import org.apache.logging.log4j.core.time.MutableInstant; 033import org.apache.logging.log4j.message.*; 034import org.apache.logging.log4j.util.ReadOnlyStringMap; 035import org.apache.logging.log4j.util.StringBuilders; 036import org.apache.logging.log4j.util.StringMap; 037import org.apache.logging.log4j.util.Strings; 038 039import com.lmax.disruptor.EventFactory; 040 041/** 042 * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during 043 * the life of the RingBuffer. 044 */ 045public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequence, ParameterVisitable { 046 047 /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */ 048 public static final Factory FACTORY = new Factory(); 049 050 private static final long serialVersionUID = 8462119088943934758L; 051 private static final Message EMPTY = new SimpleMessage(Strings.EMPTY); 052 053 /** 054 * Creates the events that will be put in the RingBuffer. 055 */ 056 private static class Factory implements EventFactory<RingBufferLogEvent> { 057 058 @Override 059 public RingBufferLogEvent newInstance() { 060 final RingBufferLogEvent result = new RingBufferLogEvent(); 061 if (Constants.ENABLE_THREADLOCALS) { 062 result.messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE); 063 result.parameters = new Object[10]; 064 } 065 return result; 066 } 067 } 068 069 private int threadPriority; 070 private long threadId; 071 private MutableInstant instant = new MutableInstant(); 072 private long nanoTime; 073 private short parameterCount; 074 private boolean includeLocation; 075 private boolean endOfBatch = false; 076 private Level level; 077 private String threadName; 078 private String loggerName; 079 private Message message; 080 private String messageFormat; 081 private StringBuilder messageText; 082 private Object[] parameters; 083 private transient Throwable thrown; 084 private ThrowableProxy thrownProxy; 085 private StringMap contextData = ContextDataFactory.createContextData(); 086 private Marker marker; 087 private String fqcn; 088 private StackTraceElement location; 089 private ContextStack contextStack; 090 091 private transient AsyncLogger asyncLogger; 092 093 public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker, 094 final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, 095 final StringMap mutableContextData, final ContextStack aContextStack, final long threadId, 096 final String threadName, final int threadPriority, final StackTraceElement aLocation, 097 final Clock clock, final NanoClock nanoClock) { 098 this.threadPriority = threadPriority; 099 this.threadId = threadId; 100 this.level = aLevel; 101 this.threadName = threadName; 102 this.loggerName = aLoggerName; 103 setMessage(msg); 104 initTime(clock); 105 this.nanoTime = nanoClock.nanoTime(); 106 this.thrown = aThrowable; 107 this.thrownProxy = null; 108 this.marker = aMarker; 109 this.fqcn = theFqcn; 110 this.location = aLocation; 111 this.contextData = mutableContextData; 112 this.contextStack = aContextStack; 113 this.asyncLogger = anAsyncLogger; 114 } 115 116 private void initTime(final Clock clock) { 117 if (message instanceof TimestampMessage) { 118 instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0); 119 } else { 120 instant.initFrom(clock); 121 } 122 } 123 124 @Override 125 public LogEvent toImmutable() { 126 return createMemento(); 127 } 128 129 private void setMessage(final Message msg) { 130 if (msg instanceof ReusableMessage) { 131 final ReusableMessage reusable = (ReusableMessage) msg; 132 reusable.formatTo(getMessageTextForWriting()); 133 messageFormat = reusable.getFormat(); 134 if (parameters != null) { 135 parameters = reusable.swapParameters(parameters); 136 parameterCount = reusable.getParameterCount(); 137 } 138 } else { 139 this.message = InternalAsyncUtil.makeMessageImmutable(msg); 140 } 141 } 142 143 private StringBuilder getMessageTextForWriting() { 144 if (messageText == null) { 145 // Should never happen: 146 // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false 147 messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE); 148 } 149 messageText.setLength(0); 150 return messageText; 151 } 152 153 /** 154 * Event processor that reads the event from the ringbuffer can call this method. 155 * 156 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer 157 */ 158 public void execute(final boolean endOfBatch) { 159 this.endOfBatch = endOfBatch; 160 asyncLogger.actualAsyncLog(this); 161 } 162 163 /** 164 * Returns {@code true} if this event is the end of a batch, {@code false} otherwise. 165 * 166 * @return {@code true} if this event is the end of a batch, {@code false} otherwise 167 */ 168 @Override 169 public boolean isEndOfBatch() { 170 return endOfBatch; 171 } 172 173 @Override 174 public void setEndOfBatch(final boolean endOfBatch) { 175 this.endOfBatch = endOfBatch; 176 } 177 178 @Override 179 public boolean isIncludeLocation() { 180 return includeLocation; 181 } 182 183 @Override 184 public void setIncludeLocation(final boolean includeLocation) { 185 this.includeLocation = includeLocation; 186 } 187 188 @Override 189 public String getLoggerName() { 190 return loggerName; 191 } 192 193 @Override 194 public Marker getMarker() { 195 return marker; 196 } 197 198 @Override 199 public String getLoggerFqcn() { 200 return fqcn; 201 } 202 203 @Override 204 public Level getLevel() { 205 if (level == null) { 206 level = Level.OFF; // LOG4J2-462, LOG4J2-465 207 } 208 return level; 209 } 210 211 @Override 212 public Message getMessage() { 213 if (message == null) { 214 return messageText == null ? EMPTY : this; 215 } 216 return message; 217 } 218 219 /** 220 * @see ReusableMessage#getFormattedMessage() 221 */ 222 @Override 223 public String getFormattedMessage() { 224 return messageText != null // LOG4J2-1527: may be null in web apps 225 ? messageText.toString() // note: please keep below "redundant" braces for readability 226 : (message == null ? null : message.getFormattedMessage()); 227 } 228 229 /** 230 * @see ReusableMessage#getFormat() 231 */ 232 @Override 233 public String getFormat() { 234 return messageFormat; 235 } 236 237 /** 238 * @see ReusableMessage#getParameters() 239 */ 240 @Override 241 public Object[] getParameters() { 242 return parameters == null ? null : Arrays.copyOf(parameters, parameterCount); 243 } 244 245 /** 246 * @see ReusableMessage#getThrowable() 247 */ 248 @Override 249 public Throwable getThrowable() { 250 return getThrown(); 251 } 252 253 /** 254 * @see ReusableMessage#formatTo(StringBuilder) 255 */ 256 @Override 257 public void formatTo(final StringBuilder buffer) { 258 buffer.append(messageText); 259 } 260 261 /** 262 * Replaces this ReusableMessage's parameter array with the specified value and return the original array 263 * @param emptyReplacement the parameter array that can be used for subsequent uses of this reusable message 264 * @return the original parameter array 265 * @see ReusableMessage#swapParameters(Object[]) 266 */ 267 @Override 268 public Object[] swapParameters(final Object[] emptyReplacement) { 269 final Object[] result = this.parameters; 270 this.parameters = emptyReplacement; 271 return result; 272 } 273 274 /* 275 * @see ReusableMessage#getParameterCount 276 */ 277 @Override 278 public short getParameterCount() { 279 return parameterCount; 280 } 281 282 @Override 283 public <S> void forEachParameter(ParameterConsumer<S> action, S state) { 284 if (parameters != null) { 285 for (short i = 0; i < parameterCount; i++) { 286 action.accept(parameters[i], i, state); 287 } 288 } 289 } 290 291 @Override 292 public Message memento() { 293 if (message != null) { 294 return message; 295 } 296 final Object[] params = parameters == null ? new Object[0] : Arrays.copyOf(parameters, parameterCount); 297 return new ParameterizedMessage(messageText.toString(), params); 298 } 299 300 // CharSequence impl 301 302 @Override 303 public int length() { 304 return messageText.length(); 305 } 306 307 @Override 308 public char charAt(final int index) { 309 return messageText.charAt(index); 310 } 311 312 @Override 313 public CharSequence subSequence(final int start, final int end) { 314 return messageText.subSequence(start, end); 315 } 316 317 318 private Message getNonNullImmutableMessage() { 319 return message != null ? message : new SimpleMessage(String.valueOf(messageText)); 320 } 321 322 @Override 323 public Throwable getThrown() { 324 // after deserialization, thrown is null but thrownProxy may be non-null 325 if (thrown == null) { 326 if (thrownProxy != null) { 327 thrown = thrownProxy.getThrowable(); 328 } 329 } 330 return thrown; 331 } 332 333 @Override 334 public ThrowableProxy getThrownProxy() { 335 // lazily instantiate the (expensive) ThrowableProxy 336 if (thrownProxy == null) { 337 if (thrown != null) { 338 thrownProxy = new ThrowableProxy(thrown); 339 } 340 } 341 return this.thrownProxy; 342 } 343 344 @SuppressWarnings("unchecked") 345 @Override 346 public ReadOnlyStringMap getContextData() { 347 return contextData; 348 } 349 350 void setContextData(final StringMap contextData) { 351 this.contextData = contextData; 352 } 353 354 @SuppressWarnings("unchecked") 355 @Override 356 public Map<String, String> getContextMap() { 357 return contextData.toMap(); 358 } 359 360 @Override 361 public ContextStack getContextStack() { 362 return contextStack; 363 } 364 365 @Override 366 public long getThreadId() { 367 return threadId; 368 } 369 370 @Override 371 public String getThreadName() { 372 return threadName; 373 } 374 375 @Override 376 public int getThreadPriority() { 377 return threadPriority; 378 } 379 380 @Override 381 public StackTraceElement getSource() { 382 return location; 383 } 384 385 @Override 386 public long getTimeMillis() { 387 return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : instant.getEpochMillisecond(); 388 } 389 390 @Override 391 public Instant getInstant() { 392 return instant; 393 } 394 395 @Override 396 public long getNanoTime() { 397 return nanoTime; 398 } 399 400 /** 401 * Release references held by ring buffer to allow objects to be garbage-collected. 402 */ 403 public void clear() { 404 this.asyncLogger = null; 405 this.loggerName = null; 406 this.marker = null; 407 this.fqcn = null; 408 this.level = null; 409 this.message = null; 410 this.messageFormat = null; 411 this.thrown = null; 412 this.thrownProxy = null; 413 this.contextStack = null; 414 this.location = null; 415 if (contextData != null) { 416 if (contextData.isFrozen()) { // came from CopyOnWrite thread context 417 contextData = null; 418 } else { 419 contextData.clear(); 420 } 421 } 422 423 // ensure that excessively long char[] arrays are not kept in memory forever 424 StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE); 425 426 if (parameters != null) { 427 for (int i = 0; i < parameters.length; i++) { 428 parameters[i] = null; 429 } 430 } 431 } 432 433 private void writeObject(final java.io.ObjectOutputStream out) throws IOException { 434 getThrownProxy(); // initialize the ThrowableProxy before serializing 435 out.defaultWriteObject(); 436 } 437 438 /** 439 * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. 440 * 441 * @return a new immutable copy of the data in this {@code RingBufferLogEvent} 442 */ 443 public LogEvent createMemento() { 444 return new Log4jLogEvent.Builder(this).build(); 445 446 } 447 448 /** 449 * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}. 450 * @param builder the builder whose fields to populate 451 */ 452 public void initializeBuilder(final Log4jLogEvent.Builder builder) { 453 builder.setContextData(contextData) // 454 .setContextStack(contextStack) // 455 .setEndOfBatch(endOfBatch) // 456 .setIncludeLocation(includeLocation) // 457 .setLevel(getLevel()) // ensure non-null 458 .setLoggerFqcn(fqcn) // 459 .setLoggerName(loggerName) // 460 .setMarker(marker) // 461 .setMessage(getNonNullImmutableMessage()) // ensure non-null & immutable 462 .setNanoTime(nanoTime) // 463 .setSource(location) // 464 .setThreadId(threadId) // 465 .setThreadName(threadName) // 466 .setThreadPriority(threadPriority) // 467 .setThrown(getThrown()) // may deserialize from thrownProxy 468 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy 469 .setInstant(instant) // 470 ; 471 } 472 473}