以编程方式配置LogBack附加程序


75

我在logback.xml中定义了一个logback附加程序,它是一个数据库附加程序,但我很好奇是否有任何方法可以使用定义为bean的我自己的连接池在java中配置附加程序。

我发现类似的事情,但没有实际答案。

Answers:


113

这是一个对我有用的简单示例(请注意,在此示例中我使用FileAppender)

import org.slf4j.LoggerFactory;

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.ILoggingEvent;
import ch.qos.logback.core.FileAppender;

public class Loggerutils {

    public static void main(String[] args) {
          Logger foo = createLoggerFor("foo", "foo.log");
          Logger bar = createLoggerFor("bar", "bar.log");
          foo.info("test");
          bar.info("bar");
    }

    private static Logger createLoggerFor(String string, String file) {
          LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
          PatternLayoutEncoder ple = new PatternLayoutEncoder();

          ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
          ple.setContext(lc);
          ple.start();
          FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
          fileAppender.setFile(file);
          fileAppender.setEncoder(ple);
          fileAppender.setContext(lc);
          fileAppender.start();

          Logger logger = (Logger) LoggerFactory.getLogger(string);
          logger.addAppender(fileAppender);
          logger.setLevel(Level.DEBUG);
          logger.setAdditive(false); /* set to true if root should log too */

          return logger;
    }

}

@Andrew Swan:您能否在注释中注明您想对当前版本进行的更改。我将尝试相应地对其进行更新。
雷托

7
Logger不再公开该addAppender()方法。
Mike C

1
@trevor我不记得了,现在Java代码库已无法使用。
Mike C

3
对于那些意识到Logger没有addAppender方法的人,是因为您要指出Loggersl4j中的抽象类。如果Loggerlogback包中指出一种实现,则可以使用addAppender。这样做的缺点是您的测试代码将与logback实现耦合。
TiagoZortéaDe Conto

1
更好地使用lc.getLogger(string)
user8681709 '19

14

您可以通过编程方式配置附加程序。几乎所有的附加程序都使用程序化配置进行了测试。因此,在logback项目源代码中有许多程序化附加程序配置示例。对于logback-core追加器,请查看logback-core/src/test/java,对于logback-classic追加器,请查看logback-classic/src/test/java


10

作为参考,当您尝试修改负责创建记录器的代码时,为了使记录器正常工作,必须满足一系列规则。

这些规则在slf4j / logback的程序化配置文章中非常有帮助:

现在,我对slf4j / logback的编程配置有了经验。

任务

程序必须为每个处理的输入文件打开单独的日志文件。

任务解决方案

无需通过xml配置登录,而是需要“手动”实例化编码器,附加程序和记录器,然后将它们配置并链接在一起。

警告1

尝试在追加程序之间共享编码器(即PatternLayoutEncoder)时,Logback变得疯狂。

警告1的解决方案

为每个追加程序创建单独的编码器。

警告2

如果编码器和附加器未与日志记录上下文关联,则Logback拒绝记录任何内容。

警告2的解决方案

在每个编码器和附加器上调用setContext,并将LoggerFactory作为参数传递。

警告3

如果未启动编码器和附加程序,则Logback拒绝记录任何内容。

警告3的解决方案

编码器和附加器需要以正确的顺序启动,即首先编码器,然后是附加器。

警告4

当RollingPolicy对象未附加到附加程序时,它们会生成奇怪的错误消息,例如“无法识别日期格式”,例如RollingPolicy对象(即TimeBasedRollingPolicy)。

警告4的解决方案

在RollingPolicy上调用setContext与在编码器和追加器上调用setContext相同。

这是“手动”登录配置的工作示例:

package testpackage

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.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy

import org.slf4j.LoggerFactory

class TestLogConfig {

