Програмно змінити рівень журналу в Log4j2


109

Мені цікаво програмно змінити рівень журналу в Log4j2. Я спробував переглянути їх конфігураційну документацію, але, схоже, нічого не було. Я також спробував заглянути в пакет: org.apache.logging.log4j.core.configале нічого там теж не було корисним.


2
Якщо ви не отримаєте відповіді тут, спробуйте список електронної пошти, як правило, його розглядають раз на 2 дні основні автори. Потім поверніться і дайте відповідь на власне запитання :-)
tgkprog

Відповіді:


139

ВИДАЛЕНО відповідно до log4j2 версії 2.4 FAQ

Ви можете встановити рівень реєстратора за допомогою конфігуратора класу від Log4j Core. Але слід пам’ятати, що клас Конфігуратор не є частиною загальнодоступного 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);

Джерело

ЗРЕШЕНО для відображення змін в API, введеному в Log4j2 версії 2.0.2

Якщо ви хочете змінити рівень кореневого реєстратора, зробіть щось подібне:

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.

Ось javadoc для LoggerConfig.


3
Правильно, і якщо ви хочете змінити лише для конкретного реєстратора (класу / пакета), отримайте контекст цього реєстратора, setLevel та updateLoggers.
tgkprog

34
Такий складний спосіб просто встановити рівень журналу. Я впевнений, що є причина робити це з п'ятьма рядками коду, а не оригінальним одним рядком у попередніх версіях log4j, але я просто не бачу цього. У будь-якому випадку, дякую за це, @slaadvak!
Штурм

1
.updateLoggers () видається не потрібним. Здається, що зміни, зроблені за допомогою .setLevel (), застосовуються негайно.
zbyszek

1
Заклик до updateLoggers завжди необхідний, навіть якщо ви додали новий LoggerConfig. UpdateLoggers змушує всіх реєстраторів повторно асоціюватися з LoggerConfigs та змінювати свій рівень журналу на рівень асоційованого LoggerConfig. Якщо ви додасте новий LoggerConfig, усі Logger, які відповідають новому шаблону LoggerConfig, будуть перенаправлені на нього. Шлях "згортання" необхідний, оскільки Логгери та їх конфігурація були розділені в Log4j2.
rgoers

1
Ось відповідь , який надає оновлений, 1 або розчин 2-лінія для нових версій Log4J: stackoverflow.com/a/44678752/1339923
Lambart

37

Прийнята відповідь @slaadvak для мене не працювала для Log4j2 2.8.2 . Зробив наступне.

Щоб змінити журнал Level універсально, використовуйте:

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

Щоб змінити журнал Levelлише для поточного класу, використовуйте:

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

1
Отримав моє голосування, тому що ви витягнули імена реєстраторів із самих реєстраторів, а не жорстко кодували ім'я як рядок.
DWoldrich

18

Якщо ви хочете змінити один конкретний рівень реєстратора (не кореневий реєстратор або реєстратори, налаштовані у файлі конфігурації), ви можете зробити це:

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();
}

3
Це зовсім не вплинуло на мій реєстратор. Я використовував setLevel(logger, Level.ERROR);і logger.debug заяви, які все ще надруковані. Мій файл log4j2.xml знаходиться на pastebin.com/fcbV2mTW
Ноумен,

Я оновив код. Повідомте мене, чи є з цим якісь проблеми.
Йорг Фрідріх

4
У log4j 2.7 LoggerContext не має методу getConfiguration (), див. Logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme

log4j-core-2.7.jar має і може використовуватися як остаточний LoggerContext ctx = (LoggerContext) LogManager.getContext (помилково); остаточна конфігурація конфігурації = ctx.getConfiguration ();
jprism

3
Побачивши це, я замислююся .. "Може бути, вони не хочуть, щоб ми змінювали рівень під час виконання?"
Корай Тугай

13

Тут я знайшов хорошу відповідь: 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);

2
Ця відповідь показує те ж саме рішення, а також як встановити його для кореневого реєстратора - що іноді корисно: stackoverflow.com/a/44678752/1339923
Lambart

4

Програмний підхід досить нав'язливий. Можливо, ви повинні перевірити підтримку JMX, надану Log4J2:

  1. Увімкніть порт JMX у запуску програми:

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

  2. Використовуйте будь-який із доступних клієнтів JMX (JVM надає його в JAVA_HOME / bin / jconsole.exe) під час виконання вашої заявки.

  3. У JConsole шукайте боб "org.apache.logging.log4j2.Loggers"

  4. Нарешті змінити рівень свого реєстратора

Що мені найбільше подобається в тому, що вам не потрібно змінювати свій код або конфігурацію для управління цим. Все зовнішнє та прозоре.

Більше інформації: http://logging.apache.org/log4j/2.x/manual/jmx.html


Дуже корисно, спасибі!
Даррен Паркер

3

Більшість відповідей за замовчуванням передбачають, що ведення журналу повинно бути адитивним. Але скажіть, що якийсь пакет генерує безліч журналів, і ви хочете вимкнути журнал лише для цього конкретного реєстратора. Ось код, за допомогою якого я працював

    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>

2

Я знайшов один із незвичних способів - створити два окремі файли з різним рівнем реєстрації.
Наприклад. 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));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.