I just do not like Logback's XML or Groovy configuration and would prefer to do the configuration in Java (this is also because I'm going to change the configuration at runtime at various times after initialization).
It seems the only way to do Java configuration of Logback is to have some sort of initialization that hijacks the root appender or to have custom system properties that tell logback to not use the default configuration (which is rather annoying for unit tests and deployment).
What I would much rather prefer is something like a logback.xml
:
<configuration javaConfig="com.myco.LogBackConfig" />
Where LogbackConfig either has some established static method that logback calls or it just re-instantiates a Java bean.
That is I want a default classpath resource that will tell logback to use Java instead of groovy/xml. Basically I want something akin to the Java ServiceLoader for logback config.
Perhaps there is a way to do this in Logback that I am missing?
UPDATE: Apparently Log4j2 (the new log4j) does exactly what I want and is very similar to the Java ServiceLoader.
This feature was just merged into logback's source code. See https://github.com/qos-ch/logback/pull/181
An alternative way.
logback version: 1.1.2
logback.xml
<configuration debug="true">
<contextListener class="com.example.log.CustomLoggerContextListener"/>
</configuration>
CustomLoggerContextListener.java
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
public class CustomLoggerContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
private boolean started = false;
@Override
public void start() {
if (started) return;
LoggerContext lctx = (LoggerContext) getContext();
LoggerConfigurer configurer = new LoggerConfigurer();
configurer.configure(lctx);
started = true;
}
@Override
public void stop() {
}
@Override
public boolean isStarted() {
return started;
}
@Override
public boolean isResetResistant() {
return true;
}
@Override
public void onStart(LoggerContext context) {
}
@Override
public void onReset(LoggerContext context) {
}
@Override
public void onStop(LoggerContext context) {
}
@Override
public void onLevelChange(Logger logger, Level level) {
}
}
LoggerConfigurer.java
import ch.qos.logback.classic.AsyncAppender;
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.filter.ThresholdFilter;
import ch.qos.logback.classic.html.HTMLLayout;
import ch.qos.logback.classic.net.SMTPAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.spi.CyclicBufferTracker;
import ch.qos.logback.core.status.InfoStatus;
import ch.qos.logback.core.status.StatusManager;
import java.io.File;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
public class LoggerConfigurer {
public void configure(LoggerContext loggerContext) {
loggerContext.reset();
StatusManager sm = loggerContext.getStatusManager();
if (sm != null) {
sm.add(new InfoStatus("Setting configuration from " + getClass().getName(), loggerContext));
}
/**
* Root Level
*/
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.INFO);
/**
* Custom Log Levels
*/
loggerContext.getLogger("com.example.package1").setLevel(Level.INFO);
loggerContext.getLogger("com.example.package2").setLevel(Level.DEBUG);
/**
* Console Appender
*/
configureConsoleAppender(loggerContext, rootLogger);
/**
* File Appender
*/
configureFileAppender(loggerContext, rootLogger);
/**
* SMTP Mail Appender
*/
configureMailAppender(loggerContext, rootLogger);
}
protected void configureConsoleAppender(LoggerContext loggerContext, Logger rootLogger) {
ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<ILoggingEvent>();
consoleAppender.setContext(loggerContext);
consoleAppender.setName("myapp-log-console");
consoleAppender.setEncoder(getEncoder(loggerContext));
consoleAppender.start();
rootLogger.addAppender(consoleAppender);
}
protected void configureFileAppender(LoggerContext loggerContext, Logger rootLogger) {
String logFileName = "MY_LOG_FILE";
String maxFileSize = "500mb";
String logPath = "~/myAppLogs";
int maxHistory = 7;
String fileLogLevel = "WARN"; // if needed
logPath = parseLogPath(logPath);
logFileName = parseLogFileName(logFileName);
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<ILoggingEvent>();
rollingFileAppender.setContext(loggerContext);
rollingFileAppender.setName("log-file");
rollingFileAppender.setFile(logPath + File.separator + logFileName + ".log");
rollingFileAppender.setEncoder(getEncoder(loggerContext));
rollingFileAppender.setAppend(true);
TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy();
rollingPolicy.setContext(loggerContext);
rollingPolicy.setParent(rollingFileAppender);
rollingPolicy.setFileNamePattern(logFileName + "-%d{yyyy-MM-dd}.log");
rollingPolicy.setMaxHistory(maxHistory);
rollingPolicy.start();
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
triggeringPolicy.setContext(loggerContext);
triggeringPolicy.setMaxFileSize(maxFileSize);
triggeringPolicy.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.setTriggeringPolicy(triggeringPolicy);
ThresholdFilter fileThresholdFilter = new ThresholdFilter();
fileThresholdFilter.setName("file-thresholdFilter");
fileThresholdFilter.setLevel(fileLogLevel);
fileThresholdFilter.start();
rollingFileAppender.start();
AsyncAppender fileAsyncAppender = new AsyncAppender();
fileAsyncAppender.setContext(loggerContext);
fileAsyncAppender.setName(rollingFileAppender.getName() + "-async");
fileAsyncAppender.addAppender(rollingFileAppender);
fileAsyncAppender.setDiscardingThreshold(0);
fileAsyncAppender.start();
rootLogger.addAppender(fileAsyncAppender);
}
protected void configureMailAppender(LoggerContext loggerContext, Logger rootLogger) {
List<String> toMails = Arrays.asList("[email protected]", "[email protected]");
String host = "mail.host";
Integer port = 22;
String username = "mail.user";
String password = "mail.password";
String from = "mail.from";
String subject = "mail.subject";
Boolean startTLS = true;
String mailLogLevel = "ERROR"; // if needed
HTMLLayout htmlLayout = new HTMLLayout();
htmlLayout.setContext(loggerContext);
htmlLayout.setTitle(subject);
htmlLayout.start();
SMTPAppender smtpAppender = new SMTPAppender();
smtpAppender.setContext(loggerContext);
smtpAppender.setName("log-mail");
smtpAppender.setSMTPHost(host);
smtpAppender.setSMTPPort(port);
smtpAppender.setUsername(username);
smtpAppender.setPassword(password);
smtpAppender.setFrom(from);
smtpAppender.setSTARTTLS(startTLS);
smtpAppender.setSubject(subject);
smtpAppender.setLayout(htmlLayout);
CyclicBufferTracker<ILoggingEvent> cyclicBufferTracker = new CyclicBufferTracker<ILoggingEvent>();
cyclicBufferTracker.setBufferSize(1);
smtpAppender.setCyclicBufferTracker(cyclicBufferTracker);
ThresholdFilter mailThresholdFilter = new ThresholdFilter();
mailThresholdFilter.setName("mail-thresholdFilter");
mailThresholdFilter.setLevel(mailLogLevel);
mailThresholdFilter.start();
smtpAppender.addFilter(mailThresholdFilter);
/**
* to mail addresses
*/
for (String toMail : toMails) {
smtpAppender.addTo(toMail);
}
smtpAppender.start();
AsyncAppender mailAsyncAppender = new AsyncAppender();
mailAsyncAppender.setContext(loggerContext);
mailAsyncAppender.setName(smtpAppender.getName() + "-async");
mailAsyncAppender.addAppender(smtpAppender);
mailAsyncAppender.setDiscardingThreshold(0);
mailAsyncAppender.start();
rootLogger.addAppender(mailAsyncAppender);
}
private PatternLayoutEncoder getEncoder(LoggerContext loggerContext) {
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n");
encoder.setCharset(Charset.forName("UTF-8"));
encoder.start();
return encoder;
}
private String parseLogFileName(String logFileName) {
if (logFileName.startsWith("/") || logFileName.startsWith("\\")) {
logFileName = logFileName.substring(1);
}
return logFileName;
}
private String parseLogPath(String logPath) {
if (logPath.startsWith("~")) {
logPath = logPath.replaceFirst("~", System.getProperty("user.home"));
}
if (logPath.endsWith("/") || logPath.endsWith("\\")) {
logPath = logPath.substring(0, logPath.length() - 1);
}
return logPath;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With