日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

求你了,别再随便打日志了,教你动态修改日志级别!

發布時間:2025/3/16 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 求你了,别再随便打日志了,教你动态修改日志级别! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

GitHub 19k Star 的Java工程師成神之路,不來了解一下嗎!

之前寫過一篇文章《明明有自動清理,日志還是把我的服務干爆了!》,介紹過一次大促故障,是因為日志量激增,導致服務器差點掛掉。

在那次問題發生之后,我開發了一個簡單的日志降級的小工具,通過配置的方式,動態推送日志級別,動態修改線上的日志輸出級別。并且把這份配置的修改配置到我們的預案平臺上,大促期間進行定時或者緊急預案處理。

那么,這篇文章就來簡單介紹下思路以及代碼實現。

日志級別

在開始正文前簡單介紹下日志級別,不同的日志框架支持不同的日志級別,其中比較常見的就是Log4j和Logback。

在Log4j中支持8種日志級別,優先級從高到低依次為:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

Logback中支持7種日志級別,優先級從高到低分別是:OFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL。

可以看到常見的ERROR、WARN、INFO、DEBUG,這兩者都是支持的。

所謂設置日志的輸出級別表示的是輸出的日志的最低級別,也就是說,如果我們把級別設置成INFO,那么包括INFO在內以及比INFO優先級高的級別的日志都可以輸出。

無論是Log4j還是Logback,都是通過日志的配置文件來控制日志輸出級別的。這里就不詳述了。

日志框架

上面我們提到了Log4j和Logback,這兩種都是比較常用的日志框架。

但是很多時候,我們在代碼中打印日志并不是直接使用這種日志框架來進行的,而是依賴了一個日志門面來進行的,如slf4j、commons-logging等。

一般最最常用的方法就是通過slf4j提供的LoggerFactory的getLogger來獲取Logger,然后進行日志打印。

private static final Logger LOGGER = LoggerFactory.getLogger(LoggerService.class);public void test(){LOGGER.info("hollis log test"); }

當我們使用LoggerFactory.getLogger方法創建一個Logger對象的時候,會給他傳入一個loggerName,通過這個loggerName來唯一識別一個Logger,如上面的方式就是使用LoggerService這個類的全路徑名作為其loggerName。

loggerName是每一個Logger的配置信息一部分,除此之外還有日志輸出級別等信息。

關于為什么不直接使用log4j和logback打印日志,我在《為什么阿里巴巴禁止工程師直接使用日志系統(Log4j、Logback)中的 API》中分析過。

Arthas改變日志級別

在開始介紹代碼實現之前,先介紹一個工具,也可以幫助我們的動態修改日志級別。

