Modificare a livello di programmazione il livello di registro in Log4j2


109

Sono interessato a modificare in modo programmatico il livello di registro in Log4j2. Ho provato a guardare la loro documentazione di configurazione, ma non sembrava avere nulla. Ho anche provato a cercare nel pacchetto:, org.apache.logging.log4j.core.configma nemmeno lì dentro sembrava utile.


2
Se non ottieni una risposta qui, prova la mail list, generalmente viene cercata una volta in 2 giorni dagli autori principali. Quindi torna indietro e rispondi alla tua stessa domanda :-)
tgkprog

Risposte:


139

MODIFICATO secondo le FAQ di log4j2 versione 2.4

È possibile impostare il livello di un logger con il configuratore di classi di Log4j Core. MA tieni presente che la classe Configurator non fa parte dell'API pubblica.

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

MODIFICATO per riflettere le modifiche all'API introdotte nella versione 2.0.2 di Log4j2

Se desideri modificare il livello del logger di root, fai qualcosa del genere:

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.

Ecco il javadoc per LoggerConfig.


3
Giusto e se vuoi cambiare solo per un particolare logger (di una classe / pacchetto) ottieni il contesto di quel logger, setLevel e updateLoggers.
tgkprog

34
Un modo così complicato solo per impostare il livello di registrazione. Sono sicuro che ci sia una ragione per farlo con cinque righe di codice invece della riga originale nelle versioni precedenti di log4j, ma semplicemente non lo vedo. In ogni caso, grazie per questo, @slaadvak!
Sturm

1
.updateLoggers () non sembra essere necessario. Sembra che le modifiche apportate con .setLevel () vengano applicate immediatamente.
zbyszek

1
La chiamata a updateLoggers è sempre richiesta, anche se aggiungi un nuovo LoggerConfig. UpdateLoggers fa in modo che tutti i logger si associno nuovamente a LoggerConfigs e modifichino il loro livello di log a quello del LoggerConfig associato. Se aggiungi un nuovo LoggerConfig, tutti i logger che corrispondono al nuovo pattern LoggerConfig verranno reindirizzati ad esso. Il modo "contorto" è richiesto perché i logger e la loro configurazione sono stati separati in Log4j2.
arrivo

1
Ecco una risposta che fornisce un aggiornamento, a 1 o soluzione 2-line per le versioni più recenti di Log4J: stackoverflow.com/a/44678752/1339923
Lambart

37

La risposta accettata da @slaadvak non ha funzionato per me per Log4j2 2.8.2 . Quanto segue ha fatto.

Per modificare il registro Level universalmente utilizzare:

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

Per modificare il registro Levelsolo per la classe corrente, utilizzare:

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

1
Ho ottenuto il mio voto perché hai estratto i nomi dei logger dai logger stessi invece di codificare il nome come una stringa.
DWoldrich

18

Se desideri modificare un singolo livello di logger specifico (non il logger root oi logger configurati nel file di configurazione) puoi farlo:

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
Questo non ha influenzato affatto il mio logger. Ho usato le setLevel(logger, Level.ERROR);istruzioni e logger.debug ancora stampate. Il mio file log4j2.xml è su pastebin.com/fcbV2mTW
Noumenon

Ho aggiornato il codice. Fammi sapere se ci sono problemi con esso.
Jörg Friedrich

4
In log4j 2.7 LoggerContext è un metodo non getConfiguration (), vedere logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/...
maxxyme

log4j-core-2.7.jar ha e può essere utilizzato come LoggerContext finale ctx = (LoggerContext) LogManager.getContext (false); configurazione finale della configurazione = ctx.getConfiguration ();
jprism

3
Dopo aver visto questo, penso ... "Forse non vogliono che cambiamo il livello in runtime?"
Koray Tugay

13

Ho trovato una buona risposta qui: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

È possibile utilizzare org.apache.logging.log4j.core.config.Configurator per impostare il livello per un logger specifico.

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

2
Questa risposta mostra la stessa soluzione, oltre a come impostarla per il root logger, che a volte è utile: stackoverflow.com/a/44678752/1339923
Lambart

4

L'approccio programmatico è piuttosto invadente. Forse dovresti controllare il supporto JMX fornito da Log4J2:

  1. Abilita la porta JMX all'avvio dell'applicazione:

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

  2. Utilizzare uno dei client JMX disponibili (la JVM ne fornisce uno in JAVA_HOME / bin / jconsole.exe) durante l'esecuzione dell'applicazione.

  3. In JConsole cerca il bean "org.apache.logging.log4j2.Loggers"

  4. Infine cambia il livello del tuo logger

La cosa che mi piace di più è che non è necessario modificare il codice o la configurazione per gestirla. È tutto esterno e trasparente.

Maggiori informazioni: http://logging.apache.org/log4j/2.x/manual/jmx.html


Molto utile, grazie!
Darren Parker

3

La maggior parte delle risposte per impostazione predefinita presume che la registrazione debba essere additiva. Ma si supponga che alcuni pacchetti generino molti log e si desideri disattivare la registrazione solo per quel particolare logger. Ecco il codice che ho usato per farlo funzionare

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

Un caso di prova per lo stesso

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

Supponendo che il seguente log4j2.xml sia in 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>

2

Un modo insolito che ho scoperto di fare è creare due file separati con un livello di registrazione diverso.
Per esempio. log4j2.xml e log4j-debug.xml Ora cambia la configurazione da questi file.
Codice di esempio:

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));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.