Ändern Sie die Protokollebene in Log4j2 programmgesteuert

108

Ich bin daran interessiert, die Protokollstufe in Log4j2 programmgesteuert zu ändern. Ich habe versucht, die Konfigurationsdokumentation zu lesen, aber das schien nichts zu haben. Ich habe auch versucht, in das Paket zu schauen:, org.apache.logging.log4j.core.configaber nichts dort sah auch hilfreich aus.

CorayThan
quelle
2
Wenn Sie hier keine Antwort erhalten, probieren Sie die Mailingliste aus, die in der Regel alle 2 Tage von den Hauptautoren eingesehen wird. Dann komm zurück und beantworte deine eigene Frage :-)
tgkprog

Antworten:

138

BEARBEITET gemäß log4j2 Version 2.4 FAQ

Sie können die Ebene eines Loggers mit der Klasse Configurator von Log4j Core festlegen. ABER beachten Sie, dass die Configurator-Klasse nicht Teil der öffentlichen API ist.

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

Quelle

BEARBEITET, um Änderungen in der in Log4j2 Version 2.0.2 eingeführten API widerzuspiegeln

Wenn Sie die Root-Logger-Ebene ändern möchten, gehen Sie wie folgt vor:

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.

Hier ist das Javadoc für LoggerConfig.

Slaadvak
quelle
3
Richtig und wenn Sie nur für einen bestimmten Logger (einer Klasse / eines Pakets) ändern möchten, rufen Sie den Kontext dieses Loggers, setLevel und updateLoggers ab.
Tgkprog
34
Solch ein komplizierter Weg, nur um die Protokollierungsstufe einzustellen. Ich bin mir sicher, dass es einen Grund gibt, dies mit fünf Codezeilen zu tun, anstatt mit der ursprünglichen eine Zeile in früheren Versionen von log4j, aber ich sehe es einfach nicht. Auf jeden Fall danke dafür, @slaadvak!
Sturm
1
.updateLoggers () scheint nicht notwendig zu sein. Es scheint, dass die mit .setLevel () vorgenommenen Änderungen sofort angewendet werden.
Zbyszek
1
Der Aufruf von updateLoggers ist immer erforderlich, auch wenn Sie eine neue LoggerConfig hinzufügen. UpdateLoggers bewirkt, dass sich alle Logger wieder LoggerConfigs zuordnen und ihre Protokollstufe auf die der zugeordneten LoggerConfig ändern. Wenn Sie eine neue LoggerConfig hinzufügen, werden alle Logger, die dem neuen LoggerConfig-Muster entsprechen, dorthin umgeleitet. Der "verschlungene" Weg ist erforderlich, da Logger und ihre Konfiguration in Log4j2 getrennt wurden.
Besucher
1
Hier ist eine Antwort, die eine aktualisierte 1- oder 2-Zeilen-Lösung für neuere Versionen von Log4J bietet: stackoverflow.com/a/44678752/1339923
Lambart
37

Die akzeptierte Antwort von @slaadvak hat bei Log4j2 2.8.2 bei mir nicht funktioniert . Das Folgende tat es.

So ändern Sie das Protokoll Level allgemein :

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

LevelVerwenden Sie Folgendes, um das Protokoll nur für die aktuelle Klasse zu ändern :

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
quelle
1
Ich habe meine Stimme erhalten, weil Sie die Loggernamen aus den Loggern selbst gezogen haben, anstatt den Namen als Zeichenfolge fest zu codieren.
DWoldrich
18

Wenn Sie eine einzelne bestimmte Logger-Ebene ändern möchten (nicht den Root-Logger oder die in der Konfigurationsdatei konfigurierten Logger), können Sie Folgendes tun:

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
quelle
3
Mein Logger war davon überhaupt nicht betroffen. Ich habe verwendet setLevel(logger, Level.ERROR);und logger.debug Anweisungen noch gedruckt. Meine log4j2.xml-Datei befindet sich unter pastebin.com/fcbV2mTW
Noumenon
Ich habe den Code aktualisiert. Lassen Sie mich wissen, wenn es Probleme damit gibt.
Jörg Friedrich
4
In log4j 2.7 hat LoggerContext keine getConfiguration () -Methode, siehe logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme
log4j-core-2.7.jar hat und kann als endgültiger LoggerContext verwendet werden ctx = (LoggerContext) LogManager.getContext (false); endgültige Konfigurationskonfiguration = ctx.getConfiguration ();
Jprism
3
Nachdem ich das gesehen habe, denke ich ... "Vielleicht wollen sie nicht, dass wir das Level zur Laufzeit ändern?"
Koray Tugay
13

Ich habe hier eine gute Antwort gefunden: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Mit org.apache.logging.log4j.core.config.Configurator können Sie die Ebene für einen bestimmten Logger festlegen.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
quelle
2
Diese Antwort zeigt die gleiche Lösung sowie das Festlegen
Lambart
4

Der programmatische Ansatz ist eher aufdringlich. Vielleicht sollten Sie die JMX-Unterstützung von Log4J2 überprüfen:

  1. Aktivieren Sie den JMX-Port in Ihrem Anwendungsstart:

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

  2. Verwenden Sie einen der verfügbaren JMX-Clients (die JVM stellt einen in JAVA_HOME / bin / jconsole.exe bereit), während Sie Ihre Anwendung ausführen.

  3. Suchen Sie in JConsole nach der Bean "org.apache.logging.log4j2.Loggers"

  4. Ändern Sie schließlich die Ebene Ihres Loggers

Das, was mir am besten gefällt, ist, dass Sie Ihren Code oder Ihre Konfiguration nicht ändern müssen, um dies zu verwalten. Es ist alles extern und transparent.

Weitere Informationen: http://logging.apache.org/log4j/2.x/manual/jmx.html

Sieger
quelle
Sehr hilfreich, danke!
Darren Parker
3

Die meisten Antworten gehen standardmäßig davon aus, dass die Protokollierung additiv sein muss. Angenommen, ein Paket generiert viele Protokolle, und Sie möchten die Protokollierung nur für diesen bestimmten Protokollierer deaktivieren. Hier ist der Code, mit dem ich ihn zum Laufen gebracht habe

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

Ein Testfall dafür

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

Angenommen, die folgende log4j2.xml befindet sich im Klassenpfad

<?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>
Kobold
quelle
2

Eine ungewöhnliche Methode, die ich gefunden habe, besteht darin, zwei separate Dateien mit unterschiedlichen Protokollierungsstufen zu erstellen.
Beispielsweise. log4j2.xml und log4j-debug.xml Ändern Sie nun die Konfiguration aus diesen Dateien.
Beispielcode:

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
quelle