以编程方式配置 Log4j 记录器

Posted

技术标签:

【中文标题】以编程方式配置 Log4j 记录器【英文标题】:Configuring Log4j Loggers Programmatically 【发布时间】:2012-02-16 11:12:12 【问题描述】:

我是第一次尝试使用 SLF4J(带有log4j 绑定)。

我想配置 3 个不同的命名记录器,它们可以由 LoggerFactory 返回,它们将记录不同的级别并将消息推送到不同的附加程序:

Lo​​gger 1 "FileLogger" 记录 DEBUG 并附加到 DailyRollingFileAppender Lo​​gger 2 "TracingLogger" 记录 TRACE+ 并附加到 JmsAppender Lo​​gger 3 "ErrorLogger" 记录 ERROR+ 并附加到不同的 JmsAppender

此外,我希望它们以编程方式配置(在 Java 中,而不是在 XML 或 log4j.properties 文件中)。

我想,通常情况下,我会在一些引导代码中的某处定义这些 Loggers,例如 init() 方法。然而,因为我想使用slf4j-log4j,我很困惑我可以在哪里定义记录器并使它们可用于类路径。

我不相信这违反了 SLF4J 的基本目的(作为外观),因为我使用 SLF4J API 的代码永远不会知道这些记录器的存在。我的代码只是对 SLF4J API 进行正常调用,然后将它们转发到它在类路径上找到的 log4j 记录器。

但是如何在类路径上配置那些 log4j 记录器...在 Java 中?!

【问题讨论】:

***.com/questions/1666121/… 对于 log4j 1.x,请使用下面接受的 2.x 答案,请参阅logging.apache.org/log4j/2.x/manual/customconfig.html 【参考方案1】:

您可以通过编程方式向 Log4j 添加/删除 Appender:

  ConsoleAppender console = new ConsoleAppender(); //create appender
  //configure the appender
  String PATTERN = "%d [%p|%c|%C1] %m%n";
  console.setLayout(new PatternLayout(PATTERN)); 
  console.setThreshold(Level.FATAL);
  console.activateOptions();
  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(console);

  FileAppender fa = new FileAppender();
  fa.setName("FileLogger");
  fa.setFile("mylog.log");
  fa.setLayout(new PatternLayout("%d %-5p [%c1] %m%n"));
  fa.setThreshold(Level.DEBUG);
  fa.setAppend(true);
  fa.activateOptions();

  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(fa);
  //repeat with all other desired appenders

我建议您将它放入 init() 某个地方,您可以确定它会在其他任何事情之前执行。 然后,您可以使用

删除根记录器上的所有现有附加程序
 Logger.getRootLogger().getLoggerRepository().resetConfiguration();

并从添加您自己的开始。您当然需要在类路径中使用 log4j 才能使其正常工作。

备注: 您可以使用任何您喜欢的Logger.getLogger(...) 添加附加程序。我只使用了根记录器,因为它位于所有事物的底部,并且将处理通过其他类别中的其他附加程序传递的所有内容(除非通过设置可加性标志进行其他配置)。

如果您需要了解日志记录的工作原理以及如何确定将日志写入的位置read this manual,以获取更多信息。 简而言之:

  Logger fizz = LoggerFactory.getLogger("com.fizz")

将为您提供“com.fizz”类别的记录器。 对于上面的示例,这意味着使用它记录的所有内容都将被引用到根记录器上的控制台和文件附加程序。 如果您将附加程序添加到 Logger.getLogger("com.fizz").addAppender(newAppender) 然后来自fizz 的日志记录将由来自根记录器和newAppender 的所有附加程序处理。 您无需使用配置创建 Logger,您只需为系统中所有可能的类别提供处理程序。

【讨论】:

