【问题标题】:Programmatically change log level in Log4j2以编程方式更改 Log4j2 中的日志级别
【发布时间】:2014-06-19 11:58:13
【问题描述】:

我对以编程方式更改 Log4j2 中的日志级别感兴趣。我尝试查看他们的configuration documentation,但似乎没有任何内容。我还尝试查看包裹:org.apache.logging.log4j.core.config,但里面也没有任何帮助。

【问题讨论】:

  • 如果您在这里没有得到答案,请尝试邮件列表,主要作者通常会在 2 天内查看一次。然后回来回答你自己的问题:-)

标签: java log4j2


【解决方案1】:

简单的方法:

已根据 log4j2 2.4 版常见问题解答

您可以使用 Log4j Core 中的类 Configurator 设置记录器的级别。 请注意,Configurator 类不是公共 API 的一部分。

// org.apache.logging.log4j.core.config.Configurator;
Configurator.setLevel("com.example.Foo", Level.DEBUG);

// You can also set the root logger:
Configurator.setRootLevel(Level.DEBUG);

Source

首选方式:

已编辑以反映 Log4j2 版本 2.0.2 中引入的 API 的更改

如果您想更改根记录器级别,请执行以下操作:

LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME); 
loggerConfig.setLevel(level);
ctx.updateLoggers();  // This causes all Loggers to refetch information from their LoggerConfig.

Here 是 LoggerConfig 的 javadoc。

【讨论】:

  • 对,如果您只想更改(类/包的)特定记录器,请获取该记录器的上下文,setLevel 和 updateLoggers。
  • 这种复杂的方式只是为了设置日志级别。我确信使用五行代码而不是以前版本的 log4j 中的原始一行代码是有原因的,但我只是看不到它。无论如何,谢谢@slaadvak!
  • .updateLoggers() 似乎没有必要。似乎使用 .setLevel() 所做的更改会立即应用。
  • 始终需要调用 updateLoggers,即使您添加了新的 LoggerConfig。 UpdateLoggers 使所有记录器重新与 LoggerConfigs 关联,并将其日志级别更改为与其关联的 LoggerConfig 的级别。如果您添加新的 LoggerConfig,任何与新 LoggerConfig 模式匹配的 Logger 都将被重定向到它。需要“复杂”的方式,因为 Loggers 及其配置在 Log4j2 中已经分离。
  • 这是一个为较新版本的 Log4J 提供更新的 1 行或 2 行解决方案的答案:stackoverflow.com/a/44678752/1339923
【解决方案2】:

程序化方法相当具有侵入性。也许您应该检查 Log4J2 提供的 JMX 支持:

  1. 在应用程序启动时启用 JMX 端口:

    -Dcom.sun.management.jmxremote.port=[port_num]

  2. 在执行应用程序时使用任何可用的 JMX 客户端(JVM 在 JAVA_HOME/bin/jconsole.exe 中提供了一个)。

  3. 在 JConsole 中查找“org.apache.logging.log4j2.Loggers”bean

  4. 最后更改记录器的级别

我最喜欢的一点是您不必修改代码或配置来管理它。这一切都是外部和透明的。

更多信息:http://logging.apache.org/log4j/2.x/manual/jmx.html

【讨论】:

  • 非常有帮助,谢谢!
【解决方案3】:

如果您想更改单个特定记录器级别(不是根记录器或配置文件中配置的记录器),您可以这样做:

public static void setLevel(Logger logger, Level level) {
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();

    LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName());
    LoggerConfig specificConfig = loggerConfig;

    // We need a specific configuration for this logger,
    // otherwise we would change the level of all other loggers
    // having the original configuration as parent as well

    if (!loggerConfig.getName().equals(logger.getName())) {
        specificConfig = new LoggerConfig(logger.getName(), level, true);
        specificConfig.setParent(loggerConfig);
        config.addLogger(logger.getName(), specificConfig);
    }
    specificConfig.setLevel(level);
    ctx.updateLoggers();
}

【讨论】:

  • 这根本不影响我的记录器。我使用了setLevel(logger, Level.ERROR);,并且仍然打印了 logger.debug 语句。我的 log4j2.xml 文件位于 pastebin.com/fcbV2mTW
  • 我更新了代码。如果有任何问题,请告诉我。
  • 在 log4j 2.7 LoggerContext 没有 getConfiguration() 方法,见logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
  • log4j-core-2.7.jar 有并且可以作为最终的LoggerContext ctx = (LoggerContext) LogManager.getContext(false);最终配置配置 = ctx.getConfiguration();
  • 看到这个后,我在想..“也许他们不希望我们在运行时改变关卡?”
【解决方案4】:

我在这里找到了一个很好的答案:https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

您可以使用 org.apache.logging.log4j.core.config.Configurator 设置特定记录器的级别。

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);

【讨论】:

【解决方案5】:

@slaadvak 接受的答案对我来说不适用于Log4j2 2.8.2。以下是这样做的。

要更改日志Level 普遍使用:

Configurator.setAllLevels(LogManager.getRootLogger().getName(), level);

要仅为当前类更改日志Level,请使用:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);

