package org.apache.logging.log4j.core.impl;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.ContextDataInjector;
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.util.ReadOnlyStringMap;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.async.RingBufferLogEvent;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.message.LoggerNameAwareMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.message.TimestampMessage;
import org.apache.logging.log4j.util.StackLocatorUtil;
import org.apache.logging.log4j.util.StringMap;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
public class Log4jLogEvent implements LogEvent {
private static final long serialVersionUID = -8393305700508709443L;
private static final Clock CLOCK = ClockFactory.getClock();
private static volatile NanoClock nanoClock = new DummyNanoClock();
private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();
private final String loggerFqcn;
private final Marker marker;
private final Level level;
private final String loggerName;
private Message message;
private final MutableInstant instant = new MutableInstant();
private final transient Throwable thrown;
private ThrowableProxy thrownProxy;
private final StringMap contextData;
private final ThreadContext.ContextStack contextStack;
private long threadId;
private String threadName;
private int threadPriority;
private StackTraceElement source;
private boolean includeLocation;
private boolean endOfBatch = false;
private final transient long nanoTime;
public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
private String loggerFqcn;
private Marker marker;
private Level level;
private String loggerName;
private Message message;
private Throwable thrown;
private final MutableInstant instant = new MutableInstant();
private ThrowableProxy thrownProxy;
private StringMap contextData = createContextData((List<Property>) null);
private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack();
private long threadId;
private String threadName;
private int threadPriority;
private StackTraceElement source;
private boolean includeLocation;
private boolean endOfBatch = false;
private long nanoTime;
public Builder() {
}
public Builder(final LogEvent other) {
Objects.requireNonNull(other);
if (other instanceof RingBufferLogEvent) {
((RingBufferLogEvent) other).initializeBuilder(this);
return;
}
if (other instanceof MutableLogEvent) {
((MutableLogEvent) other).initializeBuilder(this);
return;
}
this.loggerFqcn = other.getLoggerFqcn();
this.marker = other.getMarker();
this.level = other.getLevel();
this.loggerName = other.getLoggerName();
this.message = other.getMessage();
this.instant.initFrom(other.getInstant());
this.thrown = other.getThrown();
this.contextStack = other.getContextStack();
this.includeLocation = other.isIncludeLocation();
this.endOfBatch = other.isEndOfBatch();
this.nanoTime = other.getNanoTime();
if (other instanceof Log4jLogEvent) {
final Log4jLogEvent evt = (Log4jLogEvent) other;
this.contextData = evt.contextData;
this.thrownProxy = evt.thrownProxy;
this.source = evt.source;
this.threadId = evt.threadId;
this.threadName = evt.threadName;
this.threadPriority = evt.threadPriority;
} else {
if (other.getContextData() instanceof StringMap) {
this.contextData = (StringMap) other.getContextData();
} else {
if (this.contextData.isFrozen()) {
this.contextData = ContextDataFactory.createContextData();
} else {
this.contextData.clear();
}
this.contextData.putAll(other.getContextData());
}
this.thrownProxy = other.getThrownProxy();
this.source = other.getSource();
this.threadId = other.getThreadId();
this.threadName = other.getThreadName();
this.threadPriority = other.getThreadPriority();
}
}
public Builder setLevel(final Level level) {
this.level = level;
return this;
}
public Builder setLoggerFqcn(final String loggerFqcn) {
this.loggerFqcn = loggerFqcn;
return this;
}
public Builder setLoggerName(final String loggerName) {
this.loggerName = loggerName;
return this;
}
public Builder setMarker(final Marker marker) {
this.marker = marker;
return this;
}
public Builder setMessage(final Message message) {
this.message = message;
return this;
}
public Builder setThrown(final Throwable thrown) {
this.thrown = thrown;
return this;
}
public Builder setTimeMillis(final long timeMillis) {
this.instant.initFromEpochMilli(timeMillis, 0);
return this;
}
public Builder setInstant(final Instant instant) {
this.instant.initFrom(instant);
return this;
}
public Builder setThrownProxy(final ThrowableProxy thrownProxy) {
this.thrownProxy = thrownProxy;
return this;
}
@Deprecated
public Builder setContextMap(final Map<String, String> contextMap) {
contextData = ContextDataFactory.createContextData();
if (contextMap != null) {
for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
contextData.putValue(entry.getKey(), entry.getValue());
}
}
return this;
}
public Builder setContextData(final StringMap contextData) {
this.contextData = contextData;
return this;
}
public Builder setContextStack(final ThreadContext.ContextStack contextStack) {
this.contextStack = contextStack;
return this;
}
public Builder setThreadId(final long threadId) {
this.threadId = threadId;
return this;
}
public Builder setThreadName(final String threadName) {
this.threadName = threadName;
return this;
}
public Builder setThreadPriority(final int threadPriority) {
this.threadPriority = threadPriority;
return this;
}
public Builder setSource(final StackTraceElement source) {
this.source = source;
return this;
}
public Builder setIncludeLocation(final boolean includeLocation) {
this.includeLocation = includeLocation;
return this;
}
public Builder setEndOfBatch(final boolean endOfBatch) {
this.endOfBatch = endOfBatch;
return this;
}
public Builder setNanoTime(final long nanoTime) {
this.nanoTime = nanoTime;
return this;
}
@Override
public Log4jLogEvent build() {
initTimeFields();
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown,
thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source,
instant.getEpochMillisecond(), instant.getNanoOfMillisecond(), nanoTime);
result.setIncludeLocation(includeLocation);
result.setEndOfBatch(endOfBatch);
return result;
}
private void initTimeFields() {
if (instant.getEpochMillisecond() == 0) {
instant.initFrom(CLOCK);
}
}
}
public static Builder newBuilder() {
return new Builder();
}
public Log4jLogEvent() {
this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
0, null, CLOCK, nanoClock.nanoTime());
}
@Deprecated
public Log4jLogEvent(final long timestamp) {
this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, 0, null,
0, null, timestamp, 0, nanoClock.nanoTime());
}
@Deprecated
public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable t) {
this(loggerName, marker, loggerFQCN, level, message, null, t);
}
public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final List<Property> properties, final Throwable t) {
this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(properties),
ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(),
0,
null,
0,
null,
CLOCK,
nanoClock.nanoTime());
}
@Deprecated
public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable t, final Map<String, String> mdc,
final ThreadContext.ContextStack ndc, final String threadName,
final StackTraceElement location, final long timestampMillis) {
this(loggerName, marker, loggerFQCN, level, message, t, null, createContextData(mdc), ndc, 0,
threadName, 0, location, timestampMillis, 0, nanoClock.nanoTime());
}
@Deprecated
public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
final Level level, final Message message, final Throwable thrown,
final ThrowableProxy thrownProxy,
final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
final String threadName, final StackTraceElement location,
final long timestamp) {
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, location, timestamp, 0, nanoClock.nanoTime());
return result;
}
private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
final String threadName, final int threadPriority, final StackTraceElement source,
final long timestampMillis, final int nanoOfMillisecond, final long nanoTime) {
this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
final long millis = message instanceof TimestampMessage
? ((TimestampMessage) message).getTimestamp()
: timestampMillis;
instant.initFromEpochMilli(millis, nanoOfMillisecond);
}
private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
final String threadName, final int threadPriority, final StackTraceElement source,
final Clock clock, final long nanoTime) {
this(loggerName, marker, loggerFQCN, level, message, thrown, thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, nanoTime);
if (message instanceof TimestampMessage) {
instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
} else {
instant.initFrom(clock);
}
}
private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId,
final String threadName, final int threadPriority, final StackTraceElement source,
final long nanoTime) {
this.loggerName = loggerName;
this.marker = marker;
this.loggerFqcn = loggerFQCN;
this.level = level == null ? Level.OFF : level;
this.message = message;
this.thrown = thrown;
this.thrownProxy = thrownProxy;
this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
this.threadId = threadId;
this.threadName = threadName;
this.threadPriority = threadPriority;
this.source = source;
if (message instanceof LoggerNameAwareMessage) {
((LoggerNameAwareMessage) message).setLoggerName(loggerName);
}
this.nanoTime = nanoTime;
}
private static StringMap createContextData(final Map<String, String> contextMap) {
final StringMap result = ContextDataFactory.createContextData();
if (contextMap != null) {
for (final Map.Entry<String, String> entry : contextMap.entrySet()) {
result.putValue(entry.getKey(), entry.getValue());
}
}
return result;
}
private static StringMap createContextData(final List<Property> properties) {
final StringMap reusable = ContextDataFactory.createContextData();
return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
}
public static NanoClock getNanoClock() {
return nanoClock;
}
public static void setNanoClock(final NanoClock nanoClock) {
Log4jLogEvent.nanoClock = Objects.requireNonNull(nanoClock, "NanoClock must be non-null");
StatusLogger.getLogger().trace("Using {} for nanosecond timestamps.", nanoClock.getClass().getSimpleName());
}
public Builder asBuilder() {
return new Builder(this);
}
@Override
public Log4jLogEvent toImmutable() {
if (getMessage() instanceof ReusableMessage) {
makeMessageImmutable();
}
return this;
}
@Override
public Level getLevel() {
return level;
}
@Override
public String getLoggerName() {
return loggerName;
}
@Override
public Message getMessage() {
return message;
}
public void makeMessageImmutable() {
message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters());
}
@Override
public long getThreadId() {
if (threadId == 0) {
threadId = Thread.currentThread().getId();
}
return threadId;
}
@Override
public String getThreadName() {
if (threadName == null) {
threadName = Thread.currentThread().getName();
}
return threadName;
}
@Override
public int getThreadPriority() {
if (threadPriority == 0) {
threadPriority = Thread.currentThread().getPriority();
}
return threadPriority;
}
@Override
public long getTimeMillis() {
return instant.getEpochMillisecond();
}
@Override
public Instant getInstant() {
return instant;
}
@Override
public Throwable getThrown() {
return thrown;
}
@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null && thrown != null) {
thrownProxy = new ThrowableProxy(thrown);
}
return thrownProxy;
}
@Override
public Marker getMarker() {
return marker;
}
@Override
public String getLoggerFqcn() {
return loggerFqcn;
}
@Override
public ReadOnlyStringMap getContextData() {
return contextData;
}
@Override
public Map<String, String> getContextMap() {
return contextData.toMap();
}
@Override
public ThreadContext.ContextStack getContextStack() {
return contextStack;
}
@Override
public StackTraceElement getSource() {
if (source != null) {
return source;
}
if (loggerFqcn == null || !includeLocation) {
return null;
}
source = StackLocatorUtil.calcLocation(loggerFqcn);
return source;
}
@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;
}
protected Object writeReplace() {
getThrownProxy();
return new LogEventProxy(this, this.includeLocation);
}
public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
if (event instanceof Log4jLogEvent) {
event.getThrownProxy();
return new LogEventProxy((Log4jLogEvent) event, includeLocation);
}
return new LogEventProxy(event, includeLocation);
}
public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) {
event.getThrownProxy();
return new LogEventProxy(event, includeLocation);
}
public static boolean canDeserialize(final Serializable event) {
return event instanceof LogEventProxy;
}
public static Log4jLogEvent deserialize(final Serializable event) {
Objects.requireNonNull(event, "Event cannot be null");
if (event instanceof LogEventProxy) {
final LogEventProxy proxy = (LogEventProxy) event;
final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
proxy.loggerFQCN, proxy.level, proxy.message,
proxy.thrown, proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId,
proxy.threadName, proxy.threadPriority, proxy.source, proxy.timeMillis, proxy.nanoOfMillisecond,
proxy.nanoTime);
result.setEndOfBatch(proxy.isEndOfBatch);
result.setIncludeLocation(proxy.isLocationRequired);
return result;
}
throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
}
private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
public static LogEvent createMemento(final LogEvent logEvent) {
return new Log4jLogEvent.Builder(logEvent).build();
}
public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
return deserialize(serialize(event, includeLocation));
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
final String n = loggerName.isEmpty() ? LoggerConfig.ROOT : loggerName;
sb.append("Logger=").append(n);
sb.append(" Level=").append(level.name());
sb.append(" Message=").append(message == null ? null : message.getFormattedMessage());
return sb.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Log4jLogEvent that = (Log4jLogEvent) o;
if (endOfBatch != that.endOfBatch) {
return false;
}
if (includeLocation != that.includeLocation) {
return false;
}
if (!instant.equals(that.instant)) {
return false;
}
if (nanoTime != that.nanoTime) {
return false;
}
if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
return false;
}
if (level != null ? !level.equals(that.level) : that.level != null) {
return false;
}
if (source != null ? !source.equals(that.source) : that.source != null) {
return false;
}
if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
return false;
}
if (contextData != null ? !contextData.equals(that.contextData) : that.contextData != null) {
return false;
}
if (!message.equals(that.message)) {
return false;
}
if (!loggerName.equals(that.loggerName)) {
return false;
}
if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
return false;
}
if (threadId != that.threadId) {
return false;
}
if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
return false;
}
if (threadPriority != that.threadPriority) {
return false;
}
if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
return false;
}
if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
result = 31 * result + (marker != null ? marker.hashCode() : 0);
result = 31 * result + (level != null ? level.hashCode() : 0);
result = 31 * result + loggerName.hashCode();
result = 31 * result + message.hashCode();
result = 31 * result + instant.hashCode();
result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
result = 31 * result + (contextData != null ? contextData.hashCode() : 0);
result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
result = 31 * result + (int) (threadId ^ (threadId >>> 32));
result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
result = 31 * result + (threadPriority ^ (threadPriority >>> 32));
result = 31 * result + (source != null ? source.hashCode() : 0);
result = 31 * result + (includeLocation ? 1 : 0);
result = 31 * result + (endOfBatch ? 1 : 0);
return result;
}
static class LogEventProxy implements Serializable {
private static final long serialVersionUID = -8634075037355293699L;
private final String loggerFQCN;
private final Marker marker;
private final Level level;
private final String loggerName;
private final transient Message message;
private MarshalledObject<Message> marshalledMessage;
private String messageString;
private final long timeMillis;
private final int nanoOfMillisecond;
private final transient Throwable thrown;
private final ThrowableProxy thrownProxy;
private final StringMap contextData;
private final ThreadContext.ContextStack contextStack;
private final long threadId;
private final String threadName;
private final int threadPriority;
private final StackTraceElement source;
private final boolean isLocationRequired;
private final boolean isEndOfBatch;
private final transient long nanoTime;
public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
this.loggerFQCN = event.loggerFqcn;
this.marker = event.marker;
this.level = event.level;
this.loggerName = event.loggerName;
this.message = event.message instanceof ReusableMessage
? memento((ReusableMessage) event.message)
: event.message;
this.timeMillis = event.instant.getEpochMillisecond();
this.nanoOfMillisecond = event.instant.getNanoOfMillisecond();
this.thrown = event.thrown;
this.thrownProxy = event.thrownProxy;
this.contextData = event.contextData;
this.contextStack = event.contextStack;
this.source = includeLocation ? event.getSource() : null;
this.threadId = event.getThreadId();
this.threadName = event.getThreadName();
this.threadPriority = event.getThreadPriority();
this.isLocationRequired = includeLocation;
this.isEndOfBatch = event.endOfBatch;
this.nanoTime = event.nanoTime;
}
public LogEventProxy(final LogEvent event, final boolean includeLocation) {
this.loggerFQCN = event.getLoggerFqcn();
this.marker = event.getMarker();
this.level = event.getLevel();
this.loggerName = event.getLoggerName();
final Message temp = event.getMessage();
message = temp instanceof ReusableMessage
? memento((ReusableMessage) temp)
: temp;
this.timeMillis = event.getInstant().getEpochMillisecond();
this.nanoOfMillisecond = event.getInstant().getNanoOfMillisecond();
this.thrown = event.getThrown();
this.thrownProxy = event.getThrownProxy();
this.contextData = memento(event.getContextData());
this.contextStack = event.getContextStack();
this.source = includeLocation ? event.getSource() : null;
this.threadId = event.getThreadId();
this.threadName = event.getThreadName();
this.threadPriority = event.getThreadPriority();
this.isLocationRequired = includeLocation;
this.isEndOfBatch = event.isEndOfBatch();
this.nanoTime = event.getNanoTime();
}
private static Message memento(final ReusableMessage message) {
return message.memento();
}
private static StringMap memento(final ReadOnlyStringMap data) {
final StringMap result = ContextDataFactory.createContextData();
result.putAll(data);
return result;
}
private static MarshalledObject<Message> marshall(final Message msg) {
try {
return new MarshalledObject<>(msg);
} catch (final Exception ex) {
return null;
}
}
private void writeObject(final java.io.ObjectOutputStream s) throws IOException {
this.messageString = message.getFormattedMessage();
this.marshalledMessage = marshall(message);
s.defaultWriteObject();
}
protected Object readResolve() {
final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message(), thrown,
thrownProxy, contextData, contextStack, threadId, threadName, threadPriority, source, timeMillis,
nanoOfMillisecond, nanoTime);
result.setEndOfBatch(isEndOfBatch);
result.setIncludeLocation(isLocationRequired);
return result;
}
private Message message() {
if (marshalledMessage != null) {
try {
return marshalledMessage.get();
} catch (final Exception ex) {
}
}
return new SimpleMessage(messageString);
}
}
}