Jak programowo zmienić poziom rejestrowania głównego w celu wylogowania

144

Mam następujący plik 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>

Teraz, po wystąpieniu określonego zdarzenia, chcę programowo zmienić poziom głównego programu rejestrującego z debugowania na błąd . Nie mogę używać podstawiania zmiennych, jest to obowiązkowe, aby zrobić to w kodzie.

Jak można to zrobić ? Dzięki.

Kai Sternad
źródło

Odpowiedzi:

235

Spróbuj tego:

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

Zauważ, że możesz także powiedzieć logback'owi, aby okresowo skanował twój plik konfiguracyjny w następujący sposób:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
dogbane
źródło
64
Należy zauważyć, że slf4j ma na celu wyodrębnienie struktury rejestrowania, ale ta pierwsza metoda eliminuje to przez bezpośrednie odwoływanie się do struktury rejestrowania.
Tim Gautier
3
Jeśli to zrobisz i otrzymasz wyjątek ClassCastException, najprawdopodobniej jest to spowodowane posiadaniem wielu powiązań SLF4J w ścieżce klas. Dane wyjściowe dziennika wskażą to i które powiązania są obecne, aby umożliwić określenie, które z nich należy wykluczyć.
icfantv
4
Slf4j zapewnia interfejs API, dzięki czemu biblioteki mogą rejestrować dzienniki aplikacji przy użyciu dowolnej struktury dzienników, jakiej chce twórca aplikacji. Chodzi o to, że twórca aplikacji nadal musi wybrać strukturę dziennika, polegać na niej i ją skonfigurować. Skonfigurowanie loggera w taki sam sposób, jak zrobił to dogbane, nie narusza tej zasady.
Maksymalnie
4
@JohnWiseman Jeśli chcesz, aby to zostało skonfigurowane, musisz to gdzieś skonfigurować . Ponieważ slf4j nic nie oferuje pod tym względem, zawsze będzie coś zależnego od bazowego programu rejestrującego. Czy to fragment kodu, czy plik konfiguracyjny. +++ Jeśli powinno to być zrobione programowo, zgodnie z żądaniem PO, nie masz wyboru. Jednak zalety pozostają: 1. Tylko niewielka część kodu zależy od konkretnego silnika rejestrującego (i można go napisać tak, aby obsługiwał różne implementacje). 2. Możesz skonfigurować biblioteki napisane przy użyciu innych loggerów.
maaartinus
4
Dlaczego musi to być tak skomplikowane w przypadku czegoś takiego jak logowanie, czy nie powinien istnieć bezpośredni sposób zmiany poziomu rejestrowania w samym kodzie. W jaki sposób przestrzeganie zasady konkretnej biblioteki ma pierwszeństwo przed jej prostotą? Pochodząc ze świata Pythona, nie rozumiem, dlaczego coś tak prostego jak logowanie jest tak skomplikowane w Javie / Scali.
Abhinandan Dubey
11

Zakładam, że używasz logback (z pliku konfiguracyjnego).

Z instrukcji wylogowywania , widzę

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

Może to pomoże Ci zmienić wartość?

Raghuram
źródło
10

używając logback 1.1.3 musiałem wykonać następujące czynności (kod 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
źródło
4

Myślę, że można użyć MDC do programowej zmiany poziomu rejestrowania. Poniższy kod jest przykładem zmiany poziomu rejestrowania w bieżącym wątku. Takie podejście nie powoduje zależności od implementacji logback (interfejs API SLF4J zawiera 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
źródło
3

Jak zauważyli inni, po prostu tworzysz, mockAppendera następnie tworzysz LoggingEventinstancję, która zasadniczo nasłuchuje zarejestrowanego / zachodzącego w niej zdarzenia logowaniamockAppender .

Oto jak to wygląda w teście:

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"));
}
}
Proste rozwiązanie
źródło
0

Wydaje się, że odnoszę sukcesy

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

Następnie, aby uzyskać szczegółowe logowanie z netty, wykonano następujące czynności

org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
user7610
źródło