log4j日志打印级别动态调整
1,为什么日志打印级别要动态调整?
随着项目越来越大,访问量也越来越高,遇到问题时想要排查,可是日志一打开却刷的太快太快,不好排查问题,有的时候甚至因为短时间打印日志太多,严重影响了性能,这个时候日志的打印级别的动态调整就相当有必要了,在不重启项目的情况,不改动代码的情况下,通过Apollo动态配置就可以通过配置动态的调整日志的级别,可以精确到配置具体的类的日志打印级别。
2,动态调整的方案
大致思路为在springboot项目启动之后,读取Apollo配置文件里的配置文件,总共有两个,一个是总的日志级别,一个是单独的类的配置,然后设置总的之后再设置具体到类的自定义的,同时注册一个监听器监听两个文件的变化,一旦有变化就重新设置一遍,是不是很简单呢?
具体代码如下,将该类在启动时注册入spring容器就行。值得注意的是该类中的initCustomClass()方法,该方法是因为有很多类没有在springboot启动时没有初始化,那么也就没有注册入
LoggerContext的属性中,所以是无法设置的,通过手动初始化该类的形式来初始化之后重新设置一遍。在详细的配置文件中是支持正则表达式来匹配的。
@Service
@Slf4j
public class LoggerConfiguration implements ConfigChangeListener, ApplicationListener<ContextRefreshedEvent> { private static final String LOGGER_LEVEL = "logger_level"; private static final String LOGGER_LEVEL_DETAIL = "logger_level_detail"; private static final String DEFAULT_LEVEL = "error"; private static final String INFO_LEVEL = "info"; private Config applicationConfig; public LoggerConfiguration(Config applicationConfig) {
this.applicationConfig = applicationConfig;
} @Override
public void onChange(ConfigChangeEvent changeEvent) {
if (changeEvent.changedKeys().contains(LOGGER_LEVEL)) {
String newValue = changeEvent.getChange(LOGGER_LEVEL).getNewValue();
try {
log.info("update rootLoggerLevel {}", newValue);
setRootLoggerLevel(newValue);
} catch (Exception e) {
log.error("loggerLevel onChange failed {}", ExceptionUtil.stacktraceToString(e));
}
}
if (changeEvent.changedKeys().contains(LOGGER_LEVEL_DETAIL)) {
String newValue = changeEvent.getChange(LOGGER_LEVEL_DETAIL).getNewValue();
try {
log.info("update loggerLevel detail {}", newValue);
parseLoggerConfig(newValue);
} catch (Exception e) {
log.error("loggerLevel detail onChange failed {}", ExceptionUtil.stacktraceToString(e));
}
}
} @Override
public void onApplicationEvent(ContextRefreshedEvent event) {
try {
// 初始化风控监听action配置
String level = applicationConfig.getProperty(LOGGER_LEVEL, DEFAULT_LEVEL);
log.info("init root loggerLevel {}", level);
setRootLoggerLevel(level);
// 注册配置监听
applicationConfig.addChangeListener(this);
} catch (Exception e) {
log.error("loggerLevel init failed {}", ExceptionUtil.stacktraceToString(e));
}
} /**
* 将未注册进日志容器的类处初始化
*
* @param className
*/
private boolean initCustomClass(String className) {
try {
Class.forName(className);
return true;
} catch (Exception e) {
log.error("init {} failed", className);
return false;
}
} private void setRootLoggerLevel(String level) {
try {
Level newLevel = Level.valueOf(level);
LoggerContext logContext = LoggerContext.getContext(false);
Configuration configuration = logContext.getConfiguration();
LoggerConfig loggerConfig = configuration.getRootLogger();
loggerConfig.setLevel(newLevel);
logContext.updateLoggers();
//update后会覆盖定制化的
setLoggerLevel(this.getClass().getName(), INFO_LEVEL);
reConfig();
log.info("update rootLoggerLevel {}", level);
} catch (Exception e) {
log.error("setRootLoggerLevel failed {}", ExceptionUtil.stacktraceToString(e));
} } private void setLoggerLevel(String name, String level) {
try {
Level newLevel = Level.valueOf(level);
LoggerContext logContext = LoggerContext.getContext(false); //是否没有匹配到
boolean flag = false; if (logContext.hasLogger(name)) {
//精确匹配
Logger logger = logContext.getLogger(name);
logger.setLevel(newLevel);
log.info("update {} logger level {}", name, level);
flag = true;
} else {
//正则匹配
Collection<Logger> loggers = logContext.getLoggers();
for (Logger logger : loggers) {
if (Pattern.matches(name, logger.getName())) {
logger.setLevel(newLevel);
log.info("update {} logger level {}", name, level);
flag = true;
}
}
} //该类未注册就注册,注册失败那么也就不再继续设置
if (!flag && initCustomClass(name)) {
//初始化未注册的类
setLoggerLevel(name, level);
} } catch (Exception e) {
log.error("setLoggerLevel failed {}", ExceptionUtil.stacktraceToString(e));
}
} private void reConfig() {
String detail = applicationConfig.getProperty(LOGGER_LEVEL_DETAIL, "");
if (StringUtils.isNotEmpty(detail)) {
parseLoggerConfig(detail);
}
} private void parseLoggerConfig(String value) {
Map<String, String> config = JSON.parseObject(value, Map.class);
if (config == null) {
return;
}
config.forEach((k, v) -> setLoggerLevel(k, v));
} public void setApplicationConfig(Config applicationConfig) {
this.applicationConfig = applicationConfig;
}
}
log4j日志打印级别动态调整的更多相关文章
- springboot2整合logback.xml动态修改日志打印级别
今天找bug烦到了,生产上的日志级别不能修改,非常不利于排查问题,于是想到了动态修改日志打印级别, 因为上一周把项目升级成springboot2,并且使用logback.xml管理日志打印,所以修改也 ...
- Nginx 改变错误日志打印级别
Nginx 改变错误日志打印级别 user root;worker_processes 2; worker_rlimit_nofile 10240;error_log logs/nginx_err ...
- Storm中log4j日志打印不出来的解决办法
使用storm命令启动JAVA进程的时候,发现log4j日志打印不出来,咋办呢? 解决办法如下(亲测): 删除strom/lib目录下的log4j-over-slf4j-1.6.6.jar strom ...
- log4j日志输出级别变更
1. 现阶段log4j日志输出配置 示例:基础服务日志配置 #DEBUG < INFO < WARN < ERROR < FATAL\u65E5\u5FD7\u7684\u ...
- log4j日志打印的配置文件简单使用
log4j.properties #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,c ...
- scarpy设置日志打印级别和存储位置
在settings.py中配置 日志级别设置 LOG_LEVEL = 'ERROR' # 当LOG_LEVEL设置为ERROR时,在进行日志打印时,只是打印ERROR级别的日志 日志存储设置 LOG_ ...
- log4j日志输出级别高低
Log4j是Apache的开源项目一个功能强大的日志组件,提供方便的日志记录.日志记录器(Logger)是日志处理的核心组件Log4j建议只使用四个级别,优先级从高到低分别是FATAL, ERROR. ...
- java项目log4j日志打印配置
#定义输出级别和输出平台 添加DEBUG表示打印sql 语句 log4j.rootLogger=DEBUG,INFO,ERROR,stdout,R log4j.category.org.spring ...
- Tomcat - 怎么控制某个类或者包下的日志打印级别
问题与分析 Tomcat是使用自己的日志实现tomcat-juli.jar来打印日志信息的,日志会被打印到catalina.out里,除去你在项目里自己使用的日志框架外,由System.out,Sys ...
随机推荐
- 恕我直言你可能真的不会java第8篇-函数式接口
一.函数式接口是什么? 所谓的函数式接口,实际上就是接口里面只能有一个抽象方法的接口.我们上一节用到的Comparator接口就是一个典型的函数式接口,它只有一个抽象方法compare. 只有一个抽象 ...
- 半导体质量管理_SQM 供应商质量管理
供应链上的质量保证 SPACE的此附加组件可帮助您与全球生产现场的供应商和分包商更紧密地合作.基于电子分析证书(eCOA,电子分析证书),您可以为整个供应链实施具有约束力的质量标准,并可以对其进行验证 ...
- MySQL Error (Always Continue)
MySQL Error (Always Continue) 其实大部分error在网上都有对应的解决办法,尤其是对Mysql这种使用范围很广的技术.自己就不为每个error单独发布博客了,仅在这里 ...
- 读CSAPP第二章的收获
一:一道很有意思的位运算题目:你只有两种操作 bis(x, y): 在y为1的每个位置上,将x的对应的位设为1bic(x, y): 在y为1的每个位置上,将x的对应的位设为0 简单的化简一下bis(x ...
- NEST教程系列:推断索引名
NEST教程系列:三种推断索引名写法 目录 NEST教程系列:三种推断索引名写法 连接时设置默认索引 设置 .NET 类映射索引名 在创建请求的时候直接显式指定索引名 总结 连接时设置默认索引 构建 ...
- 大话一个CPU(沙子是如何影响未来的)
大话一个CPU(沙子是如何影响未来的) CPU是个啥? 先大体上了解一下 中央处理器 (英语:Central Processing Unit,缩写:CPU),是计算机的主要设备之一,功能主要是解释计算 ...
- 错题重错之枪战Maf
题目描述 有 n 个人,用1∼n 进行编号,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人 ...
- java 基础(三) 搭建Java编译环境(树莓派)
安装需求1.JDK的安装2.PI4J的安装 JDK的安装1.首先到JDK的官网:https://www.oracle.com/technetwork/java/javase/downloads/ind ...
- Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇 本文主要内容: 1:spring cloud整合Eureka总结 本文是由凯哥(凯哥Java:kagejava ...
- 使用MapReduce运行WordCount案例
@ 目录 一.准备数据 二.MR的编程规范 三.编程步骤 四.编写程序 Mapper程序解读 一.准备数据 注意:准备的数据的格式必须是文本,每个单词之间使用制表符分割.编码必须是utf-8无bom ...