求求你,别再随便打日志了,教你动态修改日志级别!
共 11844字,需浏览 24分钟
·
2020-12-30 17:38
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerService.class);
public void test(){
LOGGER.info("hollis log test");
}
[arthas@2062]$ logger -n org.springframework.web
name org.springframework.web
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel INFO
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
[arthas@2062]$ logger --name ROOT --level debug
update logger level success.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.springframework.boot.logging.LoggingSystem.ROOT_LOGGER_NAME;
/**
* 日志级别设置服务类
*
* @author Hollis
*/
public class LoggerLevelSettingService {
@Autowired
private LoggingSystem loggingSystem;
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerLevelSettingService.class);
public void setRootLoggerLevel(String level) {
LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(ROOT_LOGGER_NAME);
if (loggerConfiguration == null) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("no loggerConfiguration with loggerName " + level);
}
return;
}
if (!supportLevels().contains(level)) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("current Level is not support : " + level);
}
return;
}
if (!loggerConfiguration.getEffectiveLevel().equals(LogLevel.valueOf(level))) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("setRootLoggerLevel success,old level is '" + loggerConfiguration.getEffectiveLevel()
+ "' , new level is '" + level + "'");
}
loggingSystem.setLogLevel(ROOT_LOGGER_NAME, LogLevel.valueOf(level));
}
}
private ListsupportLevels() {
return loggingSystem.getSupportedLogLevels().stream().map(Enum::name).collect(Collectors.toList());
}
}
beforeInitialize方法:日志系统初始化之前需要处理的事情。抽象方法,不同的日志架构进行不同的处理
initialize方法:初始化日志系统。默认不进行任何处理,需子类进行初始化工作
cleanUp方法:日志系统的清除工作。默认不进行任何处理,需子类进行清除工作
getShutdownHandler方法:返回一个Runnable用于当jvm退出的时候处理日志系统关闭后需要进行的操作,默认返回null,也就是什么都不做
setLogLevel方法:抽象方法,用于设置对应logger的级别
/**
* 执行LoggingSystem初始化的前置操作
*/
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
//获取LoggingSystem的真实实现,
// 此处会根据不同的日志框架获取不同的实现,
// logback :LogbackLoggingSystem
// log4j2:Log4J2LoggingSystem
// javalog:JavaLoggingSystem
this.loggingSystem = LoggingSystem
.get(event.getSpringApplication().getClassLoader());
//执行beforeInitialize方法完成初始化前置操作
this.loggingSystem.beforeInitialize();
}
/**
* the config of logger
*
* @author Hollis
*/
public class LoggerConfig {
/**
* the name of the logger
*/
private String loggerName;
/**
* the log level
*
* @see LogLevel
*/
private String level;
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
}
接着提供方法动态修改日志级别:
public void setLoggerLevel(List configList) {
Optional.ofNullable(configList).orElse(Collections.emptyList()).forEach(
config -> {
LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(config.getLoggerName());
if (loggerConfiguration == null) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("no loggerConfiguration with loggerName " + config.getLoggerName());
}
return;
}
if (!supportLevels().contains(config.getLevel())) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("current Level is not support : " + config.getLevel());
}
return;
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("setLoggerLevel success for logger '" + config.getLoggerName() + "' ,old level is '"
+ loggerConfiguration.getEffectiveLevel()
+ "' , new level is '" + config.getLevel() + "'");
}
loggingSystem.setLogLevel(config.getLoggerName(), LogLevel.valueOf(config.getLevel()));
}
);
}
[{'loggerName':'com.hollis.degradation.core.logger.LoggerLevelSettingService','level':'WARN'}]
[{'loggerName':'com.hollis.degradation.core.logger','level':'WARN'}]
[
{'loggerName':'com.hollis.degradation.core.logger','level':'WARN'}
,{'loggerName':'com.hollis.degradation.core.logger.LoggerLevelSettingService','level':'INFO'}
]
private static final Logger LOGGER1 = LoggerFactory.getLogger(LoggerLevelSettingService.class);
private static final Logger LOGGER2 = LoggerFactory.getLogger(TestService.class);
private static final Logger LOGGER3 = LoggerFactory.getLogger(DebugService.class);
public void setDegradationLoggerLevel(String level) {
LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(
this.getClass().getName());
if (loggerConfiguration == null) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("no loggerConfiguration with loggerName " + level);
}
return;
}
if (!supportLevels().contains(level)) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("current Level is not support : " + level);
}
return;
}
if (!loggerConfiguration.getEffectiveLevel().equals(LogLevel.valueOf(level))) {
loggingSystem.setLogLevel(this.getClass().getName(), LogLevel.valueOf(level));
}
}
/**
* 降级开关注册器
*
* @author Hollis
*/
public class DegradationSwitchInitializer implements Listener, InitializingBean {
//从配置项中读取应用名,方便注册到配置中心
@Value("${project.name}")
private String appName;
@Autowired
private LoggerLevelSettingService loggerLevelSettingService;
//配置中心值发生变化会自动回调该方法
@Override
public void valueChange(String appName, String nameSpace, String name,
String value) {
if (name.equals(rootLogLevel.name())) {
loggerLevelSettingService.setRootLoggerLevel(value);
}
if (name.equals(logLevelConfig.name())) {
ListloggerConfigs = JSON.parseArray(value, LoggerConfig.class);
loggerLevelSettingService.setLoggerLevel(loggerConfigs);
}
//将降级工具的日志输出级别设置成INFO,保证其日志可以正常输出
loggerLevelSettingService.setDegradationLoggerLevel("INFO");
}
@Override
public void afterPropertiesSet() {
//将服务配置到配置中心
ConfigCenterManager.addListener(this);
ConfigCenterManager.init(appName, DegradationConfig.class);
}
}
/**
* @author Hollis
*/
@Configuration
@ConditionalOnProperty(prefix = "hollis.degradation", name = "enable", havingValue = "true")
public class HollisDegradationAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "project.name")
public LoggerLevelSettingService loggerLevelSettingService() {
return new LoggerLevelSettingService();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(value = LoggerLevelSettingService.class)
public DegradationSwitchInitializer degradationSwitchInitializer() {
return new DegradationSwitchInitializer();
}
}
hollis.degradation.enable = true
project.name = test
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hollis.degradation.starter.autoconfiguration.HollisDegradationAutoConfiguration
1、通用性。要同时可以支持不同的日志框架,客户端使用的日志框架不影响我们的功能,并且客户端不需要关心自己的日志框架的区别。
2、可配置性。可以将配置信息通过外部配置中心推送,可以快速进行调整。
3、易用性。通过封装到SpringBoot Starter中,方便客户端快速接入。
4、无侵入性。框架的使用不应该影响到应用的正常运行。
往期推荐
HTTP/2做错了什么?刚刚辉煌2年就要被弃用了!?
央视曝光:全国第九大电商平台倒了!创始人卷走260亿,1200万人被骗
那个 CEO 写下 70 万行代码的公司,马上要上市了
直面Java第343期:为什么TOMCAT要破坏双亲委派
深入并发第013期:拓展synchronized——锁优化