Cara mengubah level root logging secara program untuk logback

144

Saya memiliki file logback.xml berikut:

<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>

Sekarang, setelah terjadinya peristiwa tertentu, saya ingin secara programatik mengubah tingkat logger root dari debug ke kesalahan . Saya tidak bisa menggunakan substitusi variabel, saya wajib melakukan ini dalam kode.

Bagaimana itu bisa dilakukan? Terima kasih.

Kai Sternad
sumber

Jawaban:

235

Coba ini:

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

Perhatikan bahwa Anda juga dapat memberi tahu logback untuk secara berkala memindai file konfigurasi Anda seperti ini:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
dogbane
sumber
64
Perlu dicatat bahwa tujuan slf4j adalah untuk mengabstraksi kerangka logging, tetapi metode pertama tidak melakukannya dengan merujuk kerangka kerja logging secara langsung.
Tim Gautier
3
Jika Anda melakukan ini dan mendapatkan ClassCastException, kemungkinan besar karena memiliki beberapa ikatan SLF4J di classpath. Output log akan menunjukkan ini dan binding apa yang hadir untuk memungkinkan Anda menentukan yang mana yang harus Anda kecualikan.
icfantv
4
Slf4j menyediakan API sehingga perpustakaan dapat mencatat log aplikasi menggunakan kerangka log apa pun yang diinginkan pengembang aplikasi. Intinya adalah bahwa pengembang aplikasi masih harus memilih kerangka kerja log, bergantung padanya, dan mengkonfigurasinya. Mengkonfigurasi penebang sebagai dogbane telah dilakukan tidak melanggar prinsip ini.
Maks
4
@ JohnWiseman Jika Anda ingin dikonfigurasi, maka Anda harus mengkonfigurasinya di suatu tempat . Karena slf4j tidak menawarkan apa pun dalam hal ini, akan selalu ada sesuatu yang bergantung pada logger yang mendasarinya. Baik itu sepotong kode atau file konfigurasi. +++ Jika harus dilakukan secara programatik seperti OP yang diminta, maka Anda tidak punya pilihan. Namun, masih ada keuntungan: 1. Hanya sebagian kecil dari kode tergantung pada mesin logger beton (dan itu bisa ditulis sehingga dapat menangani implementasi yang berbeda). 2. Anda dapat mengonfigurasi perpustakaan yang ditulis menggunakan penebang lain juga.
maaartinus
4
Mengapa harus sangat rumit untuk sesuatu seperti Logging, seharusnya tidak ada cara langsung untuk mengubah level logging dalam kode itu sendiri. Bagaimana mengikuti prinsip perpustakaan tertentu lebih diutamakan dari kesederhanaannya? Berasal dari dunia Python, saya gagal memahami mengapa sesuatu yang sederhana seperti Logging sangat rumit di Java / Scala.
Abhinandan Dubey
11

Saya berasumsi Anda menggunakan logback (dari file konfigurasi).

Dari manual logback , saya mengerti

Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

Mungkin ini dapat membantu Anda mengubah nilainya?

Raghuram
sumber
10

menggunakan logback 1.1.3 Saya harus melakukan hal berikut (kode Scala):

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

Saya pikir Anda dapat menggunakan MDC untuk mengubah level logging secara terprogram. Kode di bawah ini adalah contoh untuk mengubah level logging pada utas saat ini. Pendekatan ini tidak menciptakan ketergantungan pada implementasi logback (API SLF4J berisi 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");
SATO Yusuke
sumber
3

Seperti yang ditunjukkan oleh orang lain, Anda cukup membuat mockAppenderdan kemudian membuat LoggingEventcontoh yang pada dasarnya mendengarkan acara logging yang terdaftar / terjadi di dalam mockAppender.

Berikut ini tampilannya dalam tes:

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"));
}
}
Solusi Sederhana
sumber
0

Sepertinya saya mengalami kesuksesan

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

Kemudian untuk mendapatkan detail logging dari netty, berikut ini telah dilakukan

org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
pengguna7610
sumber