  public static void main(String[] args) {
    LoggerContext logCtx = LoggerFactory.getILoggerFactory();

    PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    ConsoleAppender logConsoleAppender = new ConsoleAppender();
    logConsoleAppender.setContext(logCtx);
    logConsoleAppender.setName("console");
    logConsoleAppender.setEncoder(logEncoder);
    logConsoleAppender.start();

    logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    RollingFileAppender logFileAppender = new RollingFileAppender();
    logFileAppender.setContext(logCtx);
    logFileAppender.setName("logFile");
    logFileAppender.setEncoder(logEncoder);
    logFileAppender.setAppend(true);
    logFileAppender.setFile("logs/logfile.log");

    TimeBasedRollingPolicy logFilePolicy = new TimeBasedRollingPolicy();
    logFilePolicy.setContext(logCtx);
    logFilePolicy.setParent(logFileAppender);
    logFilePolicy.setFileNamePattern("logs/logfile-%d{yyyy-MM-dd_HH}.log");
    logFilePolicy.setMaxHistory(7);
    logFilePolicy.start();

    logFileAppender.setRollingPolicy(logFilePolicy);
    logFileAppender.start();

    Logger log = logCtx.getLogger("Main");
    log.setAdditive(false);
    log.setLevel(Level.INFO);
    log.addAppender(logConsoleAppender);
    log.addAppender(logFileAppender);
  }
}

1
由于某种原因,我得到了java.lang.ClassCastException: org.apache.logging.slf4j.Log4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
Sridhar Sarnobat '18

看来您使用的是Log4j的记录器工厂,而不是slf4j
xxSwordy,

"Main"名称似乎无效。Logger.ROOT_LOGGER_NAME"ROOT"
Miha_x64 '18 / 12/29

@ Miha_x64:任何字符串均有效。传递“ ROOT”将意味着配置记录器的根实例。如果要配置更多从根记录器继承的记录器,该怎么办?您创建子记录器(例如“ Main”,“ Main2”,“ Auxiliary”,“ whateverNameYouWant”),所有这些子记录器都可以具有自定义配置。参见Javadoc:slf4j.org/api/org/slf4j/…–
dominik

6

只是,如果有人要寻找程序化配置的具体示例。

在这里,我设置了ConsoleAppender的字符集:

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ConsoleAppender<ILoggingEvent> appender =
    (ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT");
LayoutWrappingEncoder<ILoggingEvent> enc = 
    (LayoutWrappingEncoder<ILoggingEvent>) appender.getEncoder();
enc.setCharset(Charset.forName("utf-8"));

而我的logback.xml:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <charset>866</charset>
        <pattern>[%level] %msg%n</pattern>
    </encoder>
</appender>

<logger name="appconsole">
    <appender-ref ref="STDOUT" />
</logger>

为什么我需要以编程方式配置记录器?因为,我将我的应用程序(Spring Boot)打包到一个jar文件中。因此,Logback.xml文件似乎隐藏在jar中。但是,解包和更改它并不方便。而且我的app.jar旁边不需要任何logback.xml文件。我只有app.yaml文件,其中包含应用程序的所有配置属性。


0

不允许发表评论(还好吗?),我只想补充三个提示。

  • 关于上述注意事项,如果您有任何问题,只需致电即可

    StatusPrinter.print(context);
    

    一切都已经配置后,也就是在已经加入你的追加程序的根/“主”附加器:它告诉你什么是错的。

  • 我非常喜欢将日志记录级别分隔在不同的文件中。当查找错误时,我首先查找错误文件,依此类推,将它们设置为

tot_[app name].log   : Level.INFO
deb_[app name].log   : Level.DEBUG
err_[app name].log   : Level.ERROR

通过简单的私有过滤器类(例如

    private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {

        private final Level level;

        private ThresholdLoggerFilter(Level level){
            this.level = level;
        }

        @Override
        public FilterReply decide(ILoggingEvent event) {
            if (event.getLevel().isGreaterOrEqual(level)) {
                return FilterReply.NEUTRAL;
            } else {
                return FilterReply.DENY;
            }
        }
    }

然后只需致电myFilter.start()myAppender.addFilter(myFilter);

  • 最后,将它们放在一起,我通常希望能够通过设置程序实现一些简单的界面来动态更改日志级别。

    public interface LoggingService {
        void setRootLogLevel(Level level);
    }
    

将根日志记录级别保留在受监视的某些属性文件中,以便每当那里存在一些有效输入时,我只需调用此服务即可实现

    @Override
    public void setRootLogLevel(Level level) {
        if (context != null && context.isStarted()) {
        ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
        }
    }

与我的新的根记录器级别。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.