Programowo zmień poziom dziennika w Log4j2

109

Jestem zainteresowany programową zmianą poziomu logowania w Log4j2. Próbowałem przejrzeć ich dokumentację konfiguracyjną, ale wydawało się, że nic tam nie ma. Próbowałem też zajrzeć do paczki: org.apache.logging.log4j.core.configale tam też nic nie wyglądało na pomocne.

CorayThan
źródło
2
Jeśli nie znajdziesz tutaj odpowiedzi, wypróbuj listę mailingową, której główni autorzy zazwyczaj przeglądają raz na 2 dni. W takim razie wróć i odpowiedz na własne pytanie :-)
tgkprog

Odpowiedzi:

139

EDYTOWANE zgodnie z FAQ log4j2 w wersji 2.4

Możesz ustawić poziom loggera za pomocą klasy Configurator z Log4j Core. ALE należy pamiętać, że klasa Configurator nie jest częścią publicznego interfejsu 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);

Źródło

ZMIENIONO, aby odzwierciedlić zmiany w API wprowadzone w Log4j2 w wersji 2.0.2

Jeśli chcesz zmienić poziom głównego programu rejestrującego, zrób coś takiego:

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.

Oto javadoc dla LoggerConfig.

slaadvak
źródło
3
Zgadza się i jeśli chcesz zmienić tylko dla konkretnego rejestratora (klasy / pakietu), pobierz kontekst tego rejestratora, setLevel i updateLoggers.
tgkprog
34
Taki zawiły sposób wystarczy ustawić poziom logowania. Jestem pewien, że jest powód, aby robić to z pięcioma liniami kodu, a nie oryginalną linią w poprzednich wersjach log4j, ale po prostu tego nie widzę. W każdym razie dziękuję za to, @slaadvak!
Sturm
1
.updateLoggers () nie wydaje się być konieczne. Wygląda na to, że zmiany dokonane za pomocą .setLevel () są stosowane natychmiast.
zbyszek
1
Wywołanie updateLoggers jest zawsze wymagane, nawet jeśli dodasz nowy LoggerConfig. UpdateLoggers powoduje, że wszystkie programy rejestrujące ponownie kojarzą się z LoggerConfigs i zmieniają swój poziom dziennika na powiązany z nimi LoggerConfig. Jeśli dodasz nowy LoggerConfig, wszystkie Loggery pasujące do nowego wzorca LoggerConfig zostaną do niego przekierowane. Metoda „zawiła” jest wymagana, ponieważ Loggery i ich konfiguracja zostały oddzielone w Log4j2.
osoby
1
Oto odpowiedź, która zapewnia zaktualizowane, 1- lub 2-wierszowe rozwiązanie dla nowszych wersji Log4J: stackoverflow.com/a/44678752/1339923
Lambart,
37

Zaakceptowana odpowiedź @slaadvak nie zadziałała dla mnie dla Log4j2 2.8.2 . Następujące zrobiły.

Aby zmienić dziennik Level powszechnie używać:

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

Aby zmienić dziennik Leveltylko dla bieżącej klasy, użyj:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
źródło
1
Mam swój głos, ponieważ pobrałeś nazwy rejestratorów z samych rejestratorów, zamiast na sztywno zakodować nazwę jako ciąg.
DWoldrich
18

Jeśli chcesz zmienić konkretny poziom rejestratora (nie root lub loggery skonfigurowane w pliku konfiguracyjnym), możesz to zrobić:

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
źródło
3
W ogóle nie wpłynęło to na mój rejestrator. Użyłem setLevel(logger, Level.ERROR);i nadal drukowano instrukcje logger.debug. Mój plik log4j2.xml znajduje się pod adresem pastebin.com/fcbV2mTW
Noumenon
Zaktualizowałem kod. Daj mi znać, jeśli są z tym jakieś problemy.
Jörg Friedrich,
4
W log4j 2.7 LoggerContext nie ma metody getConfiguration (), patrz logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme
log4j-core-2.7.jar ma i może być używany jako ostateczny LoggerContext ctx = (LoggerContext) LogManager.getContext (false); końcowa konfiguracja konfiguracji = ctx.getConfiguration ();
jprism
3
Widząc to, myślę… „Może nie chcą, żebyśmy zmieniali poziom w czasie wykonywania?”
Koray Tugay
13

Znalazłem dobrą odpowiedź tutaj: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Możesz użyć org.apache.logging.log4j.core.config.Configurator, aby ustawić poziom dla określonego programu rejestrującego.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
źródło
2
Ta odpowiedź pokazuje to samo rozwiązanie, a także jak ustawić je dla głównego loggera - co czasami jest przydatne: stackoverflow.com/a/44678752/1339923
Lambart
4

Podejście programowe jest raczej uciążliwe. Być może powinieneś sprawdzić obsługę JMX zapewnianą przez Log4J2:

  1. Włącz port JMX podczas uruchamiania aplikacji:

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

  2. Użyj dowolnego z dostępnych klientów JMX (JVM zapewnia jednego w JAVA_HOME / bin / jconsole.exe) podczas wykonywania aplikacji.

  3. W JConsole poszukaj komponentu bean „org.apache.logging.log4j2.Loggers”

  4. Wreszcie zmień poziom swojego loggera

Najbardziej podoba mi się to, że nie musisz modyfikować swojego kodu ani konfiguracji, aby tym zarządzać. To wszystko jest zewnętrzne i przejrzyste.

Więcej informacji: http://logging.apache.org/log4j/2.x/manual/jmx.html

Zwycięzca
źródło
Bardzo pomocne, dzięki!
Darren Parker,
3

Większość odpowiedzi domyślnie zakłada, że ​​rejestrowanie musi być addytywne. Ale powiedz, że jakiś pakiet generuje dużo logów i chcesz wyłączyć rejestrowanie tylko dla tego konkretnego rejestratora. Oto kod, którego użyłem, aby to działało

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

Przypadek testowy dla tego samego

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

Zakładając, że następujący plik log4j2.xml znajduje się w ścieżce klasy

<?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>
chochlik
źródło
2

Jednym z nietypowych sposobów jest utworzenie dwóch oddzielnych plików z różnymi poziomami rejestrowania.
Na przykład. log4j2.xml i log4j-debug.xml Teraz zmień konfigurację z tych plików.
Przykładowy kod:

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
źródło