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