是否有一种机制可以以可预测的方式重新配置 log4j2?

epz*_*epz 5 log4j2

我正在寻找在 log4j 可能已初始化或未初始化后配置/重新配置 log4j 的方法。这应该可以独立运行或在 Web 容器中运行。

配置可以由特定任意 URI 处的配置文件来表示。URI 的知识来自应用程序,而不是 log4j 框架。配置也可以通过编程方式完成(问题较少,但问题仍然存在)。

遗憾的是,公共 API 严重缺乏,因此开发人员被迫使用 log4j 核心的实现类编写脆弱的代码。经过数周的研究文档和逐步浏览 log4j 代码,我发现了两种完成重新配置的方法:

  1. 停止当前上下文并使用 log4j.core.config.Configurator 重新初始化,

类似于以下内容:

  ((LoggerContext) LogManager.getContext(false)).stop();
  Configurator.initialize(buildDefaultConfiguration()); //programmatically building a configuration
Run Code Online (Sandbox Code Playgroud)

或者

  ((LoggerContext) LogManager.getContext(false)).stop();
  Configurator.initialize(null, ConfigurationSource.fromUri(loggingUri)); //passing the configuration source constructed from a known URI
Run Code Online (Sandbox Code Playgroud)

如果当前上下文已经创建并启动(例如在 Web 容器中运行时),两个示例中的第一行都会停止当前上下文。如果 log4j 尚未初始化(例如作为独立应用程序运行时),它将使用默认配置初始化 log4j 并首先启动上下文(作为调用 getContext() 的副作用),然后停止它。

如果当前上下文没有首先停止,则对 Configurator.initialize() 的调用将不起作用。log4j 将忽略您重新初始化的尝试,不会给您任何指示,而只是简单地返回当前上下文。本手册的“使用 ConfigurationBuilder 和配置器重新配置 Log4j”部分中未提及此行为。它只是说:“但是,如果在调用 Configurator.initialize() 之前尝试进行任何日志记录,那么默认配置将用于这些日志事件。” 默认配置还将用于提供的示例中的所有后续日志事件,因为调用 Configurator.initialize() 将不起作用,除非首先停止当前上下文。

  1. 在现有上下文上设置新的配置位置,从而强制重新配置,

类似于以下内容:

  ((LoggerContext) LogManager.getContext(false)).setConfigLocation(loggingUri);
Run Code Online (Sandbox Code Playgroud)

这以类似的方式工作:如果 log4j 尚未初始化,则对 getContext() 的调用将触发默认上下文的初始化和创建,然后重新配置默认上下文;如果它已经被初始化,那么当前的上下文,无论它是什么,都将被重新配置。配置源将由 log4j 框架从 URI 创建。

不同之处在于,在第一种方式中,上下文被替换,并且旧(已停止)上下文中的所有记录器都将死亡。如果堆栈上的任何代码保存对这些失效记录器的引用并尝试记录它们,那么这将是一个空操作。在第二种方式中,保留上下文,但替换配置,并使用新配置更新现有记录器。

两种方法都使用核心代码,因此都很脆弱,但都适用于晴天场景(无论如何使用 log4j-core 2.10.0)。然而,两者似乎都没有为用户提供处理任何异常事件的任何控制权,甚至没有通知用户出现了问题。Log4j 将“吃掉”任何异常,并自行决定如何处理它们。

如果在调用 Configurator.initialize() 后出现问题,log4j 将创建一个默认配置,该配置有效地切断除控制台错误之外的所有日志记录,并愉快地返回新上下文,而不向调用代码提供任何日志记录已有效停止的线索。

如果在调用 LoggerContext.setConfigLocation() 后出现问题,log4j 基本上会执行相同的操作,只是保留当前上下文。人们可能会认为,特别是在重新配置失败的情况下,最典型的处理是恢复到旧的配置。似乎没有办法完成此操作,因为 log4j 将简单地停止记录(除了控制台的错误)并且不会向调用代码提供任何失败指示。

这是一个典型的场景:多个应用程序扩展了一个公共框架。该框架为所有扩展应用程序配置相同的日志记录(以方便重用并简化日志的后处理)。某些应用程序具有独特的日志记录需求,并尝试从其自己的元数据(位于已知 URI 的配置文件)重新配置 log4j。XML 解析器在解析该文件时抛出异常。该异常由 log4j 内部处理,日志记录悄然停止,没有人知道。嗯,有一个错误日志发送到 StatusLogger,但有异常,但调用代码不知道。

因此,有了这个冗长的序言,问题是:是否有一种我尚未发现的机制可以以可预测的方式修改 log4j 配置并能够处理出现的异常事件?也就是说,除了像上面的例子一样扩展 XML 配置类并替换处理异常的代码,从而冒着在当前 log4j 实现中产生不良副作用的风险,更不用说更大的破坏风险了。将来如果实施发生变化。

任何帮助将不胜感激!