Cómo cambiar el nivel de registro raíz mediante programación para el inicio de sesión

144

Tengo el siguiente archivo 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>

Ahora, cuando ocurre un evento específico, quiero cambiar programáticamente el nivel del registrador raíz de depuración a error . No puedo usar la sustitución de variables, es obligatorio que lo haga dentro del código.

Cómo puede hacerse esto ? Gracias.

Kai Sternad
fuente

Respuestas:

235

Prueba esto:

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

Tenga en cuenta que también puede indicarle a Logback que escanee periódicamente su archivo de configuración de esta manera:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
dogbane
fuente
64
Cabe señalar que el propósito de slf4j es abstraer el marco de registro, pero ese primer método lo elimina al hacer referencia directamente al marco de registro.
Tim Gautier
3
Si hace esto y obtiene una ClassCastException, lo más probable es que tenga múltiples enlaces SLF4J en el classpath. La salida del registro indicará esto y qué enlaces están presentes para permitirle determinar cuál (es) debe excluir.
icfantv
44
Slf4j proporciona una API para que las bibliotecas puedan registrar registros de aplicaciones utilizando cualquier marco de registro que el desarrollador de la aplicación desee. El punto es que el desarrollador de la aplicación aún debe elegir un marco de registro, depender de él y configurarlo. Configurar el registrador como lo ha hecho dogbane no viola este principio.
Max
44
@JohnWiseman Si desea que se configure, debe configurarlo en algún lugar . Como slf4j no ofrece nada a este respecto, siempre habrá algo dependiente del registrador subyacente. Ya sea un código o un archivo de configuración. +++ Si se debe hacer mediante programación como lo solicitó el OP, entonces no tiene otra opción. Aún así, las ventajas permanecen: 1. Solo una pequeña parte del código depende del motor de registrador concreto (y podría escribirse para que pueda manejar diferentes implementaciones). 2. También puede configurar bibliotecas escritas con otros registradores.
maaartinus
44
¿Por qué tiene que ser tan complicado para algo como el registro? ¿No debería haber una forma directa de cambiar el nivel de registro en el código mismo? ¿Cómo tiene prioridad el seguir el principio de una biblioteca particular sobre su simplicidad? Viniendo de un mundo de Python, no entiendo por qué algo tan simple como el registro es tan complicado en Java / Scala.
Abhinandan Dubey
11

Supongo que está utilizando logback (del archivo de configuración).

Del manual de logback , veo

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

¿Quizás esto puede ayudarte a cambiar el valor?

Raghuram
fuente
10

usando logback 1.1.3 tuve que hacer lo siguiente (código 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
fuente
4

Creo que puede usar MDC para cambiar el nivel de registro mediante programación. El siguiente código es un ejemplo para cambiar el nivel de registro en el subproceso actual. Este enfoque no crea dependencia para la implementación de inicio de sesión (API SLF4J contiene 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
fuente
3

Como lo señalaron otros, simplemente crea mockAppendery luego crea una LoggingEventinstancia que esencialmente escucha el evento de registro registrado / sucede dentro mockAppender.

Así es como se ve en la prueba:

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"));
}
}
Solución simple
fuente
0

Parece que estoy teniendo éxito haciendo

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

Luego, para obtener un registro detallado de netty, lo siguiente lo ha hecho

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