【讨论】:

  • 得到了我的投票,因为您从记录器本身中提取了记录器名称,而不是将名称硬编码为字符串。
  • 虽然此解决方案适用于控制台日志记录,但对于 rollingFile 使用,我必须设置适当的 ThresholdFilter 值以确保更改得到反映。
【解决方案6】:

我发现一种不寻常的方法是创建两个具有不同日志记录级别的单独文件。
例如。 log4j2.xml 和 log4j-debug.xml 现在从此文件更改配置。
示例代码:

ConfigurationFactory configFactory = XmlConfigurationFactory.getInstance();
            ConfigurationFactory.setConfigurationFactory(configFactory);
            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
            ClassLoader classloader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classloader.getResourceAsStream(logFileName);
            ConfigurationSource configurationSource = new ConfigurationSource(inputStream);

            ctx.start(configFactory.getConfiguration(ctx, configurationSource));

【讨论】:

    【解决方案7】:

    默认情况下,大多数答案都假定日志记录必须是附加的。但是假设某个包正在生成大量日志,而您只想关闭该特定记录器的日志记录。这是我用来让它工作的代码

        public class LogConfigManager {
    
        public void setLogLevel(String loggerName, String level) {
            Level newLevel = Level.valueOf(level);
            LoggerContext logContext = (LoggerContext) LogManager.getContext(false);
            Configuration configuration = logContext.getConfiguration();
            LoggerConfig loggerConfig = configuration.getLoggerConfig(loggerName);
            // getLoggerConfig("a.b.c") could return logger for "a.b" if there is no logger for "a.b.c"
            if (loggerConfig.getName().equalsIgnoreCase(loggerName)) {
                loggerConfig.setLevel(newLevel);
                log.info("Changed logger level for {} to {} ", loggerName, newLevel);
            } else {
                // create a new config.
                loggerConfig = new LoggerConfig(loggerName, newLevel, false);
                log.info("Adding config for: {} with level: {}", loggerConfig, newLevel);
                configuration.addLogger(loggerName, loggerConfig);
    
    
                LoggerConfig parentConfig = loggerConfig.getParent();
                do {
                    for (Map.Entry<String, Appender> entry : parentConfig.getAppenders().entrySet()) {
                        loggerConfig.addAppender(entry.getValue(), null, null);
                    }
                    parentConfig = parentConfig.getParent();
                } while (null != parentConfig && parentConfig.isAdditive());
            }
            logContext.updateLoggers();
        }
    }
    

    相同的测试用例

    public class LogConfigManagerTest {
        @Test
        public void testLogChange() throws IOException {
            LogConfigManager logConfigManager = new LogConfigManager();
            File file = new File("logs/server.log");
            Files.write(file.toPath(), new byte[0], StandardOpenOption.TRUNCATE_EXISTING);
            Logger logger = LoggerFactory.getLogger("a.b.c");
            logger.debug("Marvel-1");
            logConfigManager.setLogLevel("a.b.c", "debug");
            logger.debug("DC-1");
            // Parent logger level should remain same
            LoggerFactory.getLogger("a.b").debug("Marvel-2");
            logConfigManager.setLogLevel("a.b.c", "info");
            logger.debug("Marvel-3");
            // Flush everything
            LogManager.shutdown();
    
            String content = Files.readAllLines(file.toPath()).stream().reduce((s1, s2) -> s1 + "\t" + s2).orElse(null);
            Assert.assertEquals(content, "DC-1");
        }
    }
    

    假设以下 log4j2.xml 在类路径中

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
    
        <Appenders>
            <File name="FILE" fileName="logs/server.log" append="true">
                <PatternLayout pattern="%m%n"/>
            </File>
            <Console name="STDOUT" target="SYSTEM_OUT">
                <PatternLayout pattern="%m%n"/>
            </Console>
        </Appenders>
    
        <Loggers>
            <AsyncLogger name="a.b" level="info">
                <AppenderRef ref="STDOUT"/>
                <AppenderRef ref="FILE"/>
            </AsyncLogger>
    
            <AsyncRoot level="info">
                <AppenderRef ref="STDOUT"/>
            </AsyncRoot>
        </Loggers>
    
    </Configuration>
    

    【讨论】:

      【解决方案8】:

      对于那些仍在为此苦苦挣扎的人,我不得不将类加载器添加到“getContext()”调用中:

        log.info("Modifying Log level! (maybe)");
        LoggerContext ctx = (LoggerContext) LogManager.getContext(this.getClass().getClassLoader(), false);
        Configuration config = ctx.getConfiguration();
        LoggerConfig loggerConfig = config.getLoggerConfig("com.cat.barrel");
        loggerConfig.setLevel(org.apache.logging.log4j.Level.TRACE);
        ctx.updateLoggers();
      

      我在测试中添加了一个 jvm 参数:-Dlog4j.debug。这为 log4j 做了一些详细的日志记录。我注意到最终的 LogManager 不是我使用的那个。 Bam,添加类加载器,你就可以参加比赛了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-17
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 2019-05-06
        相关资源
        最近更新 更多