package org.apache.logging.log4j.core.impl;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
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;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.async.InternalAsyncUtil;
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.StackLocatorUtil;
import org.apache.logging.log4j.util.StringBuilders;
import org.apache.logging.log4j.util.StringMap;
import org.apache.logging.log4j.util.Strings;
public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisitable {
private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
private int threadPriority;
private long threadId;
private final 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 Throwable thrown;
private ThrowableProxy thrownProxy;
private StringMap contextData = ContextDataFactory.createContextData();
private Marker marker;
private String loggerFqcn;
private StackTraceElement source;
private ThreadContext.ContextStack contextStack;
transient boolean reserved = false;
public MutableLogEvent() {
this(new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE), new Object[10]);
}
public MutableLogEvent(final StringBuilder msgText, final Object[] replacementParameters) {
this.messageText = msgText;
this.parameters = replacementParameters;
}
@Override
public Log4jLogEvent toImmutable() {
return createMemento();
}
public void initFrom(final LogEvent event) {
this.loggerFqcn = event.getLoggerFqcn();
this.marker = event.getMarker();
this.level = event.getLevel();
this.loggerName = event.getLoggerName();
this.thrown = event.getThrown();
this.thrownProxy = event.getThrownProxy();
this.instant.initFrom(event.getInstant());
this.contextData.putAll(event.getContextData());
this.contextStack = event.getContextStack();
this.source = event.isIncludeLocation() ? event.getSource() : null;
this.threadId = event.getThreadId();
this.threadName = event.getThreadName();
this.threadPriority = event.getThreadPriority();
this.endOfBatch = event.isEndOfBatch();
this.includeLocation = event.isIncludeLocation();
this.nanoTime = event.getNanoTime();
setMessage(event.getMessage());
}
public void clear() {
loggerFqcn = null;
marker = null;
level = null;
loggerName = null;
message = null;
messageFormat = null;
thrown = null;
thrownProxy = null;
source = null;
if (contextData != null) {
if (contextData.isFrozen()) {
contextData = null;
} else {
contextData.clear();
}
}
contextStack = null;
StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
parameters[i] = null;
}
}
}
@Override
public String getLoggerFqcn() {
return loggerFqcn;
}
public void setLoggerFqcn(final String loggerFqcn) {
this.loggerFqcn = loggerFqcn;
}
@Override
public Marker getMarker() {
return marker;
}
public void setMarker(final Marker marker) {
this.marker = marker;
}
@Override
public Level getLevel() {
if (level == null) {
level = Level.OFF;
}
return level;
}
public void setLevel(final Level level) {
this.level = level;
}
@Override
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(final String loggerName) {
this.loggerName = loggerName;
}
@Override
public Message getMessage() {
if (message == null) {
return messageText == null ? EMPTY : this;
}
return message;
}
public void setMessage(final Message msg) {
if (msg instanceof ReusableMessage) {
final ReusableMessage reusable = (ReusableMessage) msg;
reusable.formatTo(getMessageTextForWriting());
this.messageFormat = msg.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;
}
@Override
public String getFormattedMessage() {
return messageText.toString();
}
@Override
public String getFormat() {
return messageFormat;
}
@Override
public Object[] getParameters() {
return parameters == null ? null : Arrays.copyOf(parameters, parameterCount);
}
@Override
public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) {
if (parameters != null) {
for (short i = 0; i < parameterCount; i++) {
action.accept(parameters[i], i, state);
}
}
}
@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 Message memento() {
if (message == null) {
message = new MementoMessage(String.valueOf(messageText), messageFormat, getParameters());
}
return message;
}
@Override
public Throwable getThrown() {
return thrown;
}
public void setThrown(final Throwable thrown) {
this.thrown = thrown;
}
void initTime(final Clock clock, final NanoClock nanoClock) {
if (message instanceof TimestampMessage) {
instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
} else {
instant.initFrom(clock);
}
nanoTime = nanoClock.nanoTime();
}
@Override
public long getTimeMillis() {
return instant.getEpochMillisecond();
}
public void setTimeMillis(final long timeMillis) {
this.instant.initFromEpochMilli(timeMillis, 0);
}
@Override
public Instant getInstant() {
return instant;
}
@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null && thrown != null) {
thrownProxy = new ThrowableProxy(thrown);
}
return thrownProxy;
}
@Override
public StackTraceElement getSource() {
if (source != null) {
return source;
}
if (loggerFqcn == null || !includeLocation) {
return null;
}
source = StackLocatorUtil.calcLocation(loggerFqcn);
return source;
}
@SuppressWarnings("unchecked")
@Override
public ReadOnlyStringMap getContextData() {
return contextData;
}
@Override
public Map<String, String> getContextMap() {
return contextData.toMap();
}
public void setContextData(final StringMap mutableContextData) {
this.contextData = mutableContextData;
}
@Override
public ThreadContext.ContextStack getContextStack() {
return contextStack;
}
public void setContextStack(final ThreadContext.ContextStack contextStack) {
this.contextStack = contextStack;
}
@Override
public long getThreadId() {
return threadId;
}
public void setThreadId(final long threadId) {
this.threadId = threadId;
}
@Override
public String getThreadName() {
return threadName;
}
public void setThreadName(final String threadName) {
this.threadName = threadName;
}
@Override
public int getThreadPriority() {
return threadPriority;
}
public void setThreadPriority(final int threadPriority) {
this.threadPriority = threadPriority;
}
@Override
public boolean isIncludeLocation() {
return includeLocation;
}
@Override
public void setIncludeLocation(final boolean includeLocation) {
this.includeLocation = includeLocation;
}
@Override
public boolean isEndOfBatch() {
return endOfBatch;
}
@Override
public void setEndOfBatch(final boolean endOfBatch) {
this.endOfBatch = endOfBatch;
}
@Override
public long getNanoTime() {
return nanoTime;
}
public void setNanoTime(final long nanoTime) {
this.nanoTime = nanoTime;
}
protected Object writeReplace() {
return new Log4jLogEvent.LogEventProxy(this, this.includeLocation);
}
private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
public Log4jLogEvent createMemento() {
return Log4jLogEvent.deserialize(Log4jLogEvent.serialize(this, includeLocation));
}
public void initializeBuilder(final Log4jLogEvent.Builder builder) {
builder.setContextData(contextData)
.setContextStack(contextStack)
.setEndOfBatch(endOfBatch)
.setIncludeLocation(includeLocation)
.setLevel(getLevel())
.setLoggerFqcn(loggerFqcn)
.setLoggerName(loggerName)
.setMarker(marker)
.setMessage(memento())
.setNanoTime(nanoTime)
.setSource(source)
.setThreadId(threadId)
.setThreadName(threadName)
.setThreadPriority(threadPriority)
.setThrown(getThrown())
.setThrownProxy(thrownProxy)
.setInstant(instant)
;
}
}