Como alterar o nível de log de raiz programaticamente para logback

144

Eu tenho o seguinte arquivo 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>

Agora, após a ocorrência de um evento específico, desejo alterar programaticamente o nível do criador de logs raiz de depuração para erro . Não posso usar substituição de variável, é obrigatório fazer isso dentro do código.

Como pode ser feito ? Obrigado.

Kai Sternad
fonte

Respostas:

235

Tente o seguinte:

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

Observe que você também pode solicitar ao logback que verifique periodicamente seu arquivo de configuração assim:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
dogbane
fonte
64
Deve-se notar que o objetivo do slf4j é abstrair a estrutura de log, mas esse primeiro método elimina isso referenciando a estrutura de log diretamente.
Tim Gautier
3
Se você fizer isso e obter uma ClassCastException, é provável que tenha várias ligações SLF4J no caminho de classe. A saída do log indicará isso e quais ligações estão presentes para permitir que você determine quais devem ser excluídas.
icfantv
4
O Slf4j fornece uma API para que as bibliotecas possam registrar os logs do aplicativo usando a estrutura de log que o desenvolvedor do aplicativo desejar. O ponto é que o desenvolvedor de aplicativos ainda deve escolher uma estrutura de log, depender dela e configurá-la. Configurar o criador de logs como o dogbane fez não viola esse princípio.
Max
4
@ JohnWiseman Se você quiser que seja configurado, precisará configurá-lo em algum lugar . Como o slf4j não oferece nada a esse respeito, sempre haverá algo dependente do criador de logs subjacente. Seja um pedaço de código ou um arquivo de configuração. +++ Se isso deve ser feito programaticamente conforme o OP solicitado, você não tem escolha. Ainda assim, as vantagens permanecem: 1. Apenas uma pequena parte do código depende do mecanismo do registrador de concreto (e pode ser escrito para que ele possa lidar com implementações diferentes). 2. Você também pode configurar bibliotecas escritas usando outros registradores.
Maaartinus 15/05
4
Por que isso tem que ser tão complicado para algo como Logging, caso não haja uma maneira direta de alterar o nível de log no próprio código. Como seguir o princípio de determinada biblioteca tem precedência sobre sua simplicidade? Vindo de um mundo Python, não entendo por que algo tão simples como o Logging é tão complicado em Java / Scala.
Abhinandan Dubey
11

Presumo que você esteja usando o logback (do arquivo de configuração).

No manual de logback , vejo

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

Talvez isso possa ajudá-lo a alterar o valor?

Raghuram
fonte
10

usando o logback 1.1.3, tive que fazer o seguinte (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
fonte
4

Eu acho que você pode usar o MDC para alterar o nível de log programaticamente. O código abaixo é um exemplo para alterar o nível de log no thread atual. Essa abordagem não cria dependência para a implementação de logback (a API SLF4J contém 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
fonte
3

Conforme apontado por outros, você simplesmente cria mockAppendere depois cria uma LoggingEventinstância que essencialmente escuta o evento de log registrado / acontece dentro mockAppender.

Aqui está como fica em teste:

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"));
}
}
Solução Simples
fonte
0

Eu pareço ter sucesso fazendo

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

Então, para obter um registro detalhado do netty, o seguinte foi feito

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