那就是阿里開源的神器——Arthas (https://arthas.aliyun.com/doc/ )。

Arthas提供了一個logger命令,這個命令可以查看和更新logger信息,包括日志級別。

查看指定名字的logger信息

[arthas@2062]$ logger -n org.springframework.webname org.springframework.webclass ch.qos.logback.classic.LoggerclassLoader sun.misc.Launcher$AppClassLoader@2a139a55classLoaderHash 2a139a55level nulleffectiveLevel INFOadditivity truecodeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar

更新logger level

[arthas@2062]$ logger --name ROOT --level debug update logger level success.

簡單吧,使用一個命令就可以修改機日志級別了。

但是Arthas目前對于集群的支持并不是特別的友好,雖然他支持了通過Arthas Tunnel Server/Client 來遠程管理/連接多個Agent,但是使用起來還不是很方便,并且對于命令的使用要求比較高。

還有就是我們系統通過一個工具,方便我們在大促期間通過預案方式動態調整日志級別,這方面使用arthas就不是很方便了。

代碼實現

我寫的這個工具功能很簡單,就是提供動態修改日志級別的入口,方便用戶動態修改級別。

并且為了方便使用,我將他封裝在一個Spring Boot Starter里面了,還有就是將他直接對接到公司內部的配置中心中,可以方便的通過配置中心一鍵修改日志級別。

首先看下其中最核心的功能,那就是動態修改日志級別的部分,代碼如下:

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 {@Autowiredprivate 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 List<String> supportLevels() {return loggingSystem.getSupportedLogLevels().stream().map(Enum::name).collect(Collectors.toList());} }

以上代碼,就是根據用戶傳入的level的級別,將應用的ROOT日志輸出級別修改掉。

這里面用到了一個關鍵的服務:org.springframework.boot.logging.LoggingSystem

這個服務是SpringBoot對日志系統的抽象,是一個頂層的抽象類。他有很多具體的實現:

通過上圖,我們可以發現目前SpringBoot目前支持4種類型的日志,分別是JDK內置的Log(JavaLoggingSystem)以及Log4j(Log4JLoggingSystem)、Log4j2(Log4J2LoggingSystem)以及Logback(LogbackLoggingSystem)。

LoggingSystem是個抽象類,內部有這幾個方法:

  • beforeInitialize方法:日志系統初始化之前需要處理的事情。抽象方法,不同的日志架構進行不同的處理
  • initialize方法:初始化日志系統。默認不進行任何處理,需子類進行初始化工作
  • cleanUp方法:日志系統的清除工作。默認不進行任何處理,需子類進行清除工作
  • getShutdownHandler方法:返回一個Runnable用于當jvm退出的時候處理日志系統關閉后需要進行的操作,默認返回null,也就是什么都不做
  • setLogLevel方法:抽象方法,用于設置對應logger的級別

SpringBoot在啟動時,會完成LoggingSystem的初始化,這部分代碼是在LoggingApplicationListener中實現的:

/*** 執行LoggingSystem初始化的前置操作*/ private void onApplicationStartingEvent(ApplicationStartingEvent event) {//獲取LoggingSystem的真實實現,// 此處會根據不同的日志框架獲取不同的實現,// logback : LogbackLoggingSystem// log4j2: Log4J2LoggingSystem// javalog: JavaLoggingSystemthis.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());//執行beforeInitialize方法完成初始化前置操作this.loggingSystem.beforeInitialize(); }

有了LoggingSystem以后,我們就可以通過他來動態的修改日志級別。他幫我們屏蔽掉了底層的具體日志框架。

除了支持修改ROOT級別的日志以外,還可以支持用戶自定義的日志的級別修改。

先定義一個LoggerConfig,用來封裝日志的配置:

/*** 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<LoggerConfig> 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()));}); }

以上,根據用戶傳入的LoggerConfig,修改指定的loggerName對應的loggerLevel。至于LoggerLevel是怎么來的,就可以通過配置的方式傳入,比如解析JSON格式的配置或者YML文件等。

如我們可以在配置中心中采用以下配置來控制日志級別,并推送:

[{'loggerName':'com.hollis.degradation.core.logger.LoggerLevelSettingService','level':'WARN'}]

以上配置,會使得loggerName為com.hollis.degradation.core.logger.LoggerLevelSettingService的日志的級別動態修改為WARN,另外,如果配置信息如下:

[{'loggerName':'com.hollis.degradation.core.logger','level':'WARN'}]

那么,就會將以com.hollis.degradation.core.logger這個包路徑下面的所有的類為LoggerName的日志輸出的級別全都動態修改為WARN。

當然,這個配置也支持配置多個Logger的級別,如果是以下配置內容:

[{'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);

那么,配置生效后,會使得以上的LOGGER1的輸出級別為INFO,而LOGGER2和LOGGER3的級別為WARN。

除此以外,上面的日志級別修改,可能會影響到我們自己這個工具本身的日志輸出,所以,我們提供了一個方法,可以直接修改我們自己這個日志服務的日志級別:

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));} }

有了以上的LoggerLevelSettingService類以后,基本具備了動態修改日志的能力,接下來就是想辦法通過配置中心動態修改日志級別了。

這里面因為不同的配置中心用法不同,我只是拿我們自己的配置中心簡單舉例:

/*** 降級開關注冊器** @author Hollis*/ public class DegradationSwitchInitializer implements Listener, InitializingBean {//從配置項中讀取應用名,方便注冊到配置中心@Value("${project.name}")private String appName;@Autowiredprivate LoggerLevelSettingService loggerLevelSettingService;//配置中心值發生變化會自動回調該方法@Overridepublic void valueChange(String appName, String nameSpace, String name,String value) {if (name.equals(rootLogLevel.name())) {loggerLevelSettingService.setRootLoggerLevel(value);}if (name.equals(logLevelConfig.name())) {List<LoggerConfig> loggerConfigs = JSON.parseArray(value, LoggerConfig.class);loggerLevelSettingService.setLoggerLevel(loggerConfigs);}//將降級工具的日志輸出級別設置成INFO,保證其日志可以正常輸出loggerLevelSettingService.setDegradationLoggerLevel("INFO");}@Overridepublic void afterPropertiesSet() {//將服務配置到配置中心ConfigCenterManager.addListener(this);ConfigCenterManager.init(appName, DegradationConfig.class);} }

以上,我們實現了監聽配置中心的值的變化,動態修改日志級別。

基本功能就都完成了,接下來可以考慮如何讓其他應用快速接入,那就是定義一個Starter,可以方便快速接入。主要代碼如下:

先定義一個Configuration類:

/*** @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();} }

在這個類里面定義兩個bean,并且bean定義的前提是應用中配置了以下兩個配置項:

hollis.degradation.enable = true project.name = test

接下來就是定一個spring.factories文件,定義內容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hollis.degradation.starter.autoconfiguration.HollisDegradationAutoConfiguration

以上,只需要在需要引入降級工具的應用中,引入我們的這個starter,并且配置兩個配置項即可。

接入后,可以方便的在配置中心中動態修改單機或者集群的日志輸出級別,并且可以在大促期間配置到預案平臺上,通過緊急預案快速執行。

總結

以上,基本實現了很多基本的功能,實現時考慮的因素主要有以下幾個:

  • 1、通用。要同時可以支持不同的日志框架,客戶端使用的日志框架不影響我們的功能,并且客戶端不需要關心自己的日志框架的區別。
  • 2、可配置。可以將配置信息通過外部配置中心推送,可以快速進行調整。
  • 3、易用。通過封裝到SpringBoot Starter中,方便客戶端快速接入。
  • 4、無侵入性。框架的使用不應該影響到應用的正常運行。

當然,這個工具只是我花了幾個小時擼出來的,其中還有很多不足,其實還有很多事情可以優化,比如配置的格式可以支持多種、支持通過EndPoint查看日志配置情況等,這些都還沒有實現。

本文只是提供一個思路,希望大家都能學會用工具化的方式解決日常工作中遇到的問題,學會造輪子。

關于作者:Hollis,一個對Coding有著獨特追求的人,阿里巴巴技術專家,《程序員的三門課》聯合作者,《Java工程師成神之路》系列文章作者。

如果您有任何意見、建議,或者想與作者交流,都可以關注公眾號【Hollis】,直接后臺給我留言。

總結

以上是生活随笔為你收集整理的求你了,别再随便打日志了,教你动态修改日志级别!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩欧av| 欧美日韩一区二区三区四区五区 | 少妇又色又爽又黄的视频 | 台湾swag在线观看 | 蜜臀人妻四季av一区二区不卡 | 久久久久久久影视 | 久久久91精品国产一区二区三区 | 美女屁股眼视频免费 | 丁香综合激情 | 人与禽一级全黄 | 一本大道久久精品 | 色播激情 | 在线免费播放av | 在线视频日韩 | 日韩黄色片 | 爱爱小视频网站 | 国产精品日韩一区二区 | 91在线无精精品白丝 | 欧美xo影院 | 久久加勒比 | 麻豆福利视频 | 一本大道视频 | 日韩午夜片 | 男生和女生操操 | 日韩网站视频 | 国产精品亚洲αv天堂无码 伊人性视频 | 久久久嫩草 | 日本在线播放 | 亚洲一区二区三区婷婷 | 一二三区在线视频 | 毛片毛片毛片毛片毛片毛片毛片毛片毛片毛片 | 国产一二三在线视频 | 中文字幕+乱码+中文字幕一区 | 91黄版 | 国产一区二区三区在线视频观看 | 懂色av蜜臂av粉嫩av | 91porny丨首页入口在线 | 日日做夜夜爽毛片麻豆 | 国产精品传媒在线观看 | 欧美18av| 夜色福利| 97视频总站| 国产尤物在线观看 | 亚洲女人天堂色在线7777 | 成年人在线观看视频 | 日本老年老熟无码 | 午夜欧美激情 | 少妇捆绑紧缚av | 熟女av一区二区三区 | 国产一级久久久久毛片精品 | 丝袜av网站 | 久艹在线播放 | 懂色av中文字幕 | 国产综合视频 | 日韩高清专区 | 色爽交| 97爱爱爱 | 国产一区精品无码 | 裸体黄色片 | 懂色tv | 国产嫩草影视 | 久久爱成人 | 亚洲四虎影院 | 久久久资源 | 亚洲第三色 | 久久久精品影视 | 天堂成人在线视频 | 亚洲成人a∨ | 中文字幕在线日本 | 日本a级片网站 | 深夜影院在线观看 | 国产精品爽爽久久 | 另类男人与善交video | 在线黄色网 | www在线播放 | 欧美精品一区二区三区蜜臀 | 国产视频九色蝌蚪 | 九九热最新网址 | 午夜视频免费在线观看 | 黄色大片aa | 97avcc| 精品人妻大屁股白浆无码 | 亚洲成人tv | wwwww在线观看 | 国产黄大片在线观看画质优化 | 欧美脚交视频 | 中文视频在线 | 欧洲亚洲国产精品 | 国产国产精品 | 2020亚洲天堂| 中韩毛片 | 极品销魂美女一区二区 | 国产影音先锋 | 91视频免费 | 最新在线黄色网址 | 日韩精品在线免费视频 | 久久精品www人人爽人人 | 久久99热久久99精品 | 国产精品最新 |