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日志打印级别动态调整的更多相关文章

  1. springboot2整合logback.xml动态修改日志打印级别

    今天找bug烦到了,生产上的日志级别不能修改,非常不利于排查问题,于是想到了动态修改日志打印级别, 因为上一周把项目升级成springboot2,并且使用logback.xml管理日志打印,所以修改也 ...

  2. Nginx 改变错误日志打印级别

    Nginx 改变错误日志打印级别 user  root;worker_processes  2; worker_rlimit_nofile 10240;error_log logs/nginx_err ...

  3. Storm中log4j日志打印不出来的解决办法

    使用storm命令启动JAVA进程的时候,发现log4j日志打印不出来,咋办呢? 解决办法如下(亲测): 删除strom/lib目录下的log4j-over-slf4j-1.6.6.jar strom ...

  4. log4j日志输出级别变更

    1.   现阶段log4j日志输出配置 示例:基础服务日志配置 #DEBUG < INFO < WARN < ERROR < FATAL\u65E5\u5FD7\u7684\u ...

  5. log4j日志打印的配置文件简单使用

    log4j.properties #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,c ...

  6. scarpy设置日志打印级别和存储位置

    在settings.py中配置 日志级别设置 LOG_LEVEL = 'ERROR' # 当LOG_LEVEL设置为ERROR时,在进行日志打印时,只是打印ERROR级别的日志 日志存储设置 LOG_ ...

  7. log4j日志输出级别高低

    Log4j是Apache的开源项目一个功能强大的日志组件,提供方便的日志记录.日志记录器(Logger)是日志处理的核心组件Log4j建议只使用四个级别,优先级从高到低分别是FATAL, ERROR. ...

  8. java项目log4j日志打印配置

    #定义输出级别和输出平台  添加DEBUG表示打印sql 语句 log4j.rootLogger=DEBUG,INFO,ERROR,stdout,R log4j.category.org.spring ...

  9. Tomcat - 怎么控制某个类或者包下的日志打印级别

    问题与分析 Tomcat是使用自己的日志实现tomcat-juli.jar来打印日志信息的,日志会被打印到catalina.out里,除去你在项目里自己使用的日志框架外,由System.out,Sys ...

随机推荐

  1. 入门大数据---Redis集群分布式学习

    Redis是什么? 官方介绍: Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如 字符串(strings), 散列( ...

  2. dart快速入门教程 (8)

    9.dart中的库 9.1.自定义库 自定义库我们在前面已经使用过了,把某些功能抽取到一个文件,使用的时候通过import引入即可 9.2.系统内置库 以math库为例: import "d ...

  3. 【秒懂Java】【第1章_初识Java】04_学习资料

    为了学到更多的新知识,我们经常会去网上搜索各种学习资料.或者,在学习.工作过程中遇到了解决不了的问题,我们也会去网上搜索答案(比如百度.谷歌一下).这篇文章,主要想跟大家聊聊关于学习资料的选择. 建议 ...

  4. JavaScript中数组去重的几种方法

    JavaScript中数组去重的几种方法 正常情况下,数据去重的工作一般都是由后端同事来完成的,但是前端也要掌握好处理数据的能力,万一去重的工作交给我们大前端处理,我们也不能怂呀.现在我总结了一些去重 ...

  5. Java源码详解系列(十)--全面分析mybatis的使用、源码和代码生成器(总计5篇博客)

    简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository ...

  6. Java实现 第十一届蓝桥杯——走方格(渴望有题目的大佬能给小编提供一下题目,讨论群:99979568)

    走方格 问题描述在平面上有一些二维的点阵. 这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第1 至第 m 列,每一个点可以用行号和列号来表示. 现在有个人站在第 ...

  7. C++栈(stack)、队列(queue)、链表(list)的常用函数

    C++队列Queue是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构.1.back() 返回一个引用,指向最后一个元素2.empty() 如果队列空则返回真3.front() 返回第一 ...

  8. 简单的JdbcUtil 类

    import java.sql.*; /** JDBC工具类 */ public class JdbcUtil { /** * 获取数据库连接对象并返回 * * @return Connection对 ...

  9. cmake的下载和安装

    背景: 最近迷上了 vscode 编辑器, 快速便捷,而且插件丰富,使用起来很爽.既然这样,本身游戏也是用 mingw 加 cygwin 开发的, 可以配置一下,开搞. 实操: 1.登陆cmake官网 ...

  10. 新手用Python运行selenium的常见问题

    1.更换Python版本 打开pycharm,点击 file——setting——project项目名——project Interpreter,点击右侧的设置,如下图 选择新Python版本的安装路 ...