Как программно изменить корневой уровень ведения журнала для входа

144

У меня есть следующий файл 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>

Теперь, при возникновении определенного события, я хочу программно изменить уровень корневого логгера с отладки на ошибку . Я не могу использовать подстановку переменных, это обязательно сделать в коде.

Как это можно сделать? Спасибо.

Кай Стернад
источник

Ответы:

235

Попробуй это:

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

Обратите внимание, что вы также можете указать logback периодически сканировать ваш файл конфигурации следующим образом:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 
кендырь
источник
64
Следует отметить, что цель slf4j состоит в том, чтобы абстрагировать каркас журналирования, но этот первый метод устраняет это путем прямой ссылки на каркас логирования.
Тим Готье
3
Если вы сделаете это и получите ClassCastException, это, скорее всего, связано с несколькими привязками SLF4J на пути к классам. Выходные данные журнала будут указывать это и какие привязки присутствуют, чтобы вы могли определить, какие из них нужно исключить.
icfantv
4
Slf4j предоставляет API, так что библиотеки могут регистрировать журналы приложений, используя любую структуру журналов, которую хочет разработчик приложения. Дело в том, что разработчик приложения все еще должен выбрать каркас журнала, зависеть от него и настроить его. Конфигурирование логгера так же, как и собачьей, не нарушает этот принцип.
Макс
4
@JohnWiseman Если вы хотите, чтобы он был настроен, то вы должны настроить его где-нибудь . Поскольку slf4j ничего не предлагает в этом отношении, всегда будет что-то зависеть от основного регистратора. Будь то кусок кода или файл конфигурации. +++ Если это должно быть сделано программно в соответствии с запросом OP, то у вас нет выбора. Тем не менее, преимущества остаются: 1. Только крошечная часть кода зависит от конкретного механизма ведения журнала (и он может быть написан так, чтобы он мог обрабатывать различные реализации). 2. Вы также можете настроить библиотеки, написанные с помощью других регистраторов.
Maaartinus
4
Почему это должно быть настолько сложно для чего-то вроде ведения журнала, если не должно быть прямого способа изменить уровень ведения журнала в самом коде. Как следование принципу конкретной библиотеки превалирует над ее простотой? Исходя из мира Python, я не понимаю, почему такая простая вещь, как Logging, настолько сложна в Java / Scala.
Абхинандан Дубей
11

Я предполагаю, что вы используете logback (из файла конфигурации).

Из руководства пользователя logback я вижу

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

Возможно, это может помочь вам изменить значение?

Raghuram
источник
10

используя logback 1.1.3 мне пришлось сделать следующее (код Scala):

import ch.qos.logback.classic.Logger
import org.slf4j.LoggerFactory    
...
val root: Logger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[Logger]
Тодор Колев
источник
4

Я думаю, что вы можете использовать MDC для программного изменения уровня регистрации. Приведенный ниже код является примером изменения уровня ведения журнала в текущем потоке. Этот подход не создает зависимости от реализации обратного входа (API SLF4J содержит 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");
Сато Юсуке
источник
3

Как отмечают другие, вы просто создаете, mockAppenderа затем создаете LoggingEventэкземпляр, который, по сути, прослушивает зарегистрированное / происходящее событие регистрации mockAppender.

Вот как это выглядит в тесте:

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"));
}
}
Простое решение
источник
0

Кажется, я делаю успехи

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

Затем, чтобы получить подробную регистрацию от netty, следующее сделало это

org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
user7610
источник