So ändern Sie die Root-Protokollierungsstufe programmgesteuert für die Rückmeldung

144

Ich habe die folgende logback.xml-Datei:

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

Wenn nun ein bestimmtes Ereignis eintritt, möchte ich die Ebene des Root-Loggers programmgesteuert von Debug auf Fehler ändern . Ich kann keine Variablensubstitution verwenden. Es ist obligatorisch, dass ich dies innerhalb des Codes mache.

Wie geht das? Vielen Dank.

Kai Sternad
quelle

Antworten:

235

Versuche dies:

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

Beachten Sie, dass Sie logback auch anweisen können, Ihre Konfigurationsdatei regelmäßig wie folgt zu scannen:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
Dogbane
quelle
64
Es sollte beachtet werden, dass der Zweck von slf4j darin besteht, das Protokollierungsframework zu abstrahieren, aber diese erste Methode beseitigt dies, indem sie direkt auf das Protokollierungsframework verweist.
Tim Gautier
3
Wenn Sie dies tun und eine ClassCastException erhalten, liegt dies höchstwahrscheinlich daran, dass der Klassenpfad mehrere SLF4J-Bindungen enthält. Die Protokollausgabe zeigt dies und welche Bindungen vorhanden sind, damit Sie bestimmen können, welche Bindungen Sie ausschließen müssen.
icfantv
4
Slf4j bietet eine API, mit der Bibliotheken Anwendungsprotokolle mit dem vom Anwendungsentwickler gewünschten Protokollframework protokollieren können. Der Punkt ist, dass der Anwendungsentwickler immer noch ein Protokollframework auswählen, davon abhängig sein und es konfigurieren muss. Die Konfiguration des Loggers wie bei Dogbane verstößt nicht gegen dieses Prinzip.
Max
4
@ JohnWiseman Wenn Sie möchten, dass es konfiguriert wird, müssen Sie es irgendwo konfigurieren . Da slf4j diesbezüglich nichts bietet, hängt immer etwas vom zugrunde liegenden Logger ab. Sei es ein Stück Code oder eine Konfigurationsdatei. +++ Wenn es programmgesteuert wie vom OP angefordert durchgeführt werden soll, haben Sie keine Wahl. Dennoch bleiben Vorteile: 1. Nur ein winziger Teil des Codes hängt von der konkreten Logger-Engine ab (und er könnte so geschrieben werden, dass er unterschiedliche Implementierungen handhaben kann). 2. Sie können Bibliotheken konfigurieren, die auch mit anderen Loggern geschrieben wurden.
Maaartinus
4
Warum muss es für so etwas wie die Protokollierung so kompliziert sein, sollte es keinen direkten Weg geben, die Protokollierungsstufe im Code selbst zu ändern. Wie hat das Befolgen des Prinzips einer bestimmten Bibliothek Vorrang vor ihrer Einfachheit? Ich komme aus einer Python-Welt und verstehe nicht, warum etwas so Einfaches wie die Protokollierung in Java / Scala so kompliziert ist.
Abhinandan Dubey
11

Ich gehe davon aus, dass Sie Logback verwenden (aus der Konfigurationsdatei).

Aus dem Logback-Handbuch sehe ich

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

Vielleicht kann Ihnen dies helfen, den Wert zu ändern?

Raghuram
quelle
10

mit logback 1.1.3 musste ich folgendes tun (Scala-Code):

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

Ich denke, Sie können MDC verwenden, um die Protokollierungsstufe programmgesteuert zu ändern. Der folgende Code ist ein Beispiel zum Ändern der Protokollierungsstufe für den aktuellen Thread. Dieser Ansatz schafft keine Abhängigkeit von der Logback-Implementierung (SLF4J-API enthält 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
quelle
3

Wie von anderen hervorgehoben, erstellen Sie einfach mockAppendereine LoggingEventInstanz und erstellen sie dann, die im Wesentlichen das darin registrierte / stattfindende Protokollierungsereignis abhört mockAppender.

So sieht es im Test aus:

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"));
}
}
Einfache Lösung
quelle
0

Ich scheine Erfolg zu haben

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

Um eine detaillierte Protokollierung von netty zu erhalten, haben Sie Folgendes getan

org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
user7610
quelle