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 ...
随机推荐
- ip修改器
哈哈,算法来源于网络... 源码:http://pan.baidu.com/s/11P0P9 参考:http://bbs.csdn.net/topics/370201571 http://bbs.cs ...
- JavaScript中数组去重的几种方法
JavaScript中数组去重的几种方法 正常情况下,数据去重的工作一般都是由后端同事来完成的,但是前端也要掌握好处理数据的能力,万一去重的工作交给我们大前端处理,我们也不能怂呀.现在我总结了一些去重 ...
- html中绝对路径和相对路径的区别?比较相对路径和绝对路径的优缺点
绝对路径和相对路径的区别? 1. 绝对路径:就是你的文件或目录在硬盘上的真正的路径例如“bg.jpg”这个图片是存放在硬盘的“E:\img”目录下,那么 “bg.jpg”这个图片的绝对路径就是“E:\ ...
- django 后端分页
分页处理脚本: # -*- coding: utf-8 -*- # @Time : 2019-01-22 10:41 # @Author : 小贰 # @FileName: page.py # @fu ...
- 6.26模拟赛(1)总结(T1:信息传递;T2:传染病控制;T3:排列;T4:最大数)
16:33:56 2020-06-26 当然可以先看一下成绩: 非常显然的成绩不能算有多好,当然其实这也可能是假期水课的报应 (额) 但是比我集训前想象的要好一点(集训时想象的是排名前30就可以,嗯 ...
- [Noip2016]蚯蚓 (单调队列)
题干 本题中,我们将用符号[c]表示对c向下取整,例如:[3.0」= [3.1」=[3.9」=3.蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓.蛐蛐国 ...
- Mysql中Union和OR性能对比
博客已搬家,更多内容查看https://liangyongrui.github.io/ Mysql中Union和OR性能对比 在leetcode上看到一篇文章,整理一下 参考:https://leet ...
- django 本地项目部署uwsgi 以及云服务器部署 uwsgi+Nginx+Docker+MySQL主从
一 .django 本地项目部署uwsgi 1 本地部署项目 uwsgi安装测试 通过uwsgi 进行简单部署 安装uwsgi命令:pip install uwsgi -i http://pypi.d ...
- scrapy 基础组件专题(八):scrapy-redis 框架分析
scrapy-redis简介 scrapy-redis是scrapy框架基于redis数据库的组件,用于scrapy项目的分布式开发和部署. 有如下特征: 分布式爬取 您可以启动多个spider工 ...
- Python面试【315+道题】
第一部分 Python基础篇(80题) 为什么学习Python? 通过什么途径学习的Python? Python和Java.PHP.C.C#.C++等其他语言的对比? 简述解释型和编译型编程语言? P ...