Modifier le niveau de journalisation par programme dans Log4j2

109

Je suis intéressé par la modification par programme du niveau de journal dans Log4j2. J'ai essayé de regarder leur documentation de configuration mais cela ne semblait rien avoir. J'ai également essayé de regarder dans le paquet:, org.apache.logging.log4j.core.configmais rien ne semblait utile non plus.

CorayThan
la source
2
Si vous n'obtenez pas de réponse ici, essayez la liste de diffusion, elle est généralement consultée une fois tous les 2 jours par les principaux auteurs. Alors revenez et répondez à votre propre question :-)
tgkprog

Réponses:

139

ÉDITÉ selon la FAQ de log4j2 version 2.4

Vous pouvez définir le niveau d'un enregistreur avec le configurateur de classe de Log4j Core. MAIS sachez que la classe Configurator ne fait pas partie de l'API publique.

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

La source

MODIFIÉ pour refléter les modifications de l'API introduites dans Log4j2 version 2.0.2

Si vous souhaitez modifier le niveau de journalisation racine, procédez comme suit:

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.

Voici le javadoc pour LoggerConfig.

slaadvak
la source
3
Bien et si vous voulez changer pour juste un enregistreur particulier (d'une classe / package), obtenez le contexte de cet enregistreur, setLevel et updateLoggers.
tgkprog
34
Une telle manière alambiquée juste pour définir le niveau de journalisation. Je suis sûr qu'il y a une raison de le faire avec cinq lignes de code plutôt que la ligne d'origine dans les versions précédentes de log4j, mais je ne la vois tout simplement pas. En tout cas, merci pour cela, @slaadvak!
Sturm
1
.updateLoggers () ne semble pas nécessaire. Il semble que les modifications effectuées avec .setLevel () sont appliquées immédiatement.
zbyszek
1
L'appel à updateLoggers est toujours requis, même si vous ajoutez un nouveau LoggerConfig. UpdateLoggers oblige tous les enregistreurs à se réassocier à LoggerConfigs et à changer leur niveau de journalisation en celui de leur LoggerConfig associé. Si vous ajoutez un nouveau LoggerConfig, tous les Loggers qui correspondent au nouveau modèle LoggerConfig y seront redirigés. La méthode «alambiquée» est requise car les enregistreurs et leur configuration ont été séparés dans Log4j2.
rgoers
1
Voici une réponse qui fournit une solution mise à jour, sur 1 ou 2 lignes pour les nouvelles versions de Log4J: stackoverflow.com/a/44678752/1339923
Lambart
37

La réponse acceptée par @slaadvak n'a pas fonctionné pour moi pour Log4j2 2.8.2 . Ce qui suit a fait.

Pour changer le journal de manière Level universelle, utilisez:

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

Pour modifier le journal Leveluniquement pour la classe actuelle, utilisez:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
la source
1
J'ai obtenu mon vote parce que vous avez extrait les noms des enregistreurs des enregistreurs eux-mêmes plutôt que de coder en dur le nom sous forme de chaîne.
DWoldrich
18

Si vous souhaitez modifier un seul niveau d'enregistreur spécifique (pas l'enregistreur racine ou les enregistreurs configurés dans le fichier de configuration), vous pouvez le faire:

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
la source
3
Cela n'a pas du tout affecté mon enregistreur. J'ai utilisé setLevel(logger, Level.ERROR);et les déclarations logger.debug toujours imprimées. Mon fichier log4j2.xml est à pastebin.com/fcbV2mTW
Noumenon
J'ai mis à jour le code. Faites-moi savoir s'il y a des problèmes avec cela.
Jörg Friedrich
4
Dans log4j 2.7 LoggerContext n'a pas de méthode getConfiguration (), voir logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org
...
log4j-core-2.7.jar a et peut être utilisé comme LoggerContext final ctx = (LoggerContext) LogManager.getContext (false); configuration de configuration finale = ctx.getConfiguration ();
jprism
3
Après avoir vu ça, je me dis… "Peut-être qu'ils ne veulent pas que nous changions le niveau en runtime?"
Koray Tugay
13

J'ai trouvé une bonne réponse ici: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Vous pouvez utiliser org.apache.logging.log4j.core.config.Configurator pour définir le niveau d'un enregistreur spécifique.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
la source
2
Cette réponse montre la même solution, ainsi que la façon de le définir pour le logger racine - ce qui est parfois utile: stackoverflow.com/a/44678752/1339923
Lambart
4

L'approche programmatique est plutôt intrusive. Vous devriez peut-être vérifier le support JMX fourni par Log4J2:

  1. Activez le port JMX au démarrage de votre application:

    -Dcom.sun.management.jmxremote.port = [numéro_port]

  2. Utilisez l'un des clients JMX disponibles (la JVM en fournit un dans JAVA_HOME / bin / jconsole.exe) lors de l'exécution de votre application.

  3. Dans JConsole, recherchez le bean "org.apache.logging.log4j2.Loggers"

  4. Modifiez enfin le niveau de votre enregistreur

Ce que j'aime le plus, c'est que vous n'avez pas à modifier votre code ou votre configuration pour gérer cela. Tout est externe et transparent.

Plus d'infos: http://logging.apache.org/log4j/2.x/manual/jmx.html

Victor
la source
Très utile, merci!
Darren Parker
3

La plupart des réponses supposent par défaut que la journalisation doit être additive. Mais disons qu'un paquet génère beaucoup de journaux et que vous souhaitez désactiver la journalisation pour cet enregistreur particulier uniquement. Voici le code que j'ai utilisé pour le faire fonctionner

    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 cas de test pour le même

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

En supposant que log4j2.xml suivant est dans le chemin de classe

<?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>
lutin
la source
2

Une façon inhabituelle que j'ai trouvée de faire est de créer deux fichiers séparés avec un niveau de journalisation différent.
Par exemple. log4j2.xml et log4j-debug.xml Modifiez maintenant la configuration à partir de ces fichiers.
Exemple de code:

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
la source