เปลี่ยนระดับการบันทึกโดยทางโปรแกรมใน Log4j2


109

ฉันสนใจที่จะเปลี่ยนระดับการบันทึกใน Log4j2 โดยทางโปรแกรม ฉันพยายามดูเอกสารการกำหนดค่าของพวกเขาแต่ดูเหมือนจะไม่มีอะไรเลย ฉันลองดูในแพ็คเกจด้วย: org.apache.logging.log4j.core.configแต่ไม่มีอะไรในนั้นก็ดูเป็นประโยชน์เช่นกัน


2
หากคุณไม่ได้รับคำตอบที่นี่ให้ลองดูรายชื่ออีเมลโดยทั่วไปจะถูกค้นหาโดยผู้เขียนหลักหนึ่งครั้งใน 2 วัน แล้วกลับมาตอบคำถามของคุณเอง :-)
tgkprog

คำตอบ:


139

แก้ไขตาม log4j2 เวอร์ชัน 2.4 คำถามที่พบบ่อย

คุณสามารถตั้งค่าระดับของคนตัดไม้ด้วยคลาส Configurator จาก Log4j Core แต่โปรดทราบว่าคลาส Configurator ไม่ได้เป็นส่วนหนึ่งของ 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);

ที่มา

แก้ไขเพื่อแสดงการเปลี่ยนแปลงใน API ที่นำมาใช้ใน Log4j2 เวอร์ชัน 2.0.2

หากคุณต้องการเปลี่ยนระดับคนตัดไม้รากให้ทำดังนี้:

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.

นี่คือ javadoc สำหรับ LoggerConfig


3
ถูกต้องและถ้าคุณต้องการเปลี่ยนเฉพาะคนตัดไม้ (ของคลาส / แพ็คเกจ) ให้รับบริบทของคนตัดไม้ setLevel และ updateLoggers นั้น
tgkprog

34
วิธีที่ซับซ้อนเช่นนี้เพื่อตั้งค่าระดับการบันทึก ฉันแน่ใจว่ามีเหตุผลในการใช้โค้ดห้าบรรทัดแทนที่จะเป็นบรรทัดเดียวใน log4j เวอร์ชันก่อนหน้า แต่ฉันไม่เห็นมัน ไม่ว่าในกรณีใดขอบคุณสำหรับสิ่งนี้ @slaadvak!
Sturm

1
.updateLoggers () ดูเหมือนจะไม่จำเป็น ดูเหมือนว่าการเปลี่ยนแปลงที่ทำกับ. setLevel () จะถูกนำไปใช้ทันที
zbyszek

1
จำเป็นต้องมีการเรียก updateLoggers เสมอแม้ว่าคุณจะเพิ่ม LoggerConfig ใหม่ UpdateLoggers ทำให้ผู้บันทึกทั้งหมดกลับมาเชื่อมโยงตนเองกับ LoggerConfigs และเปลี่ยนระดับการบันทึกเป็น LoggerConfig ที่เกี่ยวข้อง หากคุณเพิ่ม LoggerConfig ใหม่ Loggers ใด ๆ ที่ตรงกับรูปแบบ LoggerConfig ใหม่จะถูกเปลี่ยนเส้นทางไป ต้องใช้วิธี "ที่ซับซ้อน" เนื่องจากตัวบันทึกและการกำหนดค่าถูกแยกออกจากกันใน Log4j2
rgoers

1
นี่คือคำตอบที่ให้โซลูชัน 1 หรือ 2 บรรทัดที่อัปเดตสำหรับ Log4J เวอร์ชันใหม่กว่า: stackoverflow.com/a/44678752/1339923
Lambart

37

คำตอบที่ได้รับการยอมรับโดย @slaadvak ไม่ได้ทำงานสำหรับฉันLog4j2 2.8.2 ต่อไปนี้ทำ

ในการเปลี่ยนบันทึกโดยLevel ทั่วไปให้ใช้:

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

หากต้องการเปลี่ยนบันทึกLevelเฉพาะคลาสปัจจุบันให้ใช้:

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

1
ได้รับการโหวตของฉันเพราะคุณดึงชื่อคนตัดไม้มาจากคนตัดไม้เองแทนที่จะเข้ารหัสชื่อเป็นสตริง
DWoldrich

