前言
日志是用于排查問題的利器,但是在線上打贏的日志太多, 會影響性能,但是打印的日志太少,出問題的時候又難于排查,于是接受到了動態修改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+