的实现简介上一篇文章的性能调优——小日志大坑,在高并发下已经详细介绍过了。日志姿势使用不当可能会导致服务性能急剧下降的问题。文末也给大家留下了解决方案——动态调整日志级别。本文将详细介绍“动态日志”的实现原理和源码。希望大家在以后的生产环境中处理日志问题能够“得心应手”!后台日志的重要性不言而喻。是我们排查问题、解决bug的重要手段之一。但是在高并发环境下,会出现一个悖论:大量打印日志,消耗I/O,CPU占用率高。;减少日志,性能下降,但是排查问题的链接断了。痛点:一方面需要借助日志快速排查问题,另一方面需要兼顾性能。两者能否实现?那么本文的动态日志调整实现就是为了解决这个痛点而构思和开发的。特点低侵入,快速接入:以二方包(jar)的形式介入,只需要配置启用,无业务感,及时响应,按需更改:响应研发不小心在上面打印大量流量ingresslinkINFO日志,可以及时调整日志级别。梯形图配置支持:默认全局设置为底线,也可支持本地Logger释放/限制。人性化操作:具有操作界面,修改方便。以log4j2为例进行说明,其他日志实现类似,参考实现即可。以下是日志干预的配置文件示例://全局参数信息//appender详细配置//配置appender指向以前我们在调整项目的log时,要么把废log删掉代码,或者修改上面的xml配置,针对某个包或者类,用来限制日志级别,然后重新打包部署生效。此时的效率很低,不符合我们的需求。那么如何实现动态调整,首先想到的是调整日志级别后xml是如何生效的呢?xml本身就是一些配置信息。日志的实现类读取xml信息,动态修改日志级别。是不是可以直接在程序中调用log4j内部的封装方法,绕过xml呢?动态调整日志级别源码查看:详细的源码我已经放在了githubdynamic-logger-util中,大家可以自行查看。顺着思路,查看log4j源码后,发现确实可行。下面是调整日志方法的实现代码://获取日志上下文LoggerContextlogContext=LoggerContext.getContext(false);Configurationconfiguration=logContext.getConfiguration();LoggerConfigloggerConfig=configuration.getRootLogger();loggerConfig.setLevel(level);//生效logContext.updateLoggers();获取当前LoggerContext后,再获取配置,当前配置由xml中的配置转换而来,然后获取rootlogger,即对应xml中的配置如下:其中level是我们需要更改的日志级别,可用的日志级别如下(参考org.apache.logging.log4j.Level):OFF,FATAL,ERROR,WARN,INFO,DEBUG,TRACE,ALL;如上我们可以改变全局的日志级别,那么比如我想只改变某个类内的日志级别,我该如何实现呢?LoggerContextlogContext=LoggerContext.getContext(false);if(logContext.hasLogger(name)){//精确匹配Loggerlogger=logContext.getLogger(name);logger.setLevel(newLevel);flag=true;}else{//正则匹配Collectionloggers=logContext.getLoggers();for(Loggerlogger:loggers){if(Pattern.matches(name,logger.getName())){logger.setLevel(newLevel);标志=真;}}}通过获取的logContext获取对应的logger来设置当前类对应的日志级别,对应的程序代码如下://name=com.jifuwei.dynamic.logger.DynamicLoggerConfigurationprivatestaticfinalorg.slf4j.Logger=LoggerFactory.getLogger(DynamicLoggerConfiguration.class);如上,我们已经知道了如何动态修改日志api,那么如何动态触发修改呢?触发配置和触发更新的机制有很多。梳理一下:最简单方便的答案就是配置中心,可以满足我们上面的需求。现在都是微服务,大部分都是通过中心化的配置来通知各个系统信息的变化。配置中心拥有完整的接口和功能,可以满足我们发送实时变化的通知,并且可以灰度部署以减少错误。简直就是动态配置的最佳拍档。配置中心有很多选择。我将以阿波罗为例。演示如何触发日志级别更改。我设计配置key如下://全局控制日志级别key:log_levelval=OFF/FATAL/ERROR/WARN/INFO/DEBUG/TRACE/ALL//本地控制日志级别key:log_level_detailval:{"com.jifuwei.demo.Test1":"ERROR",//每个logger可以配置自己的日志级别"com.jifuwei.demo.Test2":"OFF","com.jifuwei.demo.Test3":"INFO",}key实现如下:publicvoidinit(){//初始化风控监控动作配置Stringlevel=apolloConfig.getProperty(LOGGER_LEVEL,Level.ERROR.name());setRootLoggerLevel(Level.valueOf(level));//注册监听apolloConfig.addChangeListener(this);}publicvoidonChange(ConfigChangeEventchangeEvent){if(changeEvent.changedKeys().contains(LOGGER_LEVEL)){StringnewValue=changeEvent.getChange(LOGGER_LEVEL).getNewValue();尝试{设置(RootLevel.LoggervalueOf(newValue));}catch(Exceptione){log.error("loggerLevelonChangeerror",e);}}如果(changeEvent.changedKeys().contains(LOGGER_LEVEL_DETAIL)){StringnewValue=changeEvent.getChange(LOGGER_LEVEL_DETAIL).getNewValue();试试{parseLoggerConfig(newValue);}catch(Exceptione){log.error("loggerLeveldetailonChangeerror",e);}}}initialization从apolloconfig获取当前的全局日志级别和本地日志级别,然后注册监听器。这时候你只需要在apollo配置界面设置上面的键值,程序就会立即收到更新并重新设置相应的日志级别。本文所有源码都放在github仓库:https://github.com/jifuwei/dynamic-logger-util,可以随时查看/索取/使用,有问题随时提问.综上所述,通过xml修改日志级别跟踪api方法,然后设计找到可用方法后如何触发方法调用。按照这个思路,动态调整日志级别的问题就解决了。当生产中出现大量异常时,可以将日志降级,这样I/O增加就不会导致CPU爆满,导致用户体验卡顿。如果您觉得我分享的内容比较“干”,请点赞、关注、转发。这是对我最大的鼓励,谢谢大家的支持!希望我分享的文章可以帮助到每一位读者!昔日精彩的性能调优——小日志大坑性能优化必不可少——Flink在风控场景下对实时特性的实现。欢迎关注公众号:咕咕鸡技术专栏个人技术博客:https://jifuwei.github.io/