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.HashMap; 022import java.util.Map; 023 024import org.apache.logging.log4j.Level; 025import org.apache.logging.log4j.Marker; 026import org.apache.logging.log4j.ThreadContext.ContextStack; 027import org.apache.logging.log4j.core.LogEvent; 028import org.apache.logging.log4j.core.config.Property; 029import org.apache.logging.log4j.core.impl.Log4jLogEvent; 030import org.apache.logging.log4j.core.impl.ThrowableProxy; 031import org.apache.logging.log4j.core.lookup.StrSubstitutor; 032import org.apache.logging.log4j.core.util.Constants; 033import org.apache.logging.log4j.message.Message; 034import org.apache.logging.log4j.message.ParameterizedMessage; 035import org.apache.logging.log4j.message.ReusableMessage; 036import org.apache.logging.log4j.message.SimpleMessage; 037import org.apache.logging.log4j.message.TimestampMessage; 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 { 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 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 long currentTimeMillis; 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 StringBuilder messageText; 082 private Object[] parameters; 083 private transient Throwable thrown; 084 private ThrowableProxy thrownProxy; 085 private Map<String, String> contextMap; 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 Map<String, String> aMap, final ContextStack aContextStack, long threadId, 096 final String threadName, int threadPriority, final StackTraceElement aLocation, final long aCurrentTimeMillis, final long aNanoTime) { 097 this.threadPriority = threadPriority; 098 this.threadId = threadId; 099 this.currentTimeMillis = aCurrentTimeMillis; 100 this.nanoTime = aNanoTime; 101 this.level = aLevel; 102 this.threadName = threadName; 103 this.loggerName = aLoggerName; 104 setMessage(msg); 105 this.thrown = aThrowable; 106 this.thrownProxy = null; 107 this.contextMap = aMap; 108 this.marker = aMarker; 109 this.fqcn = theFqcn; 110 this.location = aLocation; 111 this.contextStack = aContextStack; 112 this.asyncLogger = anAsyncLogger; 113 } 114 115 private void setMessage(final Message msg) { 116 if (msg instanceof ReusableMessage) { 117 ReusableMessage reusable = (ReusableMessage) msg; 118 reusable.formatTo(getMessageTextForWriting()); 119 if (parameters != null) { 120 parameters = reusable.swapParameters(parameters); 121 parameterCount = reusable.getParameterCount(); 122 } 123 } else { 124 // if the Message instance is reused, there is no point in freezing its message here 125 if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND && msg != null) { // LOG4J2-898: user may choose 126 msg.getFormattedMessage(); // LOG4J2-763: ask message to freeze parameters 127 } 128 this.message = msg; 129 } 130 } 131 132 private StringBuilder getMessageTextForWriting() { 133 if (messageText == null) { 134 // Should never happen: 135 // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false 136 messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE); 137 } 138 messageText.setLength(0); 139 return messageText; 140 } 141 142 /** 143 * Event processor that reads the event from the ringbuffer can call this method. 144 * 145 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer 146 */ 147 public void execute(final boolean endOfBatch) { 148 this.endOfBatch = endOfBatch; 149 asyncLogger.actualAsyncLog(this); 150 } 151 152 /** 153 * Returns {@code true} if this event is the end of a batch, {@code false} otherwise. 154 * 155 * @return {@code true} if this event is the end of a batch, {@code false} otherwise 156 */ 157 @Override 158 public boolean isEndOfBatch() { 159 return endOfBatch; 160 } 161 162 @Override 163 public void setEndOfBatch(final boolean endOfBatch) { 164 this.endOfBatch = endOfBatch; 165 } 166 167 @Override 168 public boolean isIncludeLocation() { 169 return includeLocation; 170 } 171 172 @Override 173 public void setIncludeLocation(final boolean includeLocation) { 174 this.includeLocation = includeLocation; 175 } 176 177 @Override 178 public String getLoggerName() { 179 return loggerName; 180 } 181 182 @Override 183 public Marker getMarker() { 184 return marker; 185 } 186 187 @Override 188 public String getLoggerFqcn() { 189 return fqcn; 190 } 191 192 @Override 193 public Level getLevel() { 194 if (level == null) { 195 level = Level.OFF; // LOG4J2-462, LOG4J2-465 196 } 197 return level; 198 } 199 200 @Override 201 public Message getMessage() { 202 if (message == null) { 203 return (messageText == null) ? EMPTY : this; 204 } 205 return message; 206 } 207 208 /** 209 * @see ReusableMessage#getFormattedMessage() 210 */ 211 @Override 212 public String getFormattedMessage() { 213 return messageText.toString(); 214 } 215 216 /** 217 * @see ReusableMessage#getFormat() 218 */ 219 @Override 220 public String getFormat() { 221 return null; 222 } 223 224 /** 225 * @see ReusableMessage#getParameters() 226 */ 227 @Override 228 public Object[] getParameters() { 229 return parameters == null ? null : Arrays.copyOf(parameters, parameterCount); 230 } 231 232 /** 233 * @see ReusableMessage#getThrowable() 234 */ 235 @Override 236 public Throwable getThrowable() { 237 return getThrown(); 238 } 239 240 /** 241 * @see ReusableMessage#formatTo(StringBuilder) 242 */ 243 @Override 244 public void formatTo(final StringBuilder buffer) { 245 buffer.append(messageText); 246 } 247 248 /** 249 * Replaces this ReusableMessage's parameter array with the specified value and return the original array 250 * @param emptyReplacement the parameter array that can be used for subsequent uses of this reusable message 251 * @return the original parameter array 252 * @see ReusableMessage#swapParameters(Object[]) 253 */ 254 @Override 255 public Object[] swapParameters(final Object[] emptyReplacement) { 256 final Object[] result = this.parameters; 257 this.parameters = emptyReplacement; 258 return result; 259 } 260 261 /* 262 * @see ReusableMessage#getParameterCount 263 */ 264 @Override 265 public short getParameterCount() { 266 return parameterCount; 267 } 268 269 @Override 270 public Message memento() { 271 if (message != null) { 272 return message; 273 } 274 Object[] params = parameters == null ? new Object[0] : Arrays.copyOf(parameters, parameterCount); 275 return new ParameterizedMessage(messageText.toString(), params); 276 } 277 278 // CharSequence impl 279 280 @Override 281 public int length() { 282 return messageText.length(); 283 } 284 285 @Override 286 public char charAt(int index) { 287 return messageText.charAt(index); 288 } 289 290 @Override 291 public CharSequence subSequence(int start, int end) { 292 return messageText.subSequence(start, end); 293 } 294 295 296 private Message getNonNullImmutableMessage() { 297 return message != null ? message : new SimpleMessage(String.valueOf(messageText)); 298 } 299 300 @Override 301 public Throwable getThrown() { 302 // after deserialization, thrown is null but thrownProxy may be non-null 303 if (thrown == null) { 304 if (thrownProxy != null) { 305 thrown = thrownProxy.getThrowable(); 306 } 307 } 308 return thrown; 309 } 310 311 @Override 312 public ThrowableProxy getThrownProxy() { 313 // lazily instantiate the (expensive) ThrowableProxy 314 if (thrownProxy == null) { 315 if (thrown != null) { 316 thrownProxy = new ThrowableProxy(thrown); 317 } 318 } 319 return this.thrownProxy; 320 } 321 322 @Override 323 public Map<String, String> getContextMap() { 324 return contextMap; 325 } 326 327 @Override 328 public ContextStack getContextStack() { 329 return contextStack; 330 } 331 332 @Override 333 public long getThreadId() { 334 return threadId; 335 } 336 337 @Override 338 public String getThreadName() { 339 return threadName; 340 } 341 342 @Override 343 public int getThreadPriority() { 344 return threadPriority; 345 } 346 347 @Override 348 public StackTraceElement getSource() { 349 return location; 350 } 351 352 @Override 353 public long getTimeMillis() { 354 return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :currentTimeMillis; 355 } 356 357 @Override 358 public long getNanoTime() { 359 return nanoTime; 360 } 361 362 /** 363 * Merges the contents of the specified map into the contextMap, after replacing any variables in the property 364 * values with the StrSubstitutor-supplied actual values. 365 * 366 * @param properties configured properties 367 * @param strSubstitutor used to lookup values of variables in properties 368 */ 369 public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties, 370 final StrSubstitutor strSubstitutor) { 371 if (properties == null) { 372 return; // nothing to do 373 } 374 375 final Map<String, String> map = contextMap == null ? new HashMap<String, String>() 376 : new HashMap<>(contextMap); 377 378 for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) { 379 final Property prop = entry.getKey(); 380 if (map.containsKey(prop.getName())) { 381 continue; // contextMap overrides config properties 382 } 383 final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop 384 .getValue(); 385 map.put(prop.getName(), value); 386 } 387 contextMap = map; 388 } 389 390 /** 391 * Release references held by ring buffer to allow objects to be garbage-collected. 392 */ 393 public void clear() { 394 this.asyncLogger = null; 395 this.loggerName = null; 396 this.marker = null; 397 this.fqcn = null; 398 this.level = null; 399 this.message = null; 400 this.thrown = null; 401 this.thrownProxy = null; 402 this.contextMap = null; 403 this.contextStack = null; 404 this.location = null; 405 406 trimMessageText(); 407 408 if (parameters != null) { 409 for (int i = 0; i < parameters.length; i++) { 410 parameters[i] = null; 411 } 412 } 413 } 414 415 // ensure that excessively long char[] arrays are not kept in memory forever 416 private void trimMessageText() { 417 if (messageText != null && messageText.length() > Constants.MAX_REUSABLE_MESSAGE_SIZE) { 418 messageText.setLength(Constants.MAX_REUSABLE_MESSAGE_SIZE); 419 messageText.trimToSize(); 420 } 421 } 422 423 private void writeObject(final java.io.ObjectOutputStream out) throws IOException { 424 getThrownProxy(); // initialize the ThrowableProxy before serializing 425 out.defaultWriteObject(); 426 } 427 428 /** 429 * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. 430 * 431 * @return a new immutable copy of the data in this {@code RingBufferLogEvent} 432 */ 433 public LogEvent createMemento() { 434 final LogEvent result = new Log4jLogEvent.Builder(this).build(); 435 return result; 436 } 437 438 /** 439 * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}. 440 * @param builder the builder whose fields to populate 441 */ 442 public void initializeBuilder(Log4jLogEvent.Builder builder) { 443 builder.setContextMap(contextMap) // 444 .setContextStack(contextStack) // 445 .setEndOfBatch(endOfBatch) // 446 .setIncludeLocation(includeLocation) // 447 .setLevel(getLevel()) // ensure non-null 448 .setLoggerFqcn(fqcn) // 449 .setLoggerName(loggerName) // 450 .setMarker(marker) // 451 .setMessage(getNonNullImmutableMessage()) // ensure non-null & immutable 452 .setNanoTime(nanoTime) // 453 .setSource(location) // 454 .setThreadId(threadId) // 455 .setThreadName(threadName) // 456 .setThreadPriority(threadPriority) // 457 .setThrown(getThrown()) // may deserialize from thrownProxy 458 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy 459 .setTimeMillis(currentTimeMillis); 460 } 461 462}