package org.apache.logging.log4j.core.async;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext.ContextStack;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.util.*;
import org.apache.logging.log4j.core.time.Instant;
import org.apache.logging.log4j.core.time.MutableInstant;
import org.apache.logging.log4j.message.*;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.StringBuilders;
import org.apache.logging.log4j.util.StringMap;
import org.apache.logging.log4j.util.Strings;
import com.lmax.disruptor.EventFactory;
public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequence, ParameterVisitable {
public static final Factory FACTORY = new Factory();
private static final long serialVersionUID = 8462119088943934758L;
private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
private static class Factory implements EventFactory<RingBufferLogEvent> {
@Override
public RingBufferLogEvent newInstance() {
final RingBufferLogEvent result = new RingBufferLogEvent();
if (Constants.ENABLE_THREADLOCALS) {
result.messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
result.parameters = new Object[10];
}
return result;
}
}
private int threadPriority;
private long threadId;
private MutableInstant instant = new MutableInstant();
private long nanoTime;
private short parameterCount;
private boolean includeLocation;
private boolean endOfBatch = false;
private Level level;
private String threadName;
private String loggerName;
private Message message;
private String messageFormat;
private StringBuilder messageText;
private Object[] parameters;
private transient Throwable thrown;
private ThrowableProxy thrownProxy;
private StringMap contextData = ContextDataFactory.createContextData();
private Marker marker;
private String fqcn;
private StackTraceElement location;
private ContextStack contextStack;
private transient AsyncLogger asyncLogger;
public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker,
final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable,
final StringMap mutableContextData, final ContextStack aContextStack, final long threadId,
final String threadName, final int threadPriority, final StackTraceElement aLocation,
final Clock clock, final NanoClock nanoClock) {
this.threadPriority = threadPriority;
this.threadId = threadId;
this.level = aLevel;
this.threadName = threadName;
this.loggerName = aLoggerName;
setMessage(msg);
initTime(clock);
this.nanoTime = nanoClock.nanoTime();
this.thrown = aThrowable;
this.thrownProxy = null;
this.marker = aMarker;
this.fqcn = theFqcn;
this.location = aLocation;
this.contextData = mutableContextData;
this.contextStack = aContextStack;
this.asyncLogger = anAsyncLogger;
}
private void initTime(final Clock clock) {
if (message instanceof TimestampMessage) {
instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
} else {
instant.initFrom(clock);
}
}
@Override
public LogEvent toImmutable() {
return createMemento();
}
private void setMessage(final Message msg) {
if (msg instanceof ReusableMessage) {
final ReusableMessage reusable = (ReusableMessage) msg;
reusable.formatTo(getMessageTextForWriting());
messageFormat = reusable.getFormat();
if (parameters != null) {
parameters = reusable.swapParameters(parameters);
parameterCount = reusable.getParameterCount();
}
} else {
this.message = InternalAsyncUtil.makeMessageImmutable(msg);
}
}
private StringBuilder getMessageTextForWriting() {
if (messageText == null) {
messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
}
messageText.setLength(0);
return messageText;
}
public void execute(final boolean endOfBatch) {
this.endOfBatch = endOfBatch;
asyncLogger.actualAsyncLog(this);
}
@Override
public boolean isEndOfBatch() {
return endOfBatch;
}
@Override
public void setEndOfBatch(final boolean endOfBatch) {
this.endOfBatch = endOfBatch;
}
@Override
public boolean isIncludeLocation() {
return includeLocation;
}
@Override
public void setIncludeLocation(final boolean includeLocation) {
this.includeLocation = includeLocation;
}
@Override
public String getLoggerName() {
return loggerName;
}
@Override
public Marker getMarker() {
return marker;
}
@Override
public String getLoggerFqcn() {
return fqcn;
}
@Override
public Level getLevel() {
if (level == null) {
level = Level.OFF;
}
return level;
}
@Override
public Message getMessage() {
if (message == null) {
return messageText == null ? EMPTY : this;
}
return message;
}
@Override
public String getFormattedMessage() {
return messageText != null
? messageText.toString()
: (message == null ? null : message.getFormattedMessage());
}
@Override
public String getFormat() {
return messageFormat;
}
@Override
public Object[] getParameters() {
return parameters == null ? null : Arrays.copyOf(parameters, parameterCount);
}
@Override
public Throwable getThrowable() {
return getThrown();
}
@Override
public void formatTo(final StringBuilder buffer) {
buffer.append(messageText);
}
@Override
public Object[] swapParameters(final Object[] emptyReplacement) {
final Object[] result = this.parameters;
this.parameters = emptyReplacement;
return result;
}
@Override
public short getParameterCount() {
return parameterCount;
}
@Override
public <S> void forEachParameter(ParameterConsumer<S> action, S state) {
if (parameters != null) {
for (short i = 0; i < parameterCount; i++) {
action.accept(parameters[i], i, state);
}
}
}
@Override
public Message memento() {
if (message != null) {
return message;
}
final Object[] params = parameters == null ? new Object[0] : Arrays.copyOf(parameters, parameterCount);
return new ParameterizedMessage(messageText.toString(), params);
}
@Override
public int length() {
return messageText.length();
}
@Override
public char charAt(final int index) {
return messageText.charAt(index);
}
@Override
public CharSequence subSequence(final int start, final int end) {
return messageText.subSequence(start, end);
}
private Message getNonNullImmutableMessage() {
return message != null ? message : new SimpleMessage(String.valueOf(messageText));
}
@Override
public Throwable getThrown() {
if (thrown == null) {
if (thrownProxy != null) {
thrown = thrownProxy.getThrowable();
}
}
return thrown;
}
@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null) {
if (thrown != null) {
thrownProxy = new ThrowableProxy(thrown);
}
}
return this.thrownProxy;
}
@SuppressWarnings("unchecked")
@Override
public ReadOnlyStringMap getContextData() {
return contextData;
}
void setContextData(final StringMap contextData) {
this.contextData = contextData;
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getContextMap() {
return contextData.toMap();
}
@Override
public ContextStack getContextStack() {
return contextStack;
}
@Override
public long getThreadId() {
return threadId;
}
@Override
public String getThreadName() {
return threadName;
}
@Override
public int getThreadPriority() {
return threadPriority;
}
@Override
public StackTraceElement getSource() {
return location;
}
@Override
public long getTimeMillis() {
return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : instant.getEpochMillisecond();
}
@Override
public Instant getInstant() {
return instant;
}
@Override
public long getNanoTime() {
return nanoTime;
}
public void clear() {
this.asyncLogger = null;
this.loggerName = null;
this.marker = null;
this.fqcn = null;
this.level = null;
this.message = null;
this.messageFormat = null;
this.thrown = null;
this.thrownProxy = null;
this.contextStack = null;
this.location = null;
if (contextData != null) {
if (contextData.isFrozen()) {
contextData = null;
} else {
contextData.clear();
}
}
StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
parameters[i] = null;
}
}
}
private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
getThrownProxy();
out.defaultWriteObject();
}
public LogEvent createMemento() {
return new Log4jLogEvent.Builder(this).build();
}
public void initializeBuilder(final Log4jLogEvent.Builder builder) {
builder.setContextData(contextData)
.setContextStack(contextStack)
.setEndOfBatch(endOfBatch)
.setIncludeLocation(includeLocation)
.setLevel(getLevel())
.setLoggerFqcn(fqcn)
.setLoggerName(loggerName)
.setMarker(marker)
.setMessage(getNonNullImmutableMessage())
.setNanoTime(nanoTime)
.setSource(location)
.setThreadId(threadId)
.setThreadName(threadName)
.setThreadPriority(threadPriority)
.setThrown(getThrown())
.setThrownProxy(thrownProxy)
.setInstant(instant)
;
}
}