利用actuator动态设置SpringBoot的logback框架日志级别

前言

日志是用于排查问题的利器,但是在线上打赢的日志太多, 会影响性能,但是打印的日志太少,出问题的时候又难于排查,于是接受到了动态修改log级别的需求。

主要使用的环境

  • spring-boot 1.4.3 作为微服务框架
  • log使用的是logback作为日志框架
  • spring-boot-actuator作为监控组件来进行检测

前提

关于spring-boot、logback和spring-boot-actuator的介绍就不一一介绍了。

技术选型

  1. 由于日志框架是logback,所以必须要使用logback所支持的方法,稍微插了一下,主要有几种方式:scan配置文件、jmx远程控制、api进行修改。
  2. 由于使用spring-boot-logging组件,所依赖的配置文件logback-spring.xml包是存在于jar包内部的,要修改这个文件,感觉复杂度很大,而且出了问题不好排查,所以scan配置文件这个方案被否决了。
  3. 由于是微服务,每次都通过jmx连接,然后在界面上进行修改,感觉步骤太繁琐,容易出问题,所以jmx远程控制也被否决了。
  4. 如果可以通过一个restful请求,然后调用api进行修改,感觉比较简单和方便,所以准备使用这个方案。

解决步骤

  1. 项目中有使用spring-boot-actuator来作为监控组件来进行检测,为了不对默认暴露的接口产生影响,所以准备自定义一个Endpoint来完成接受请求的入口。

首先创建一个Endpoint类

public class ChangeLogEndpoint extends AbstractEndpoint {

  public ChangeLogEndpoint() {
    super("changeLog", true, true);
  }

  @Override
  public Boolean invoke() {
    try {
      log.info("change log no effect, please use changeLog/{level}?package=xxx.yyy.zzz");
      return true;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return false;
    }
  }
}
  1. 但是这个类不能接受参数,所以没办法满足修改的需求,所以需要暴露更多的内容。紧接着创建一个ChangeLogMvcEndpoint类,目的是接受更多的参数,比如指定修改的包名以及修改到的级别
public class ChangeLogMvcEndpoint extends EndpointMvcAdapter {

  public ChangeLogMvcEndpoint(ChangeLogEndpoint delegate) {
    super(delegate);
  }

  @RequestMapping(value = "/{level}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
  @ResponseBody
  public Object changeLog(
      @RequestParam(name = "package")
          String packageName,
      @PathVariable String level) {
    log.info("change log package name is {}, log level is {}", packageName, level);
    Map result = Maps.newHashMap();
    boolean success = true;
    try {
          // change log level
      }
    } catch (Exception e) {
      success = false;
    }

    result.put("success", String.valueOf(success));
    return result;
  }
}

这样就可以接受到 /{level}?package="com.xxx" 的请求参数。

  1. 利用logback的api来进行修改
LogbackLoggingSystem logbackLoggingSystem = new LogbackLoggingSystem(this.getClass().getClassLoader());
logbackLoggingSystem.setLogLevel(level);
  1. 然后把创建的Endpoint和MvcEndpoint进行注册,加入初始化bean的类
@Configuration
@ConditionalOnWebApplication
public class ChangeLogConfiguration {

  @Bean
  @ConditionalOnMissingBean
  public ChangeLogEndpoint changeLogEndpoint() {
    return new ChangeLogEndpoint();
  }

  @Bean
  @ConditionalOnBean(ChangeLogEndpoint.class)
  @ConditionalOnEnabledEndpoint(value = "changeLog")
  public ChangeLogMvcEndpoint changeLogMvcEndpoint(ChangeLogEndpoint delegate) {
    return new ChangeLogMvcEndpoint(delegate);
  }
}

然后通过在 spring.factories 进行指定上面的这个配置类,就可以完成bean的注册,这样actuator就把path暴露出来了,所以在启动的时候会有暴露path的日志打印: /admin/changeLog/{level}/admin/changeLog , 我们就可以从path知道,前面的路径是MvcEndpoint所暴露的,可以接受参数完成我们的需求,后面的路径是Endpoint所暴露的,并没有任何实际的效果。

  1. 最后我们就可以通过请求这个path来完成日志级别的动态修改了。
127.0.0.1:8088/admin/changeLog/debug?package=com.dragon.study'
  1. 自从spring-boot的1.5.x版本开始, 官方原始的actuator增加了支持动态修改日志级别的feature,具体的原理和本篇文章是类似的。如果不想自己实现,可以通过把spring-boot升级到1.5+

你可能感兴趣的:(利用actuator动态设置SpringBoot的logback框架日志级别)