Logback - 编程设置日志文件名

39

我正在使用logback,并且尝试在我的Java程序中以编程方式设置日志文件名(类似于以编程方式设置Logback Appender路径),我尝试根据以下方式进行调整:

在logback-test.xml中:

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>log/${log_file_name}.log</file>
  ...

然后在我的Java程序中:

String logFileName = "" + System.currentTimeMillis(); // just for example
System.setProperty("log_file_name", logFileName);

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ContextInitializer ci = new ContextInitializer(lc);
lc.reset();
try
{
    // I prefer autoConfig() over JoranConfigurator.doConfigure() so I
    // wouldn't need to find the file myself.
    ci.autoConfig();
}
catch (JoranException e)
{
    // StatusPrinter will try to log this
    e.printStackTrace();
}
StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

然而结果是两个日志文件,一个是我想要的完整的,例如“1319041145343.log”,另一个是空的,命名为“log_file_name_IS_UNDEFINED.log”。我该如何阻止创建另一个空的日志文件?

1
你的代码唯一的问题似乎是你设置 System.setProperty("log_file_name", logFileName); 太晚了。在 Logback 自动配置执行之前执行它,你就可以得到想要的结果了。 - Robert
它可以比你的代码片段更容易地完成:https://dev59.com/bGEh5IYBdhLWcg3w6HAG#21886071 - Evgeniy Berezovsky
5个回答

40

我相信以下内容更接近于您所需的。

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;

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

    FileAppender fileAppender = new FileAppender();
    fileAppender.setContext(loggerContext);
    fileAppender.setName("timestamp");
    // set the file name
    fileAppender.setFile("log/" + System.currentTimeMillis()+".log");

    PatternLayoutEncoder encoder = new PatternLayoutEncoder();
    encoder.setContext(loggerContext);
    encoder.setPattern("%r %thread %level - %msg%n");
    encoder.start();

    fileAppender.setEncoder(encoder);
    fileAppender.start();

    // attach the rolling file appender to the logger of your choice
    Logger logbackLogger = loggerContext.getLogger("Main");
    logbackLogger.addAppender(fileAppender);

    // OPTIONAL: print logback internal status messages
    StatusPrinter.print(loggerContext);

    // log something
    logbackLogger.debug("hello");
  }
}

如果你只需要在日志文件名中添加时间戳,logback已经支持时间戳元素。因此,实际上你根本不需要任何自定义代码。


谢谢你的帮助,我会尝试一下。(我只是举了一个时间戳的例子,实际的文件名会不同) - conorsomahony
谢谢,这个可行 - 在logback-test.xml中我不再指定文件附加器,在Java代码中我创建了一个并将其附加到“root”记录器。 - conorsomahony
4
SLF4J 的目的不就是为了避免导入 ch.qos.logback.classic.Logger 吗? - Zombies
2
@Zombies:是的,没错。然而,正如其名称所示,slf4j 应该是“简单”的,因此 slf4j API 不提供对日志输出的任何控制。如果您想要这种控制权,您必须直接与您使用的日志框架进行交互(并依赖于它)。 - sleske
我注意到这只改变了当前类的日志文件,如果我想要改变整个应用程序的日志文件,是否有类似的方法?因为将此代码片段添加到所有文件中是一项漫长的任务,并且日志文件似乎没有遵循正常的执行时间顺序。 - user3091996
1
要使此更改应用于所有记录器,请在ROOT记录器上设置它。因此,不要使用loggerContext.getLogger("Main");,而是使用loggerContext.getLogger("ROOT"); - CryptoFool

17

如果您希望根据运行时属性将日志消息分离/筛选到不同的文件中,则可以使用ch.qos.logback.classic.sift.SiftingAppender

简而言之,这允许您设置一个FileAppender(或任何其他appender),其中<file>${userid}.log</file>通过MDC(Mapped Diagnostic Context)进行替换(例如,MDC.put("userid", "Alice");)。请参见第一个链接以获取完整示例。


13

以下是您可以忽略那些额外文件创建的配置文件。以下是配置文件:

<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- "application-name" is a variable -->
<File>c:/logs/${application-name}.log</File>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d %p %t %c - %m%n</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="FILE"/>
</root>
</configuration>  

这里是Java部分,
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
context.reset(); // override default configuration
// inject the name of the current application as "application-name"
// property of the LoggerContext
context.putProperty("application-name", NAME_OF_CURRENT_APPLICATION);
jc.doConfigure("/path/to/the/above/configuration/file.xml");

我从这里得到了这个http://logback.qos.ch/faq.html#sharedConfiguration


另一种方法是在调用Logger之前初始化所有系统变量。这也可以有效运行。 - Sneha Mohan
1
最终我使用了System.setProperty(..)而不是重置上下文。在我看来,这样更加清晰。感谢您的示例。 - Steven Levine
不幸的是,当您想共享配置时,使用系统属性无法起作用。 - bric3

6

看起来记录器被初始化了两次。第一次可能是在应用程序加载时,它无法解析${log_file_name}。如果您使用-Dlog_file_name=*something*启动应用程序,则可以验证此行为是否创建具有名称*something*的另一个日志文件。


你可以通过编程的方式设置文件名,具体方法是使用所需配置(名称、类别、包等)实例化一个文件附加器,并将其添加到附加器层次结构中。 - srkavin
谢谢,我将-Dlog_file_name=verify添加到VM参数中,确实生成了一个名为“verify.log”的空日志 - 有没有办法阻止记录器启动两次? - conorsomahony

1

这里已经有一些有用的答案了,似乎OP也很满意,这很好。但我在寻找最初提出的更精确的问题的答案,我认为这个问题是“如何以编程方式设置现有文件记录器的文件名?”。由于某些原因,这就是我不得不弄清楚如何做到这一点。

我没有太多麻烦地找到了答案。如果有人寻找与我寻找的相同答案,请看这里,以下是如何以编程方式设置现有FileAppender的日志文件名:

String logFileName = "" + System.currentTimeMillis(); // just for example

LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logbackLogger = (ch.qos.logback.classic.Logger) loggerContext.getLogger("ROOT");
FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>)logbackLogger.getAppender("FILE");
fileAppender.setFile(logFileName);

请注意,此代码在“ROOT”记录器中查找附加器,该记录器是所有记录器的父记录器。通常情况下,您会在这里找到现有的附加器,并通过更改此记录器来更改系统中所有日志记录的行为,这通常是您想要的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接