18

หากคุณต้องการเปลี่ยนระดับตัวบันทึกเฉพาะรายการเดียว (ไม่ใช่รูทคนตัดไม้หรือตัวบันทึกที่กำหนดค่าในไฟล์คอนฟิกูเรชัน) คุณสามารถทำได้:

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
สิ่งนี้ไม่ส่งผลกระทบต่อคนตัดไม้ของฉันเลย ฉันใช้setLevel(logger, Level.ERROR);และคำสั่ง logger.debug ยังคงพิมพ์อยู่ ไฟล์ log4j2.xml ของฉันอยู่ที่pastebin.com/fcbV2mTW
Noumenon

ฉันอัปเดตรหัสแล้ว โปรดแจ้งให้เราทราบหากมีปัญหาใด ๆ
Jörg Friedrich

4
ใน log4j 2.7 LoggerContext ไม่มีเมธอด getConfiguration () โปรดดูlogging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme

log4j-core-2.7.jar มีและสามารถใช้เป็น LoggerContext ขั้นสุดท้าย ctx = (LoggerContext) LogManager.getContext (false); การกำหนดค่าคอนฟิกสุดท้าย = ctx.getConfiguration ();
jprism

3
หลังจากเห็นสิ่งนี้ฉันก็คิดว่า .. "บางทีพวกเขาอาจไม่ต้องการให้เราเปลี่ยนระดับในรันไทม์?"
Koray Tugay

13

ฉันพบคำตอบที่ดีที่นี่: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

คุณสามารถใช้ org.apache.logging.log4j.core.config.Configurator เพื่อตั้งค่าระดับสำหรับตัวบันทึกเฉพาะ

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

2
คำตอบนี้แสดงวิธีแก้ปัญหาเดียวกันตลอดจนวิธีตั้งค่าสำหรับ root logger ซึ่งบางครั้งก็มีประโยชน์: stackoverflow.com/a/44678752/1339923
Lambart

4

วิธีการแบบเป็นโปรแกรมค่อนข้างก้าวก่าย บางทีคุณควรตรวจสอบการรองรับ JMX ที่ Log4J2 มอบให้:

  1. เปิดใช้งานพอร์ต JMX ในการเริ่มต้นแอปพลิเคชันของคุณ:

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

  2. ใช้ไคลเอนต์ JMX ใด ๆ ที่มีอยู่ (JVM มีหนึ่งใน JAVA_HOME / bin / jconsole.exe) ในขณะที่เรียกใช้แอปพลิเคชันของคุณ

  3. ใน JConsole ให้มองหาถั่ว "org.apache.logging.log4j2.Loggers"

  4. ในที่สุดก็เปลี่ยนระดับคนตัดไม้ของคุณ

สิ่งที่ฉันชอบมากที่สุดคือคุณไม่จำเป็นต้องแก้ไขโค้ดหรือการกำหนดค่าเพื่อจัดการสิ่งนี้ ภายนอกและโปร่งใสทั้งหมด

ข้อมูลเพิ่มเติม: http://logging.apache.org/log4j/2.x/manual/jmx.html


มีประโยชน์มากขอบคุณ!
Darren Parker

3

โดยค่าเริ่มต้นคำตอบส่วนใหญ่ถือว่าการบันทึกต้องมีการเพิ่มเติม แต่ให้บอกว่าบางแพ็คเกจกำลังสร้างบันทึกจำนวนมากและคุณต้องการปิดการบันทึกสำหรับคนตัดไม้นั้น ๆ เท่านั้น นี่คือรหัสที่ฉันใช้เพื่อให้มันใช้งานได้

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

กรณีทดสอบเดียวกัน

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

สมมติว่า log4j2.xml ต่อไปนี้อยู่ใน 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

วิธีหนึ่งที่ผิดปกติที่ฉันพบคือการสร้างไฟล์สองไฟล์แยกกันโดยมีระดับการบันทึกที่แตกต่างกัน
ตัวอย่างเช่น. log4j2.xml และ log4j-debug.xml ตอนนี้เปลี่ยนการกำหนดค่าจากไฟล์นี้
รหัสตัวอย่าง:

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));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.