PatternConfigurator.java
package io.jstach.rainbowgum.pattern.format;
import java.nio.charset.Charset;
import java.time.Instant;
import java.time.ZoneId;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import io.jstach.rainbowgum.LogConfig;
import io.jstach.rainbowgum.LogEncoder;
import io.jstach.rainbowgum.LogOutput.OutputType;
import io.jstach.rainbowgum.LogProperties;
import io.jstach.rainbowgum.LogProvider;
import io.jstach.rainbowgum.LogProviderRef;
import io.jstach.rainbowgum.ServiceRegistry;
import io.jstach.rainbowgum.annotation.LogConfigurable;
import io.jstach.rainbowgum.annotation.LogConfigurable.ConvertParameter;
import io.jstach.rainbowgum.annotation.LogConfigurable.KeyParameter;
import io.jstach.rainbowgum.annotation.LogConfigurable.PassThroughParameter;
import io.jstach.rainbowgum.pattern.format.DefaultFormatterConfig.StandardPropertyFunction;
import io.jstach.rainbowgum.spi.RainbowGumServiceProvider;
import io.jstach.rainbowgum.spi.RainbowGumServiceProvider.Configurator;
import io.jstach.svc.ServiceProvider;
/**
* Configures Logback style pattern encoders and offers provision with the URI scheme
* {@value PatternEncoder#PATTERN_SCHEME}.
*
* @see PatternEncoderBuilder
*/
@ServiceProvider(RainbowGumServiceProvider.class)
public final class PatternConfigurator implements Configurator {
/**
* Property that will disable this configurator from replacing the default encoder
* with the pattern encoder. The default encoder is normally the TTLL encoder but if
* this configurator is loaded the pattern encoder will be used and if a no pattern is
* provided a default one is picked.
*/
public static final String LOGGING_PATTERN_DISABLE_PROPERTY = LogProperties.ROOT_PREFIX + "pattern.disable";
/**
* For service loader to call.
*/
public PatternConfigurator() {
// for service loader.
}
@Override
public int priority() {
return -1 << 2; // internal group 2
}
static final String DEFAULT_PATTERN = """
%cyan(%date{HH:mm:ss.SSS}) %clr([%thread]){faint} %highlight(%-5level){ERROR=red bold, INFO=blue bold, WARN=red bold} %magenta(%logger{36}) - %msg%n%ex""";
@Override
public boolean configure(LogConfig config, Pass pass) {
var services = config.serviceRegistry();
String n = ServiceRegistry.DEFAULT_SERVICE_NAME;
services.putIfAbsent(PatternRegistry.class, n, PatternRegistry::of);
services.putIfAbsent(PatternCompiler.class, n, () -> PatternCompiler.of(b -> {
}).provide(n, config));
config.encoderRegistry().register(PatternEncoder.PATTERN_SCHEME, new PatternEncoderProvider());
var disable = config.properties()
.forKey(LOGGING_PATTERN_DISABLE_PROPERTY)
.ofBoolean()
.or(false)
.validateNow(PatternConfigurator.class);
if (!disable) {
config.encoderRegistry().setEncoderForOutputType(OutputType.CONSOLE_OUT, (name, c) -> {
PatternEncoderBuilder b = new PatternEncoderBuilder(name);
b.pattern(DEFAULT_PATTERN);
b.fromProperties(c.properties());
return b.build().provide(name, config);
});
}
return true;
}
@LogConfigurable(name = "PatternEncoderBuilder", prefix = LogProperties.ENCODER_PREFIX + "pattern.")
static LogProvider<LogEncoder> provideEncoder(@KeyParameter String name, String pattern,
@PassThroughParameter @Nullable PatternCompiler patternCompiler,
@ConvertParameter("convertCharset") @Nullable Charset charset, @Nullable Integer maxBufferSize) {
return (n, config) -> {
var compiler = patternCompiler;
if (compiler == null) {
compiler = PatternCompiler.of(b -> {
}).provide(name, config);
}
var formatter = compiler.compile(pattern);
var builder = LogEncoder.builder(formatter);
builder.description(pattern);
if (charset != null) {
builder.charset(charset);
}
if (maxBufferSize != null) {
builder.maxBufferSize(maxBufferSize);
}
return builder.build().provide(n, config);
};
}
static Charset convertCharset(String charset) {
return Charset.forName(charset);
}
@LogConfigurable(name = "PatternConfigBuilder", prefix = PatternConfig.PATTERN_CONFIG_PREFIX)
static PatternConfig provideFormatterConfig(@KeyParameter String name, //
@ConvertParameter("convertZoneId") @Nullable ZoneId zoneId, //
@Nullable String lineSeparator, //
@Nullable Boolean ansiDisabled, //
@PassThroughParameter @Nullable Function<String, @Nullable String> propertyFunction, //
@PassThroughParameter @Nullable Instant startTime, //
@ConvertParameter("convertSequenceNumberStart") @Nullable Long sequenceNumberStart, //
@ConvertParameter("convertAbbreviatorCache") PatternConfig.@Nullable CacheType abbreviatorCache) {
PatternConfig dc = PatternConfig.of();
ansiDisabled = ansiDisabled == null ? dc.ansiDisabled() : ansiDisabled;
lineSeparator = lineSeparator == null ? dc.lineSeparator() : lineSeparator;
zoneId = zoneId == null ? dc.zoneId() : zoneId;
propertyFunction = propertyFunction == null ? StandardPropertyFunction.INSTANCE : propertyFunction;
startTime = startTime == null ? Instant.now() : startTime;
long sequenceNumberStart_ = sequenceNumberStart == null ? dc.sequenceNumberStart() : sequenceNumberStart;
abbreviatorCache = abbreviatorCache == null ? dc.abbreviatorCache() : abbreviatorCache;
return new SimpleFormatterConfig(zoneId, lineSeparator, ansiDisabled, propertyFunction, startTime,
sequenceNumberStart_, abbreviatorCache);
}
static Long convertSequenceNumberStart(String s) {
return Long.valueOf(s);
}
static PatternConfig.CacheType convertAbbreviatorCache(String s) {
return PatternConfig.CacheType.parse(s);
}
static ZoneId convertZoneId(String zoneId) {
return ZoneId.of(zoneId);
}
}
record PatternEncoderProvider() implements LogEncoder.EncoderProvider {
@Override
public LogProvider<LogEncoder> provide(LogProviderRef ref) {
return (name, config) -> {
PatternEncoderBuilder b = new PatternEncoderBuilder(name);
b.fromProperties(config.properties(), ref);
return b.build().provide(name, config);
};
}
}