我在 logback.xml 中定义了一个 logback appender,它是一个 DB appender,但我很好奇是否有任何方法可以使用我自己定义为 bean 的连接池在 java 中配置 appender。
我发现类似的东西,但从来没有真正的答案。
这是一个对我有用的简单示例(请注意,我在此示例中使用了 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;
}
}
您可以以编程方式配置附加程序。几乎所有附加程序都使用编程配置进行测试。由此可见logback项目源码中有很多程序化appender配置的例子。对于 logback-core appender,请查看logback-core/src/test/java
,对于 logback-classic appender,请查看logback-classic/src/test/java
.
作为参考,当您尝试修改负责创建记录器的代码时,必须满足一系列规则才能使记录器工作。
这些规则在一篇非常有用的文章Programmatic configuration of slf4j/logback中进行了描述:
现在我有了 slf4j/logback 编程配置的经验。
任务
程序必须为每个已处理的输入文件打开单独的日志文件。
任务解决方案
不是通过 xml 配置 logback,而是需要“手动”实例化编码器、附加程序和记录器,然后配置并将它们链接在一起。
警告 1
尝试在 appender 之间共享编码器(即 PatternLayoutEncoder)时,Logback 变得疯狂。
警告 1 的解决方案
为每个 appender 创建单独的编码器。
警告 2
如果编码器和附加程序未与日志记录上下文关联,则 Logback 拒绝记录任何内容。
警告 2 的解决方案
在每个编码器和附加器上调用 setContext,将 LoggerFactory 作为参数传递。
警告 3
如果未启动编码器和附加程序,Logback 拒绝记录任何内容。
警告 3 的解决方案
编码器和附加器需要以正确的顺序启动,即首先编码器,然后附加器。
警告 4
当 RollingPolicy 对象(即 TimeBasedRollingPolicy)未附加到与 appender 相同的上下文时,它们会产生奇怪的错误消息,例如“无法识别日期格式”。
警告 4 的解决方案
在 RollingPolicy 上调用 setContext 的方式与在编码器和附加器上相同。
这是“手动” logback 配置的工作示例:
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);
}
}
只是,如果有人会寻找程序配置的具体示例。
这里我设置了 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 文件,其中包含应用程序的所有配置属性。
不允许评论(还没有?),我只想添加三个提示;
关于上面的警告,如果你有问题,只需添加一个电话
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()
and 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);
}
}
使用我的新根记录器级别。
这里要注意的是,我们需要将其类型转换为 ch.qos.logback.classic.Logger,然后可以通过编程方式设置附加程序。
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
Logger log = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
log.addAppender(mockAppender);