package org.example;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.joran.util.PropertySetter;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.util.DynamicClassLoadingException;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.IncompatibleClassException;
import ch.qos.logback.core.util.OptionHelper;
import java.io.File;
import java.nio.file.Path;
/**
* Configures logging. We do it this way because reading the XML files is really
* slow (over half a second on startup).
*
* @author thomas
*/
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
@Override
public void configure(LoggerContext context) {
Level level;
Appender<ILoggingEvent> appender;
if (Main.isDevelopmentEnvironment()) {
level = Level.INFO;
appender = devAppender(context);
} else {
level = Level.INFO;
var logDir = Main.appDirectory().resolve("logs");
appender = prodAppender(context, logDir);
}
var root = context.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(level);
root.addAppender(appender);
}
private static Appender<ILoggingEvent> devAppender(LoggerContext context) {
var console = new ConsoleAppender<ILoggingEvent>();
console.setContext(context);
console.setName("STDOUT");
console.setEncoder(encoder(context));
console.start();
return console;
}
private static Appender<ILoggingEvent> prodAppender(LoggerContext context, Path logDir) {
try {
var cacheClassName = "ch.qos.logback.core.joran.util.beans.BeanDescriptionCache";
var cache = OptionHelper.instantiateByClassNameAndParameter(cacheClassName, Object.class, context, Context.class, context);
var setterParams = new Class<?>[]{cache.getClass(), Object.class};
var setterConstructor = PropertySetter.class.getConstructor(setterParams);
var rollingClassName = "ch.qos.logback.core.rolling.RollingFileAppender";
@SuppressWarnings("unchecked")
var rolling = (FileAppender<ILoggingEvent>) OptionHelper.instantiateByClassName(rollingClassName, FileAppender.class, context);
var rollingSetter = setterConstructor.newInstance(cache, rolling);
rollingSetter.setContext(context);
rolling.setContext(context);
rolling.setName("ROLLING");
var policyClassName = "ch.qos.logback.core.rolling.TimeBasedRollingPolicy";
var policy = (ContextAware & LifeCycle) OptionHelper.instantiateByClassName(policyClassName, ContextAware.class, context);
var policySetter = setterConstructor.newInstance(cache, policy);
policySetter.setContext(context);
policy.setContext(context);
policySetter.setComplexProperty("parent", rolling);
var pattern = "%d{yyyy-MM-dd}.log.zip";
policySetter.setComplexProperty("fileNamePattern", logDir.toString() + File.separatorChar + pattern);
policySetter.setProperty("maxHistory", "3");
policySetter.setComplexProperty("totalSizeCap", FileSize.valueOf("10MB"));
policySetter.setProperty("cleanHistoryOnStart", "true");
policy.start();
rollingSetter.setComplexProperty("rollingPolicy", policy);
rolling.setEncoder(encoder(context));
rolling.start();
return rolling;
} catch (IncompatibleClassException | DynamicClassLoadingException | ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
private static PatternLayoutEncoder encoder(LoggerContext context) {
var encoder = new PatternLayoutEncoder();
encoder.setContext(context);
encoder.setPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%thread] %-5level %logger{36} - %msg%n");
encoder.start();
return encoder;
}
}