com.dua3.utility.logging.slf4j.LoggerFactorySlf4j Maven / Gradle / Ivy
package com.dua3.utility.logging.slf4j;
import com.dua3.utility.data.Pair;
import com.dua3.utility.lang.LangUtil;
import com.dua3.utility.logging.LogEntryHandler;
import com.dua3.utility.logging.ConsoleHandler;
import com.dua3.utility.logging.LogEntryDispatcher;
import org.slf4j.ILoggerFactory;
import org.slf4j.event.Level;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
/**
* The LoggerFactorySlf4j class is an implementation of the ILoggerFactory and LogEntryDispatcher interfaces.
*/
public class LoggerFactorySlf4j implements ILoggerFactory, LogEntryDispatcher {
public static final String LEVEL = "logger.level";
public static final String LOGGER_CONSOLE_STREAM = "logger.console.stream";
public static final String LOGGER_CONSOLE_COLORED = "logger.console.colored";
private final List> prefixes = new ArrayList<>();
private final List> handlers = new ArrayList<>();
private LogEntryHandler defaultHandler;
public LoggerFactorySlf4j() {
Properties properties = getProperties();
// parse log level entry
String leveldeclaration = properties.getProperty(LEVEL, Level.INFO.name());
String[] decls = leveldeclaration.split(",");
if (decls.length > 0) {
LoggerSlf4j.setDefaultLevel(Level.valueOf(decls[0].strip()));
}
Arrays.stream(decls)
.skip(1) // global level has already been set
.forEachOrdered(s -> {
String[] parts = s.split(":");
LangUtil.check(parts.length == 2, "invalid log level declaration: %s", s);
String prefix = parts[0].strip();
Level level = Level.valueOf(parts[1].strip());
var entry = getPrefixEntry(prefix);
LangUtil.check(entry.isEmpty(), () -> new IllegalStateException("prefix '%s' is shadowed by '%s'".formatted(prefix, entry.orElseThrow().first())));
prefixes.add(Pair.of(prefix, level));
});
// configure console handler
String propertyConsoleStream = properties.getProperty(LOGGER_CONSOLE_STREAM, "").trim().toLowerCase(Locale.ROOT);
final PrintStream stream = switch (propertyConsoleStream) {
case "" -> null;
case "system.err" -> System.err;
case "system.out" -> System.out;
default ->
throw new IllegalArgumentException("invalid value for property " + LOGGER_CONSOLE_STREAM + ": '" + propertyConsoleStream + "'");
};
String propertyConsoleColored = properties.getProperty(LOGGER_CONSOLE_COLORED, "auto").trim().toLowerCase(Locale.ROOT);
final boolean colored = switch (propertyConsoleColored) {
case "true" -> true;
case "false" -> false;
// "auto" enables colored output when a terminal is attached and the TERM environment variable is set
case "auto" -> System.console() != null && System.getenv().get("TERM") != null;
default ->
throw new IllegalArgumentException("invalid value for property " + LOGGER_CONSOLE_COLORED + ": '" + propertyConsoleColored + "'");
};
this.defaultHandler = stream != null ? new ConsoleHandler(stream, colored) : null;
if (defaultHandler != null) {
handlers.add(new WeakReference<>(defaultHandler));
}
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private static Properties getProperties() {
Properties properties = new Properties();
try (InputStream in = ClassLoader.getSystemResourceAsStream("logging.properties")) {
if (in == null) {
properties.setProperty(LOGGER_CONSOLE_STREAM, "system.out");
} else {
properties.load(in);
}
} catch (IOException e) {
properties.setProperty(LOGGER_CONSOLE_STREAM, "system.out");
e.printStackTrace(System.err);
}
return properties;
}
private Optional> getPrefixEntry(String name) {
return prefixes.stream().filter(p -> name.startsWith(p.first())).findFirst();
}
private Level getLevel(String name) {
return getPrefixEntry(name).map(Pair::second).orElseGet(LoggerSlf4j::getDefaultLevel);
}
@Override
public org.slf4j.Logger getLogger(String name) {
LoggerSlf4j logger = new LoggerSlf4j(name, handlers);
logger.setLevel(getLevel(name));
return logger;
}
@Override
public void addLogEntryHandler(LogEntryHandler handler) {
handlers.add(new WeakReference<>(handler));
}
@Override
public void removeLogEntryHandler(LogEntryHandler handler) {
handlers.removeIf(h -> h.get() == handler);
}
@Override
public Collection getLogEntryHandlers() {
return handlers.stream().map(WeakReference::get).filter(Objects::nonNull).toList();
}
public LogEntryHandler getDefaultHandler() {
return defaultHandler;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy