动态改变 log4j 日志级别

Posted

技术标签:

【中文标题】动态改变 log4j 日志级别【英文标题】:Dynamically Changing log4j log level 【发布时间】:2011-06-03 16:16:28 【问题描述】:

动态更改 log4j 日志级别的不同方法是什么,这样我就不必重新部署应用程序了。在这些情况下,这些变化会是永久性的吗?

【问题讨论】:

***.com/questions/1200449/… 非常类似于***.com/questions/2115900/… 更新了 log4j2 的问题:***.com/questions/23434252/… 请注意(几乎)此页面上的所有内容都是关于 log4j,而不是 log4j2。整个页面充满了混乱和误导,以至于毫无用处。按照@slaadvak 的建议转到***.com/questions/23434252/…。 【参考方案1】:

更改日志级别很简单;修改配置的其他部分将构成更深入的方法。

LogManager.getRootLogger().setLevel(Level.DEBUG);

这些更改在Logger 的生命周期中是永久性的。重新初始化时,配置将被读取并使用,因为在运行时设置级别不会持久更改级别。

更新:如果您使用的是 Log4j 2,则应根据 documentation 删除对 setLevel 的调用,因为这可以通过实现类来实现。

不支持调用 logger.setLevel() 或类似方法 API。应用程序应删除这些。等效功能是 在 Log4j 2 实现类中提供,但可能会离开 应用程序容易受到 Log4j 2 内部变化的影响。

【讨论】:

仅适用于运行时依赖LogManager.getLogger(Class.forName("org.hibernate.util.JDBCExceptionReporter")).setLevel(Level.FATAL); 请注意,log4j 2 API 不提供“setLevel”方法。 但这只会设置根记录器,不是吗?如果为根目录下的记录器设置了单独的级别,则设置根记录器对这些记录器没有影响。难道我们不需要做一些类似 root.getLoggerRepository().getCurrentCategories() 的事情,遍历记录器的每个实例并为每个记录器设置级别吗? @AaronMcIver @ChrisCantrell Log4j 2 does provide a way to do this,虽然没那么简单。 这个答案应该被否决,更改,删除或其他东西。它只会引起混乱,因为它没有记录如何为 log4j2 解决它。【参考方案2】:

文件看门狗

Log4j 能够监视log4j.xml 文件以进行配置更改。如果您更改 log4j 文件,log4j 将根据您的更改自动刷新日志级别。有关详细信息,请参阅org.apache.log4j.xml.DOMConfigurator.configureAndWatch(String,long) 的文档。检查之间的默认等待时间为 60 秒。这些更改将是持久的,因为您直接更改文件系统上的配置文件。您需要做的就是调用 DOMConfigurator.configureAndWatch() 一次。

注意:由于线程泄漏,configureAndWatch 方法在 J2EE 环境中使用是不安全的

JMX

另一种设置日志级别(或重新配置)log4j 的方法是使用 JMX。 Log4j 将其记录器注册为 JMX MBean。使用应用程序服务器 MBeanServer 控制台(或 JDK 的 jconsole.exe),您可以重新配置每个单独的记录器。这些更改不是持久的,并且会在您重新启动应用程序(服务器)后重置为配置文件中设置的配置。

自制

如 Aaron 所述,您可以通过编程方式设置日志级别。您可以按照您希望的方式在您的应用程序中实现它。例如,您可以有一个 GUI,用户或管理员可以在其中更改日志级别,然后在记录器上调用 setLevel() 方法。是否将设置保留在某处取决于您。

【讨论】:

关于 log4j 看门狗方法的注意事项:“因为 configureAndWatch 启动了一个单独的看门狗线程,并且因为在 log4j 1.2 中没有办法停止这个线程,所以 configureAndWatch 方法在 J2EE 中使用是不安全的应用程序被回收的环境”。参考:Log4J FAQ 如果我要使用 Log4j 的 configureAndWatch 功能,我可以在 EJB 的 @PostDestroy 方法中停止该看门狗线程(这是一个足够好的指示容器何时关闭) 是吗?或者还有更多我想念的东西..! 对不起,我的意思是@PreDestroy 方法 "Log4j 将其记录器注册为 JMX MBean。"。我的 servlet 使用 log4j 1.2 我没有看到任何 log4j MBean。 “由 Aaron 描述”- Aaron 是谁,描述了什么?这个答案需要说明是否有程序化的方式来做到这一点,而答案似乎是否定的。请这样说,因为这是您期望存在的,显然没有这样的解决方案。【参考方案3】:

Log4j2 可以配置为通过以给定的时间间隔扫描 log4j2.xml 文件(或等效文件)来刷新其配置。只需将“monitorInterval”参数添加到您的配置标签。请参阅示例 log4j2.xml 文件的第 2 行,该文件告诉 log4j 如果自上次日志事件超过 5 秒后重新扫描其配置。

<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="warn" monitorInterval="5" name="tryItApp" packages="">

    <Appenders>
        <RollingFile name="MY_TRY_IT"
                     fileName="/var/log/tryIt.log"
                     filePattern="/var/log/tryIt-%i.log.gz">
            <Policies>
                <SizeBasedTriggeringPolicy size="25 MB"/>
            </Policies>
            ...
        </RollingFile>
    </Appenders>


    <Loggers>
        <Root level="error">
            <AppenderRef ref="MY_TRY_IT"/>
        </Root>
    </Loggers>

</Configuration>

如果您要部署到 tomcat 实例、IDE 内部或使用 Spring Boot,则需要执行额外的步骤。这似乎有点超出了这里的范围,可能值得一个单独的问题。

【讨论】:

@vsingh,您是在 spring boot、tomcat 或容器中部署还是使用 IDE?有时在这些情况下可能会有额外的步骤(不是 log4j2 的错)——基本上应用程序看不到文件的变化,因为它已被框架或工具复制到另一个位置。 您好,我在 Jboss6.4 上对此进行了测试,并在应用程序可以看到该文件的位置(.war 文件内部)下更新了 log4j2 配置文件。然而它仍然没有工作。有什么建议吗?【参考方案4】:

对于 log4j 1.x,我发现最好的方法是使用 DOMConfigurator 来提交一组预定义的 XML 日志配置(例如,一个用于正常使用,一个用于调试)。

使用这些可以通过以下方式完成:

  public static void reconfigurePredefined(String newLoggerConfigName) 
    String name = newLoggerConfigName.toLowerCase();
    if ("default".equals(name)) 
      name = "log4j.xml";
     else 
      name = "log4j-" + name + ".xml";
    

    if (Log4jReconfigurator.class.getResource("/" + name) != null) 
      String logConfigPath = Log4jReconfigurator.class.getResource("/" + name).getPath();
      logger.warn("Using log4j configuration: " + logConfigPath);
      try (InputStream defaultIs = Log4jReconfigurator.class.getResourceAsStream("/" + name)) 
        new DOMConfigurator().doConfigure(defaultIs, LogManager.getLoggerRepository());
       catch (IOException e) 
        logger.error("Failed to reconfigure log4j configuration, could not find file " + logConfigPath + " on the classpath", e);
       catch (FactoryConfigurationError e) 
        logger.error("Failed to reconfigure log4j configuration, could not load file " + logConfigPath, e);
      
     else 
      logger.error("Could not find log4j configuration file " + name + ".xml on classpath");
    
  

只需使用适当的配置名称调用它,并确保将模板放在类路径中。

【讨论】:

这个方法的触发点是什么?它怎么知道当 log4j 配置发生变化时必须调用这个方法? 我仍然不明白您所说的“按需”。能否请您在连接此方法的位置显示一个 sn-p? 给你:这是来自我们使用它的 Controller 的 sn-p。我们有一个管理页面,其中包含一些动态重新配置日志记录的链接,然后对链接 /admin/setLogLevel?level=Error 进行 GET,然后我们像这样在控制器中捕获它 @ RequestMapping(value = "setLogLevel", method = RequestMethod.GET) public ModelAndView setLogLevel(@RequestParam(value = "level", required = true) String level) Log4jReconfigurator.reconfigureExisting(level); 因此,在我的情况下,“按需”意味着“当用户单击按钮时” 啊,明白了。因此,您的管理控制台必须发出请求才能调用此方法。【参考方案5】:

此答案不会帮助您动态更改日志记录级别,您需要重新启动服务,如果您可以重新启动 服务,请使用以下解决方案

我这样做是为了更改 log4j 日志级别,它对我有用,我没有参考任何文档。我使用这个系统属性值来设置我的日志文件名。我也使用了相同的技术来设置日志记录级别,并且成功了

将此作为 JVM 参数传递(我使用 Java 1.7)

抱歉,这不会动态更改日志记录级别,它需要重新启动服务

java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java

在 log4j.properties 文件中,我添加了这个条目

log4j.rootLogger=$logging.level,file,stdout

我试过了

 java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=INFO-cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=OFF -cp xxxxxx.jar  xxxxx.java

这一切都奏效了。希望这会有所帮助!

我的 pom.xml 中有以下这些依赖项

<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>apache-log4j-extras</artifactId>
    <version>1.2.17</version>
</dependency>

【讨论】:

无论您提到什么似乎都很好,但问题是关于动态更改日志记录级别。 为此,您需要重新启动服务。这不会动态更改日志记录级别。【参考方案6】:

你可以使用下面的代码sn-p

((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(packageName)).setLevel(ch.qos.logback.classic.Level.toLevel(logLevel));

【讨论】:

【参考方案7】:

如果您想更改所有记录器的记录级别,请使用以下方法。这将枚举所有记录器并将记录级别更改为给定级别。请确保您不要在您的 log4j.properties 文件中设置了 log4j.appender.loggerName.Threshold=DEBUG 属性。

public static void changeLogLevel(Level level) 
    Enumeration<?> loggers = LogManager.getCurrentLoggers();
    while(loggers.hasMoreElements()) 
        Logger logger = (Logger) loggers.nextElement();
        logger.setLevel(level);
    

【讨论】:

【参考方案8】:

我已经成功地使用了这种方法来减少“org.apache.http”日志的冗长:

ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("org.apache.http");
logger.setLevel(Level.TRACE);
logger.setAdditive(false);

【讨论】:

【参考方案9】:

对于 log4j 2 API,你可以使用

Logger logger = LogManager.getRootLogger();
Configurator.setAllLevels(logger.getName(), Level.getLevel(level));

【讨论】:

以上是关于动态改变 log4j 日志级别的主要内容,如果未能解决你的问题,请参考以下文章

如何log4j的日志级别改成debug

log4j输入日志都有哪些级别设置

关于log4j日志级别问题,懂的来

log4j动态日志级别调整

基于JMX动态配置Log4J日志级别

log4j中输入日志都有哪些级别设置