如何以编程方式更改 root 日志记录级别以进行 logback

Posted

技术标签:

【中文标题】如何以编程方式更改 root 日志记录级别以进行 logback【英文标题】:How to change root logging level programmatically for logback 【发布时间】:2011-04-19 18:19:53 【问题描述】:

我有以下 logback.xml 文件:

<configuration debug="true"> 

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
<encoder>
  <pattern>%dHH:mm:ss.SSS [%thread] %-5level %logger36 - %msg%n</pattern>
</encoder>
</appender>

<root level="debug">
  <appender-ref ref="STDOUT" />
</root>
</configuration>

现在,在发生特定事件时,我想以编程方式将根记录器的级别从 debug 更改为 error。我不能使用变量替换,我必须在代码中这样做。

怎么做?谢谢。

【问题讨论】:

【参考方案1】:

试试这个:

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

Logger root = (Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);

请注意,您还可以告诉 logback 定期扫描您的配置文件,如下所示:

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 

【讨论】:

需要注意的是,slf4j 的目的是抽象出日志框架,但是第一种方法通过直接引用日志框架来消除它。 如果你这样做并得到一个 ClassCastException,很可能是由于类路径上有多个 SLF4J 绑定。日志输出将指出这一点以及存在哪些绑定,以便您确定需要排除哪些绑定。 Slf4j 提供了一个 API,以便库可以使用应用程序开发人员想要的任何日志框架记录应用程序日志。关键是应用程序开发人员仍然必须选择一个日志框架,依赖它并配置它。像 dogbane 一样配置记录器并不违反这个原则。 @JohnWiseman 如果你想配置它,那么你必须配置它somewhere。由于 slf4j 在这方面没有提供任何东西,总会有一些依赖于底层记录器的东西。无论是一段代码还是一个配置文件。 +++ 如果它应该按照 OP 的要求以编程方式完成,那么你别无选择。尽管如此,优势仍然存在: 1. 只有一小部分代码依赖于具体的记录器引擎(并且可以编写它以便它可以处理不同的实现)。 2. 您也可以配置使用其他记录器编写的库。 为什么对于像日志记录这样的东西来说必须如此复杂,不应该有直接的方法来更改代码本身的日志记录级别。遵循特定库的原则如何优先于其简单性?来自 Python 世界,我不明白为什么像 Logging 这样简单的东西在 Java/Scala 中如此复杂。【参考方案2】:

我假设您正在使用 logback(来自配置文件)。

来自logback manual,我知道了

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

也许这可以帮助您更改值?

【讨论】:

【参考方案3】:

使用 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】:

正如其他人所指出的,您只需创建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"));


【讨论】:

【参考方案5】:

我认为您可以使用 MDC 以编程方式更改日志记录级别。下面的代码是更改当前线程的日志记录级别的示例。这种方法不会创建对 logback 实现的依赖(SLF4J API 包含 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");

【讨论】:

【参考方案6】:

我好像成功了

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

【讨论】:

org.slf4j.impl.SimpleLogger 在当前 v1.7.30 中没有 setLevel()【参考方案7】:

这是一个控制器

@RestController
@RequestMapping("/loggers")
public class LoggerConfigController 

private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(PetController.class);

@GetMapping()
public List<LoggerDto> getAllLoggers() throws CoreException 
    
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
    
    List<Logger> loggers = loggerContext.getLoggerList();
    
    List<LoggerDto> loggerDtos = new ArrayList<>();
    
    for (Logger logger : loggers) 
        
        if (Objects.isNull(logger.getLevel())) 
            continue;
        
        
        LoggerDto dto = new LoggerDto(logger.getName(), logger.getLevel().levelStr);
        loggerDtos.add(dto);
    
    
    if (LOGGER.isDebugEnabled()) 
        LOGGER.debug("All loggers retrieved. Total of  loggers found", loggerDtos.size());
    
    
    return loggerDtos;


@PutMapping
public boolean updateLoggerLevel(
        @RequestParam String name, 
        @RequestParam String level
)throws CoreException 
    
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
    
    Logger logger = loggerContext.getLogger(name);
    
    if (Objects.nonNull(logger) && StringUtils.isNotBlank(level)) 
        
        switch (level) 
            case "INFO":
                logger.setLevel(Level.INFO);
                LOGGER.info("Logger [] updated to []", name, level);
                break;
                
            case "DEBUG":
                logger.setLevel(Level.DEBUG);
                LOGGER.info("Logger [] updated to []", name, level);
                break;
                
            case "ALL":
                logger.setLevel(Level.ALL);
                LOGGER.info("Logger [] updated to []", name, level);
                break;
                
            case "OFF":
            default: 
                logger.setLevel(Level.OFF);
                LOGGER.info("Logger [] updated to []", name, level);
        
    
    
    return true;

【讨论】:

以上是关于如何以编程方式更改 root 日志记录级别以进行 logback的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 Bean 级别控制 cayenne 日志记录 - 以停止记录密码

如何以编程方式更改 UICollectionView 页脚视图高度

如何配置 WildFly 8.2.0 日志记录以仅在调试级别显示应用程序

如何以编程方式记录 PerformanceCounter

如何将python日志记录级别名称转换为整数代码

如何以编程方式在自定义列表视图中进行更改以立即反映?