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