Ubah tingkat log secara terprogram di Log4j2

109

Saya tertarik untuk mengubah level log secara terprogram di Log4j2. Saya mencoba melihat dokumentasi konfigurasi mereka tetapi sepertinya tidak ada apa-apa. Saya juga mencoba mencari di dalam paket:, org.apache.logging.log4j.core.configtetapi tidak ada yang tampak membantu juga.

CorayThan
sumber
2
Jika Anda tidak mendapatkan jawaban di sini, coba daftar surat, biasanya dilihat sekali dalam 2 hari oleh penulis utama. Kemudian kembali dan jawab pertanyaan Anda sendiri :-)
tgkprog

Jawaban:

139

DIEDIT menurut FAQ log4j2 versi 2.4

Anda dapat mengatur level logger dengan kelas Configurator dari Log4j Core. TAPI ketahuilah bahwa kelas Configurator bukan bagian dari API publik.

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

Sumber

DIEDIT untuk mencerminkan perubahan dalam API yang diperkenalkan di Log4j2 versi 2.0.2

Jika Anda ingin mengubah level root logger, lakukan sesuatu seperti ini:

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.

Berikut adalah javadoc untuk LoggerConfig.

slaadvak
sumber
3
Benar dan jika Anda ingin mengubah hanya untuk logger tertentu (dari kelas / paket) dapatkan konteks dari logger, setLevel dan updateLoggers.
tgkprog
34
Cara yang berbelit-belit hanya untuk mengatur tingkat penebangan. Saya yakin ada alasan untuk melakukannya dengan lima baris kode daripada satu baris asli di versi log4j sebelumnya, tetapi saya tidak melihatnya. Bagaimanapun, terima kasih untuk ini, @slaadvak!
Kokoh
1
.updateLoggers () tampaknya tidak diperlukan. Tampaknya perubahan yang dilakukan dengan .setLevel () segera diterapkan.
zbyszek
1
Panggilan ke updateLoggers selalu diperlukan, bahkan jika Anda menambahkan LoggerConfig baru. UpdateLoggers menyebabkan semua penebang untuk reasociate diri mereka sendiri dengan LoggerConfigs dan untuk mengubah tingkat log mereka ke yang terkait LoggerConfig mereka. Jika Anda menambahkan LoggerConfig baru, semua Logger yang cocok dengan pola LoggerConfig baru akan dialihkan ke sana. Cara "berbelit-belit" diperlukan karena Logger dan konfigurasinya telah dipisahkan di Log4j2.
pengunjung
1
Berikut adalah jawaban yang menyediakan solusi 1 atau 2 baris yang diperbarui untuk versi Log4J yang lebih baru: stackoverflow.com/a/44678752/1339923
Lambart
37

Jawaban yang diterima oleh @slaadvak tidak berhasil untuk saya untuk Log4j2 2.8.2 . Berikut ini lakukan.

Untuk mengubah log Level secara universal gunakan:

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

Untuk mengubah log Levelhanya untuk kelas saat ini, gunakan:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
sumber
1
Mendapat suara saya karena Anda menarik nama logger dari para penebang itu sendiri daripada mengkodekan nama itu sebagai string.
DWoldrich
18

Jika Anda ingin mengubah satu tingkat pencatat tertentu (bukan pencatat akar atau pencatat yang dikonfigurasi dalam file konfigurasi), Anda dapat melakukan ini:

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
sumber
3
Ini tidak mempengaruhi logger saya sama sekali. Saya menggunakan setLevel(logger, Level.ERROR);dan pernyataan logger.debug masih dicetak. File log4j2.xml saya ada di pastebin.com/fcbV2mTW
Noumenon
Saya memperbarui kode. Beri tahu saya jika ada masalah.
Jörg Friedrich
4
Di log4j 2.7 LoggerContext tidak memiliki metode getConfiguration (), lihat logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme
log4j-core-2.7.jar memiliki dan dapat digunakan sebagai LoggerContext akhir ctx = (LoggerContext) LogManager.getContext (false); Konfigurasi konfigurasi terakhir = ctx.getConfiguration ();
jprism
3
Setelah melihat ini, saya berpikir .. "Mungkin mereka tidak ingin kami mengubah level pada waktu proses?"
Koray Tugay
13

Saya menemukan jawaban yang bagus di sini: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Anda dapat menggunakan org.apache.logging.log4j.core.config.Configurator untuk menyetel tingkat logger tertentu.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
sumber
2
Jawaban ini menunjukkan solusi yang sama, serta cara mengaturnya untuk root logger - yang terkadang berguna: stackoverflow.com/a/44678752/1339923
Lambart
4

Pendekatan terprogram agak mengganggu. Mungkin Anda harus memeriksa dukungan JMX yang diberikan oleh Log4J2:

  1. Aktifkan port JMX di aplikasi Anda saat memulai:

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

  2. Gunakan salah satu klien JMX yang tersedia (JVM menyediakannya di JAVA_HOME / bin / jconsole.exe) saat menjalankan aplikasi Anda.

  3. Di JConsole, cari kacang "org.apache.logging.log4j2.Loggers"

  4. Terakhir, ubah level logger Anda

Hal yang paling saya sukai dari ini adalah Anda tidak perlu mengubah kode atau konfigurasi untuk mengelola ini. Semuanya eksternal dan transparan.

Info selengkapnya: http://logging.apache.org/log4j/2.x/manual/jmx.html

Pemenang
sumber
Sangat membantu, Terima kasih!
Darren Parker
3

Sebagian besar jawaban secara default mengasumsikan bahwa logging harus bersifat aditif. Tetapi katakanlah bahwa beberapa paket menghasilkan banyak log dan Anda ingin menonaktifkan logging hanya untuk logger tersebut. Berikut adalah kode yang saya gunakan untuk membuatnya berfungsi

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

Kasus uji untuk hal yang sama

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

Dengan asumsi log4j2.xml berikut ada di 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>
momok
sumber
2

Salah satu cara tidak biasa yang saya lakukan adalah membuat dua file terpisah dengan tingkat logging berbeda.
Sebagai contoh. log4j2.xml dan log4j-debug.xml Sekarang ubah konfigurasi dari file ini.
Kode sampel:

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
sumber