LogEncoderRegistry.java
package io.jstach.rainbowgum;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import io.jstach.rainbowgum.LogEncoder.EncoderProvider;
import io.jstach.rainbowgum.LogOutput.OutputType;
import io.jstach.rainbowgum.format.TTLL;
import io.jstach.rainbowgum.format.Json5FormatterBuilder;
import io.jstach.rainbowgum.format.LogfmtFormatterBuilder;
import io.jstach.rainbowgum.format.TTLLFormatterBuilder;
/**
* Encoder registry. Only one encoder is registered by default which has the URI schema of
* {@value TTLL#SCHEMA} and is used if no encoder is found for the {@link OutputType} of
* the resolved output.
*/
public sealed interface LogEncoderRegistry extends EncoderProvider {
/**
* Registers an encoder by uri scheme.
* @param scheme encoder name.
* @param encoder loaded encoder
*/
public void register(String scheme, EncoderProvider encoder);
/**
* Associates a default formatter with a specific output type.
* @param outputType output type to use for finding best default formatter.
* @return formatter for output type.
*/
public LogProvider<? extends LogEncoder> encoderForOutputType(OutputType outputType);
/**
* Sets a default formatter for a specific output type.
* @param outputType output type.
* @param formatter formatter.
*/
public void setEncoderForOutputType(OutputType outputType, LogProvider<? extends LogEncoder> formatter);
}
final class DefaultEncoderRegistry implements LogEncoderRegistry {
/*
* The logfmt encoder's URI scheme.
*/
static final String LOGFMT_SCHEME = "logfmt";
/*
* The core JSON5 encoder's URI scheme.
*/
static final String JSON5_SCHEME = "json5";
private final Map<String, EncoderProvider> providers = new ConcurrentHashMap<>();
/**
* Creates encoder registry
* @return encoder registry
*/
public static LogEncoderRegistry of() {
var registry = new DefaultEncoderRegistry();
registry.register(TTLL.SCHEMA,
ref -> (name, config) -> LogEncoder
.of(ttll(name, config).fromProperties(config.properties(), ref).build())
.provide(name, config));
registry.register(LOGFMT_SCHEME,
ref -> (name, config) -> LogEncoder
.of(logfmt(name, config).fromProperties(config.properties(), ref).build())
.provide(name, config));
registry.register(JSON5_SCHEME,
ref -> (name, config) -> LogEncoder
.of(json5(name, config).fromProperties(config.properties(), ref).build())
.provide(name, config));
return registry;
}
@Override
public LogProvider<LogEncoder> provide(LogProviderRef ref) {
var _ref = DefaultLogProviderRef.normalize(ref);
var uri = _ref.uri();
/*
* TODO file bug with checker as it should have found scheme to be null.
*/
String scheme = uri.getScheme();
if (scheme == null) {
throw new IllegalStateException("bug. uri was not normalized");
}
var provider = providers.get(scheme);
if (provider == null) {
throw LogProviderRef.NotFoundException.of(ProviderModule.ComponentType.ENCODER, scheme, uri,
providers.keySet());
}
return provider.provide(_ref);
}
@Override
public void register(String scheme, EncoderProvider encoder) {
/*
* TODO maybe check if one is already registered and do something?
*/
providers.put(scheme, encoder);
}
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final EnumMap<OutputType, LogProvider<? extends LogEncoder>> formatters = new EnumMap<>(OutputType.class);
/*
* Builders for the coloring formatters that honor the global ANSI disable property:
* when it is set color defaults to off, while an explicit color property still wins.
*/
static TTLLFormatterBuilder ttll(String name, LogConfig config) {
var b = new TTLLFormatterBuilder(name).serviceRegistry(config.serviceRegistry());
if (ansiDisabled(config)) {
b.color(TTLL.ColorMode.OFF);
}
return b;
}
static LogfmtFormatterBuilder logfmt(String name, LogConfig config) {
var b = new LogfmtFormatterBuilder(name).serviceRegistry(config.serviceRegistry());
if (ansiDisabled(config)) {
b.color(TTLL.ColorMode.OFF);
}
return b;
}
static Json5FormatterBuilder json5(String name, LogConfig config) {
var b = new Json5FormatterBuilder(name).serviceRegistry(config.serviceRegistry());
if (ansiDisabled(config)) {
b.color(TTLL.ColorMode.OFF);
}
return b;
}
private static boolean ansiDisabled(LogConfig config) {
return config.properties()
.forKey(LogProperties.GLOBAL_ANSI_DISABLE_PROPERTY)
.ofBoolean()
.or(false)
.validateNow(LogEncoderRegistry.class);
}
/**
* Associates a default formatter with a specific output type
* @param outputType output type to use for finding best default formatter.
* @return encoder for output type.
*/
@Override
public LogProvider<? extends LogEncoder> encoderForOutputType(OutputType outputType) {
lock.readLock().lock();
try {
var formatter = formatters.get(outputType);
if (formatter == null) {
if (outputType == OutputType.CONSOLE_OUT || outputType == OutputType.CONSOLE_ERR) {
return LogEncoder.ofTTLL();
}
/*
* Never color a file or other non console output unless asked: ANSI
* detection only knows about the console.
*/
return (name, config) -> LogEncoder
.of(ttll(name, config).color(TTLL.ColorMode.OFF).fromProperties(config.properties()).build())
.provide(name, config);
}
return Objects.requireNonNull(formatter);
}
finally {
lock.readLock().unlock();
}
}
/**
* Sets a default formatter for a specific output type.
* @param outputType output type.
* @param formatter formatter.
*/
@Override
public void setEncoderForOutputType(OutputType outputType, LogProvider<? extends LogEncoder> formatter) {
lock.writeLock().lock();
try {
formatters.put(outputType, formatter);
}
finally {
lock.writeLock().unlock();
}
}
}