Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure logback to defer to Java configuration aka Plain Java Configuration of Logback

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.

like image 240
Adam Gent Avatar asked Mar 11 '14 20:03

Adam Gent


2 Answers

This feature was just merged into logback's source code. See https://github.com/qos-ch/logback/pull/181

like image 173
Ceki Avatar answered Sep 22 '22 14:09

Ceki


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;
    }
}
like image 22
bhdrk Avatar answered Sep 21 '22 14:09

bhdrk