Alterar programaticamente o nível de registro no Log4j2

108

Estou interessado em alterar programaticamente o nível de log no Log4j2. Eu tentei olhar a documentação de configuração, mas não parecia haver nada. Eu também tentei procurar no pacote:, org.apache.logging.log4j.core.configmas nada ali parecia útil também.

CorayThan
fonte
2
Se você não obtiver uma resposta aqui, tente a lista de e-mail, geralmente é consultada uma vez a cada 2 dias pelos autores principais. Então volte e responda sua própria pergunta :-)
tgkprog

Respostas:

138

EDITADO de acordo com log4j2 versão 2.4 FAQ

Você pode definir um nível de logger com a classe Configurator do Log4j Core. MAS esteja ciente de que a classe Configurator não faz parte da API pública.

// 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);

Fonte

EDITADO para refletir as mudanças na API introduzidas no Log4j2 versão 2.0.2

Se você deseja alterar o nível do logger raiz, faça algo assim:

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.

Aqui está o javadoc para LoggerConfig.

Slaadvak
fonte
3
Certo e se você quiser alterar apenas para um logger específico (de uma classe / pacote), obtenha o contexto desse logger, setLevel e updateLoggers.
tgkprog
34
Uma forma tão complicada apenas de definir o nível de registro. Tenho certeza de que há uma razão para fazer isso com cinco linhas de código em vez da linha original nas versões anteriores do log4j, mas simplesmente não consigo ver. Em qualquer caso, obrigado por isso, @slaadvak!
Sturm
1
.updateLoggers () não parece ser necessário. Parece que as alterações feitas com .setLevel () são aplicadas imediatamente.
zbyszek
1
A chamada para updateLoggers é sempre necessária, mesmo se você adicionar um novo LoggerConfig. UpdateLoggers faz com que todos os registradores se associem novamente a LoggerConfigs e alterem seu nível de registro para o de seu LoggerConfig associado. Se você adicionar um novo LoggerConfig, quaisquer Loggers que correspondam ao novo padrão LoggerConfig serão redirecionados para ele. A forma "complicada" é necessária porque os registradores e suas configurações foram separados no Log4j2.
rgoers
1
Aqui está uma resposta que fornece uma solução atualizada de 1 ou 2 linhas para versões mais recentes do Log4J: stackoverflow.com/a/44678752/1339923
Lambart
37

A resposta aceita por @slaadvak não funcionou para mim para Log4j2 2.8.2 . O seguinte fez.

Para alterar o log Level universalmente, use:

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

Para alterar o registro Levelapenas da classe atual, use:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
fonte
1
Consegui meu voto porque você retirou os nomes dos madeireiros dos próprios madeireiros, em vez de codificar o nome como uma string.
DWoldrich
18

Se você deseja alterar um único nível de logger específico (não o logger root ou loggers configurados no arquivo de configuração), você pode fazer isso:

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();
}
Jörg Friedrich
fonte
3
Isso não afetou meu logger. Usei setLevel(logger, Level.ERROR);e as instruções logger.debug ainda impressas. Meu arquivo log4j2.xml está em pastebin.com/fcbV2mTW
Noumenon
Eu atualizei o código. Deixe-me saber se houver algum problema com isso.
Jörg Friedrich
4
Em log4j 2.7 LoggerContext não tem um método getConfiguration (), consulte logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme
log4j-core-2.7.jar tem e pode ser usado como final LoggerContext ctx = (LoggerContext) LogManager.getContext (false); configuração final da configuração = ctx.getConfiguration ();
jprism
3
Depois de ver isso, estou pensando ... "Talvez eles não queiram que mudemos o nível em tempo de execução?"
Koray Tugay
13

Encontrei uma boa resposta aqui: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Você pode usar org.apache.logging.log4j.core.config.Configurator para definir o nível de um registrador específico.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
fonte
2
Esta resposta mostra a mesma solução, bem como como configurá-la para o logger root - o que às vezes é útil: stackoverflow.com/a/44678752/1339923
Lambart
4

A abordagem programática é bastante intrusiva. Talvez você deva verificar o suporte JMX fornecido pelo Log4J2:

  1. Habilite a porta JMX na inicialização do seu aplicativo:

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

  2. Use qualquer um dos clientes JMX disponíveis (o JVM fornece um em JAVA_HOME / bin / jconsole.exe) ao executar seu aplicativo.

  3. No JConsole, procure o bean "org.apache.logging.log4j2.Loggers"

  4. Finalmente, mude o nível do seu logger

O que mais gosto disso é que você não precisa modificar seu código ou configuração para gerenciar isso. É tudo externo e transparente.

Mais informações: http://logging.apache.org/log4j/2.x/manual/jmx.html

Vencedor
fonte
Muito útil, obrigado!
Darren Parker de
3

A maioria das respostas, por padrão, assume que o log deve ser aditivo. Mas digamos que algum pacote esteja gerando muitos logs e você queira desligar o log apenas para aquele logger específico. Aqui está o código que usei para fazê-lo funcionar

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

Um caso de teste para o mesmo

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

Supondo que o seguinte log4j2.xml esteja no classpath

<?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>
hobgoblin
fonte
2

Uma maneira incomum que descobri de fazer é criar dois arquivos separados com níveis de registro diferentes.
Por exemplo. log4j2.xml e log4j-debug.xml Agora altere a configuração desses arquivos.
Código de amostra:

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));
dpp.2325
fonte