前言
日志是用于排查问题的利器,但是在线上打赢的日志太多, 会影响性能,但是打印的日志太少,出问题的时候又难于排查,于是接受到了动态修改log级别的需求。
主要使用的环境
- spring-boot 1.4.3 作为微服务框架
- log使用的是logback作为日志框架
- spring-boot-actuator作为监控组件来进行检测
前提
关于spring-boot、logback和spring-boot-actuator的介绍就不一一介绍了。
技术选型
- 由于日志框架是logback,所以必须要使用logback所支持的方法,稍微插了一下,主要有几种方式:scan配置文件、jmx远程控制、api进行修改。
- 由于使用spring-boot-logging组件,所依赖的配置文件logback-spring.xml包是存在于jar包内部的,要修改这个文件,感觉复杂度很大,而且出了问题不好排查,所以scan配置文件这个方案被否决了。
- 由于是微服务,每次都通过jmx连接,然后在界面上进行修改,感觉步骤太繁琐,容易出问题,所以jmx远程控制也被否决了。
- 如果可以通过一个restful请求,然后调用api进行修改,感觉比较简单和方便,所以准备使用这个方案。
解决步骤
- 项目中有使用spring-boot-actuator来作为监控组件来进行检测,为了不对默认暴露的接口产生影响,所以准备自定义一个Endpoint来完成接受请求的入口。
首先创建一个Endpoint类
public class ChangeLogEndpoint extends AbstractEndpoint<Boolean> {
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;
}
}
}
- 但是这个类不能接受参数,所以没办法满足修改的需求,所以需要暴露更多的内容。紧接着创建一个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<String, String> 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"
的请求参数。
- 利用logback的api来进行修改
LogbackLoggingSystem logbackLoggingSystem = new LogbackLoggingSystem(this.getClass().getClassLoader());
logbackLoggingSystem.setLogLevel(level);
- 然后把创建的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所暴露的,并没有任何实际的效果。
- 最后我们就可以通过请求这个path来完成日志级别的动态修改了。
127.0.0.1:8088/admin/changeLog/debug?package=com.dragon.study'
- 自从spring-boot的1.5.x版本开始, 官方原始的actuator增加了支持动态修改日志级别的feature,具体的原理和本篇文章是类似的。如果不想自己实现,可以通过把spring-boot升级到1.5+