RainbowGumEventBuilder.java
package io.jstach.rainbowgum.slf4j;
import java.lang.System.Logger.Level;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.slf4j.Marker;
import org.slf4j.spi.LoggingEventBuilder;
import io.jstach.rainbowgum.KeyValues;
import io.jstach.rainbowgum.KeyValues.MutableKeyValues;
import io.jstach.rainbowgum.LogEvent;
import io.jstach.rainbowgum.LogEventLogger;
import io.jstach.rainbowgum.LogEvent.Caller;
import io.jstach.rainbowgum.LogMessageFormatter;
import io.jstach.rainbowgum.LogMessageFormatter.StandardMessageFormatter;
import io.jstach.rainbowgum.slf4j.spi.LoggerDecoratorService.DepthAwareEventBuilder;
/*
* log(String message, Object arg0, Object arg1)'s param names are off by one from
* errorprone's synthesized arg0/arg1/arg2 positional guess (slf4j-api isn't compiled
* with -parameters, so it can't see the real names) - not an actual mismatch.
*/
@SuppressWarnings("OverridingMethodInconsistentArgumentNamesChecker")
class RainbowGumEventBuilder implements LoggingEventBuilder, DepthAwareEventBuilder {
private final LogEventHandler handler;
private @Nullable List<@Nullable Object> args;
private @Nullable MutableKeyValues mutableKeyValues;
private final String loggerName;
private final Level level;
private @Nullable String message;
private @Nullable Throwable throwable;
private static final LogMessageFormatter messageFormatter = StandardMessageFormatter.SLF4J;
private static final int DEPTH_DELTA = 2;
private int depth = 0;
private LogEventLogger logger;
public RainbowGumEventBuilder(LogEventHandler handler, Level level) {
this.handler = handler;
this.level = level;
this.loggerName = handler.loggerName();
this.logger = handler;
}
@Override
public RainbowGumEventBuilder setDepth(int depth) {
this.depth = depth;
return this;
}
MutableKeyValues kvs() {
var kvs = this.mutableKeyValues;
if (kvs == null) {
kvs = this.mutableKeyValues = handler.copyDefaultKeyValues();
}
return kvs;
}
List<@Nullable Object> args() {
var args = this.args;
if (args == null) {
args = this.args = new ArrayList<>(2);
}
return args;
}
@Override
public LoggingEventBuilder setCause(Throwable cause) {
this.throwable = cause;
return this;
}
@Override
public LoggingEventBuilder addMarker(Marker marker) {
return this;
}
@Override
public LoggingEventBuilder addArgument(@Nullable Object p) {
args().add(p);
return this;
}
@Override
public LoggingEventBuilder addArgument(Supplier<?> objectSupplier) {
return addArgument(objectSupplier.get());
}
@Override
public LoggingEventBuilder addKeyValue(String key, @Nullable Object value) {
kvs().putKeyValue(key, value == null ? null : value.toString());
return this;
}
@Override
public LoggingEventBuilder addKeyValue(String key, Supplier<Object> valueSupplier) {
return addKeyValue(key, valueSupplier.get());
}
@Override
public LoggingEventBuilder setMessage(String message) {
this.message = message;
return this;
}
@Override
public LoggingEventBuilder setMessage(Supplier<String> messageSupplier) {
return setMessage(messageSupplier.get());
}
@Override
public LoggingEventBuilder setLogger(LogEventLogger logger) {
this.logger = logger;
return this;
}
@Override
public @Nullable String message() {
return this.message;
}
@Override
public @Nullable Throwable throwable() {
return this.throwable;
}
@Override
public List<@Nullable Object> arguments() {
var args = this.args;
if (args == null) {
return List.of();
}
return Collections.unmodifiableList(new ArrayList<>(args));
}
@Override
public KeyValues keyValues() {
var keyValues = this.mutableKeyValues;
if (keyValues == null) {
return handler.defaultKeyValues();
}
return keyValues;
}
private void _log() {
Instant timestamp = Instant.now();
var thread = Thread.currentThread();
String threadName = thread.getName();
long threadId = thread.threadId();
KeyValues keyValues = this.mutableKeyValues;
if (keyValues == null) {
keyValues = handler.defaultKeyValues();
}
var event = LogEvent.ofAll(timestamp, threadName, threadId, level, loggerName, message, keyValues, throwable,
messageFormatter, this.args);
if (handler.isCallerAware()) {
@Nullable Caller caller = LogEventHandler.stackWalker
.walk(s -> s.skip(depth + DEPTH_DELTA).limit(1).map(f -> Caller.of(f)).findFirst().orElse(null));
if (caller != null) {
event = LogEvent.withCaller(event, caller);
}
}
logger.log(event);
}
@Override
public void log() {
_log();
}
@Override
public void log(String message) {
this.message = Objects.requireNonNullElse(message, "");
_log();
}
@Override
public void log(String message, Object arg) {
setMessage(message);
addArgument(arg);
_log();
}
@Override
public void log(String message, Object arg0, Object arg1) {
setMessage(message);
var args = args();
args.add(arg0);
args.add(arg1);
_log();
}
@Override
public void log(String message, Object... args) {
setMessage(message);
var args_ = args();
if (args != null) {
for (var a : args) {
args_.add(a);
}
}
_log();
}
@Override
public void log(Supplier<String> messageSupplier) {
setMessage(messageSupplier);
_log();
}
}