java-commons/commons-logger/src/main/java/io/gitlab/jfronny/commons/logger/HotswapLoggerFinder.java

158 lines
5.4 KiB
Java

package io.gitlab.jfronny.commons.logger;
import io.gitlab.jfronny.commons.ref.WeakSet;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
/**
* A logger factory that allows apps to swap the backing factory at runtime, automatically updating all created loggers.
*/
public class HotswapLoggerFinder extends LeveledLoggerFinder {
private static final Set<HotswapLoggerFinder> KNOWN_INSTANCES = new WeakSet<>();
private static LeveledLoggerFinder DEFAULT_FACTORY = LeveledLoggerFinder.get(StdoutLogger::fancy);
private static System.Logger.Level DEFAULT_LEVEL = System.Logger.Level.INFO;
/**
* Resets all known instances to use the default logger factory.
*/
public static void resetAllStrategies() {
updateAllStrategies(StdoutLogger::fancy);
}
/**
* Updates all known instances to use the new logger factory.
* @param factory the new logger factory to use for all known instances
*/
public static void updateAllStrategies(System.LoggerFinder factory) {
updateAllStrategies(LeveledLoggerFinder.get(factory));
}
public static void updateAllStrategies(LeveledLoggerFinder.Simple factory) {
updateAllStrategies(LeveledLoggerFinder.get(factory));
}
/**
* Updates all known instances to use the new logger factory.
* @param factory the new logger factory to use for all known instances
*/
public static void updateAllStrategies(LeveledLoggerFinder factory) {
DEFAULT_FACTORY = Objects.requireNonNull(factory);
for (HotswapLoggerFinder f : KNOWN_INSTANCES) {
f.updateStrategy(factory);
}
}
/**
* Updates all known instances to use the new minimum log level if supported.
* @param minimumLevel the new minimum level to use for all known instances
*/
public static void updateAllDefaultLevels(System.Logger.Level minimumLevel) {
DEFAULT_LEVEL = Objects.requireNonNull(minimumLevel);
for (HotswapLoggerFinder f : KNOWN_INSTANCES) {
f.updateDefaultLevel(minimumLevel);
}
}
static {
try {
ServiceLoader.load(LoggerConfiguration.class).forEach(LoggerConfiguration::configure);
} catch (Throwable t) {
System.err.println("Could not initialize LoggerConfiguration");
t.printStackTrace();
}
}
public HotswapLoggerFinder() {
KNOWN_INSTANCES.add(this);
}
private LeveledLoggerFinder defaultFactory = DEFAULT_FACTORY;
private System.Logger.Level minimumLevel = DEFAULT_LEVEL;
private int version = 0;
/**
* Resets the strategy to use the default logger factory.
*/
public void resetStrategy() {
updateStrategy(StdoutLogger::fancy);
}
/**
* Updates the strategy to use the new logger factory.
* @param factory the new logger factory to use
*/
public void updateStrategy(System.LoggerFinder factory) {
if (factory instanceof LeveledLoggerFinder f) {
updateStrategy(f);
} else {
updateStrategy(LeveledLoggerFinder.get(factory));
}
}
public void updateStrategy(LeveledLoggerFinder.Simple factory) {
updateStrategy(LeveledLoggerFinder.get(factory));
}
/**
* Updates the strategy to use the new logger factory.
* @param factory the new logger factory to use
*/
public void updateStrategy(LeveledLoggerFinder factory) {
synchronized (this) {
defaultFactory = Objects.requireNonNull(factory);
version++;
}
}
/**
* Updates the minimum log level to use if supported.
* @param minimumLevel the new minimum level to use if supported
*/
public void updateDefaultLevel(System.Logger.Level minimumLevel) {
synchronized (this) {
this.minimumLevel = Objects.requireNonNull(minimumLevel);
version++;
}
}
@Override
public SystemLoggerPlus getLogger(String name, Module module, @Nullable System.Logger.Level level) {
return new SwappableDelegateLogger(name, module, level);
}
private class SwappableDelegateLogger extends DelegateLogger {
private final String name;
private final Module module;
private final boolean keepLevel;
private Level lastMinimumLevel;
private System.Logger delegate;
int version = -1;
private SwappableDelegateLogger(String name, Module module, @Nullable Level level) {
this.name = name == null ? "null" : name;
this.module = module;
this.keepLevel = level != null;
this.lastMinimumLevel = level == null ? HotswapLoggerFinder.this.minimumLevel : level;
updateStrategy();
}
public void updateStrategy() {
if (this.version == HotswapLoggerFinder.this.version) return;
synchronized (HotswapLoggerFinder.this) {
this.version = HotswapLoggerFinder.this.version;
if (!keepLevel) this.lastMinimumLevel = HotswapLoggerFinder.this.minimumLevel;
this.delegate = HotswapLoggerFinder.this.defaultFactory.getLogger(this.name, module, this.lastMinimumLevel);
}
}
@Override
protected System.Logger getDelegate() {
updateStrategy();
return delegate;
}
}
}