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