วิธีการเปลี่ยนระดับการบันทึกรากโดยทางโปรแกรมสำหรับการย้อนกลับ


144

ฉันมีไฟล์ logback.xml ต่อไปนี้:

<configuration debug="true"> 

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
<encoder>
  <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="debug">
  <appender-ref ref="STDOUT" />
</root>
</configuration>

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

จะทำอย่างไร? ขอบคุณ

คำตอบ:


235

ลองสิ่งนี้:

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

Logger root = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);

โปรดทราบว่าคุณยังสามารถบอกให้ logback สแกนไฟล์กำหนดค่าของคุณเป็นระยะดังนี้:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 

64
ควรสังเกตว่าจุดประสงค์ของ slf4j คือการแยกเฟรมเวิร์กการบันทึกเป็นนามธรรม แต่วิธีแรกนั้นไม่ได้ทำโดยการอ้างอิงเฟรมเวิร์กการบันทึกโดยตรง
Tim Gautier

3
หากคุณทำสิ่งนี้และรับ ClassCastException เป็นไปได้มากที่สุดเนื่องจากมีการผูก SLF4J หลายครั้งบนคลาสพา ธ เอาต์พุตบันทึกจะระบุสิ่งนี้และการเชื่อมโยงใดที่มีอยู่เพื่อให้คุณกำหนดว่าคุณจะต้องแยกส่วนใด
icfantv

4
Slf4j จัดทำ API เพื่อให้ไลบรารีสามารถบันทึกแอปพลิเคชันโดยใช้เฟรมเวิร์กบันทึกที่นักพัฒนาแอปพลิเคชันต้องการ ประเด็นก็คือผู้พัฒนาแอ็พพลิเคชันยังคงต้องเลือกเฟรมเวิร์กบันทึกขึ้นอยู่กับมันและกำหนดค่า การกำหนดค่าตัวบันทึกเป็น dogbane ได้ทำแล้วไม่ได้ละเมิดหลักการนี้
สูงสุด

4
@JohnWiseman หากคุณต้องการกำหนดค่าแล้วคุณมีการกำหนดค่าที่ใดที่หนึ่ง ในฐานะที่เป็น slf4j ไม่มีอะไรในแง่นี้จะมีบางสิ่งขึ้นอยู่กับตัวบันทึกพื้นฐาน ไม่ว่าจะเป็นรหัสชิ้นส่วนหรือไฟล์การกำหนดค่า +++ ถ้ามันควรจะทำโดยทางโปรแกรมตาม OP ที่ร้องขอคุณก็ไม่มีทางเลือก ข้อดียังคงอยู่: 1. ส่วนเล็ก ๆ ของรหัสขึ้นอยู่กับเอ็นจิ้นตัวบันทึกคอนกรีต (และมันสามารถเขียนได้ 2. คุณสามารถกำหนดค่าไลบรารีที่เขียนโดยใช้ตัวบันทึกอื่นได้เช่นกัน
maaartinus

4
ทำไมมันจึงมีความซับซ้อนสำหรับบางอย่างเช่นการบันทึกไม่ควรมีวิธีโดยตรงในการเปลี่ยนระดับการบันทึกในโค้ดเอง การปฏิบัติตามหลักการของห้องสมุดโดยเฉพาะมีความสำคัญเหนือกว่าความเรียบง่ายอย่างไร มาจากโลก Python ฉันล้มเหลวที่จะเข้าใจว่าทำไมบางอย่างเรียบง่ายเหมือนกับการบันทึกจึงซับซ้อนใน Java / Scala
Abhinandan Dubey


10

ใช้ logback 1.1.3 ฉันต้องทำต่อไปนี้ (รหัส Scala):

import ch.qos.logback.classic.Logger
import org.slf4j.LoggerFactory    
...
val root: Logger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[Logger]

4

ฉันคิดว่าคุณสามารถใช้ MDC เพื่อเปลี่ยนระดับการบันทึกโดยทางโปรแกรม รหัสด้านล่างเป็นตัวอย่างในการเปลี่ยนระดับการบันทึกในเธรดปัจจุบัน วิธีนี้ไม่ได้สร้างการพึ่งพาการใช้งาน logback (SLF4J API มี MDC)

<configuration>
  <turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
    <Key>LOG_LEVEL</Key>
    <DefaultThreshold>DEBUG</DefaultThreshold>
    <MDCValueLevelPair>
      <value>TRACE</value>
      <level>TRACE</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>DEBUG</value>
      <level>DEBUG</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>INFO</value>
      <level>INFO</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>WARN</value>
      <level>WARN</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>ERROR</value>
      <level>ERROR</level>
    </MDCValueLevelPair>
  </turboFilter>
  ......
</configuration>
MDC.put("LOG_LEVEL", "INFO");

3

ในฐานะที่เป็นออกแหลมโดยคนอื่นคุณก็สร้างmockAppenderแล้วสร้างLoggingEventอินสแตนซ์ซึ่งเป็นหลักฟังเหตุการณ์การเข้าสู่ระบบลงทะเบียน / mockAppenderที่เกิดขึ้นภายใน

นี่คือลักษณะในการทดสอบ:

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;

@RunWith(MockitoJUnitRunner.class)
public class TestLogEvent {

// your Logger
private Logger log = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

// here we mock the appender
@Mock
private Appender<ILoggingEvent> mockAppender;

// Captor is generic-ised with ch.qos.logback.classic.spi.LoggingEvent
@Captor
private ArgumentCaptor<LoggingEvent> captorLoggingEvent;

/**
 * set up the test, runs before each test
 */
@Before
public void setUp() {
    log.addAppender(mockAppender);
}

/**
 * Always have this teardown otherwise we can stuff up our expectations. 
 * Besides, it's good coding practise
 */
@After
public void teardown() {
    log.detachAppender(mockAppender);
}


// Assuming this is your method
public void yourMethod() {
    log.info("hello world");
}

@Test
public void testYourLoggingEvent() {

    //invoke your method
    yourMethod();

    // now verify our logging interaction
    // essentially appending the event to mockAppender
    verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture());

    // Having a generic captor means we don't need to cast
    final LoggingEvent loggingEvent = captorLoggingEvent.getValue();

    // verify that info log level is called
    assertThat(loggingEvent.getLevel(), is(Level.INFO));

    // Check the message being logged is correct
    assertThat(loggingEvent.getFormattedMessage(), containsString("hello world"));
}
}

0

ฉันดูเหมือนจะประสบความสำเร็จในการทำ

org.jboss.logmanager.Logger logger = org.jboss.logmanager.Logger.getLogger("");
logger.setLevel(java.util.logging.Level.ALL);

จากนั้นเพื่อรับการบันทึกอย่างละเอียดจาก netty ต่อไปนี้ได้ดำเนินการแล้ว

org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.