谢谢你!快速提问 - 我注意到您正在将附加程序添加到根 Logger。这是有原因的吗? 而且,更重要的是,我需要指定从 SLF4J 的 LoggerFactory 中检索哪个 Logger。是否可以向 SLF4J 索要 log4j 的 root logger? @AdamTannon 您可以使用任何您喜欢的 Logger.getLogger(...)。我只使用了根记录器,因为它位于所有事物的底部,并且将处理通过其他类别中的其他附加程序传递的所有内容(除非另有配置)。 See logger hierarchy @AdamTannon 您不能使用 sl4j 工厂来获取 log4j 根记录器。 SL4j 是一个日志外观。你不会从中得到任何 log4j 特定的东西。 oers - 感谢您的精彩反馈,但我只是没有在这里连接所有的点。您能否修改您的示例以显示添加新的 Logger(不是根 logger),一旦添加到系统中,任何其他需要它的类都可以使用它?例如,通常由 Logger fizz = LoggerFactory.getLogger("com.fizz"); 访问的 Logger,谢谢!【参考方案2】:

听起来您正在尝试从“两端”(消费者端和配置端)使用 log4j。

如果您想针对 slf4j api 进行编码,但提前(并以编程方式)确定类路径将返回的 log4j 记录器的配置,那么您绝对必须进行某种日志调整它利用了惰性构造。

public class YourLoggingWrapper 
    private static boolean loggingIsInitialized = false;

    public YourLoggingWrapper() 
        // ...blah
    

    public static void debug(String debugMsg) 
        log(LogLevel.Debug, debugMsg);
    

    // Same for all other log levels your want to handle.
    // You mentioned TRACE and ERROR.

    private static void log(LogLevel level, String logMsg) 
        if(!loggingIsInitialized)
            initLogging();

        org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("DebugLogger");

        switch(level) 
        case: Debug:
            logger.debug(logMsg);
            break;
        default:
            // whatever
        
    

    // log4j logging is lazily constructed; it gets initialized
    // the first time the invoking app calls a log method
    private static void initLogging() 
        loggingIsInitialized = true;

        org.apache.log4j.Logger debugLogger = org.apache.log4j.LoggerFactory.getLogger("DebugLogger");

        // Now all the same configuration code that @oers suggested applies...
        // configure the logger, configure and add its appenders, etc.
        debugLogger.addAppender(someConfiguredFileAppender);
    

使用这种方法,您无需担心配置 log4j 记录器的位置/时间。类路径第一次请求它们时,它们被懒惰地构造、传回并通过 slf4j 提供。希望这会有所帮助!

【讨论】:

成功了!非常感谢您提供有用的示例! @Oers - 感谢您试图将我引向正确的方向 - 我将为您的奉献精神提供绿色支票,但必须给 zharvey 赏金,因为这正是我想要的。再次感谢大家!【参考方案3】:

如果您在 log4j 属性中定义了一个 appender,并希望以编程方式对其进行更新,请在 log4j 属性中设置名称并按名称获取。

这是一个示例 log4j.properties 条目:

log4j.appender.stdout.Name=console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.Threshold=INFO

要更新它,请执行以下操作:

((ConsoleAppender) Logger.getRootLogger().getAppender("console")).setThreshold(Level.DEBUG);

【讨论】:

【参考方案4】:

如果有人想在 Java 中以编程方式配置 log4j2,那么此链接可能会有所帮助:(https://www.studytonight.com/post/log4j2-programmatic-configuration-in-java-class)

下面是配置 Console Appender 的基本代码:

ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();

builder.setStatusLevel(Level.DEBUG);
// naming the logger configuration
builder.setConfigurationName("DefaultLogger");

// create a console appender
AppenderComponentBuilder appenderBuilder = builder.newAppender("Console", "CONSOLE")
                .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
// add a layout like pattern, json etc
appenderBuilder.add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d %p %c [%t] %m%n"));
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG);
rootLogger.add(builder.newAppenderRef("Console"));

builder.add(appenderBuilder);
builder.add(rootLogger);
Configurator.reconfigure(builder.build());

这将重新配置默认的 rootLogger 并且还将创建一个新的 appender

【讨论】:

以上是关于以编程方式配置 Log4j 记录器的主要内容,如果未能解决你的问题,请参考以下文章

如何配置 log4j 以忽略层次结构记录器

记录下log4j的两种配置方式

Java学习-007-Log4J 日志记录配置文件详解及实例源代码

以编程方式配置 log4net,但额外的日志记录到控制台

log4j日志配置

Log4j.properties配置详解