SpringBoot如何動態(tài)改變?nèi)罩炯墑e
前言
關(guān)于日志級別,大部分項目可能都設(shè)置為info級別,當(dāng)然也可能有一些追求性能或者說包含很多敏感信息的項目直接將級別設(shè)置為warn或者error;這時候如果項目中出現(xiàn)一些未知異常,需要用到很詳細(xì)的日志信息,此時如果項目中沒有動態(tài)改變?nèi)罩炯墑e的機(jī)制,排查問題將很棘手。
日志系統(tǒng)
我們常用的一些日志系統(tǒng)包括:Log4j2、Logback、Java Util Logging;我們想動態(tài)改變?nèi)罩镜募墑e,前提是這些日志系統(tǒng)都支持我們直接設(shè)置日志等級,當(dāng)然這些系統(tǒng)提供了很簡單的接口;
- Log4j2
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false); LoggerConfig loggerConfig = loggerContext.getConfiguration().getLoggers().get("root"); loggerConfig.setLevel(level);
- Logback
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); Logger logger = loggerContext.getLogger("root"); ((ch.qos.logback.classic.Logger) logger).setLevel(level);
- Java Util Logging
Logger logger = Logger.getLogger("root"); logger.setLevel(level);
當(dāng)然除了上面直接設(shè)置日志級別的方式,也有可以動態(tài)加載配置文件的方式,同樣也可以在配置文件中動態(tài)改變?nèi)罩炯墑e,以logback為例:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); File externalConfigFile = new File("logback.xml"); JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); lc.reset(); configurator.doConfigure(externalConfigFileLocation);
上面簡單介紹了一下每種日志系統(tǒng)都是如何去設(shè)置日志級別的,最關(guān)鍵的是設(shè)置完之后,可以實時生效,立馬可以看到我們想要的日志;有了這些下面其實就是通過何種方式去改變?nèi)罩炯墑e的問題了;
如何動態(tài)改變級別
如何去動態(tài)改變級別,最簡單的方式就是對外提供一個接口,給定一個日志級別作為參數(shù)實時變更;或者通過配置中心的方式;另外其實像SpringBoot這些主流的框架本身也提供了動態(tài)修改的功能;下面可以具體看一下是如何實現(xiàn)的,以logback為例;
自定義接口
自定義一個給定日志級別的接口,外部直接通過調(diào)用接口來改變級別:
@RequestMapping(value = "logLevel/{logLevel}") public String changeLogLevel(@PathVariable("logLevel") String logLevel) { try { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); Logger logger = loggerContext.getLogger("root"); ((ch.qos.logback.classic.Logger) logger).setLevel(Level.valueOf(logLevel)); } catch (Exception e) { logger.error("changeLogLevel error", e); return "fail"; } return "success"; }
想要改變?nèi)罩炯墑e直接請求如下地址即可,設(shè)置一個debug的級別:
http://[ip]:[port]/logLevel/debug
這種方式雖然比較簡單,但是如果節(jié)點很多的話,操作起來就很麻煩,當(dāng)然也可以匯總所有節(jié)點路徑,一次操作觸發(fā)所有節(jié)點的請求;其實最好的辦法應(yīng)該是類似發(fā)布訂閱的方式,發(fā)布者會給所有訂閱者都發(fā)送一個更改日志級別的通知,有新的節(jié)點只要成為訂閱者即可,這種方式其實就是現(xiàn)在主流的配置中心的方式。
配置中心
配置中心的目的其實就是把一些會經(jīng)常變動的參數(shù)集中保存起來,某個系統(tǒng)啟動時去配置中心獲取相關(guān)的參數(shù),同時會對這些參數(shù)進(jìn)行監(jiān)聽,后面在配置中心里面改變參數(shù)的值會實時推送給相關(guān)系統(tǒng);這樣系統(tǒng)就可以在不重啟的情況下就更新了配置;
利用現(xiàn)有的一些中間件我們就能很快實現(xiàn)一個配置中心,比如Zookeeper提供了對某個Node進(jìn)行監(jiān)聽的功能,MQ和Redis都有發(fā)布訂閱的功能,所以用來實時推送變更再好不過了;
- Zookeeper方式
可以直接使用PathChildrenCache用來監(jiān)聽子節(jié)點的CHILD_ADDED,CHILD_UPDATED,CHILD_REMOVED事件;這樣如果在Zookeeper服務(wù)端對節(jié)點的值就行更新,客戶端會觸發(fā)以上三個事件:
private void watcherPath(String path) { PathChildrenCache cache = new PathChildrenCache(client, path, true); cache.start(StartMode.POST_INITIALIZED_EVENT); cache.getListenable().addListener(new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { switch (event.getType()) { case CHILD_ADDED: break; case CHILD_UPDATED: String logLevel = new String(event.getData().getData()); //日志級別更新處理 break; case CHILD_REMOVED: break; default: break; } } }); }
- MQ方式
MQ一般都有Queue和Topic方式,Topic方式其實就是訂閱發(fā)布模式,所有的集群節(jié)點可以訂閱某個Topic,這樣發(fā)布端發(fā)送更新日志級別的消息,其他訂閱節(jié)點都能收到:
//日志等級Topic private final String TOPIC = "LOGLEVEL"; private void watcherPaths() throws JMSException { Topic topic = session.createTopic(TOPIC); MessageConsumer consumer = session.createConsumer(topic); consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { TextMessage tm = (TextMessage) message; String logLevel = tm.getText(); //日志級別更新處理 } }); }
- Redis方式
Redis其實除了緩存的功能,也提供了類似MQ的發(fā)布訂閱的模式;集群節(jié)點通過訂閱一個channel,發(fā)布端通過此channel來發(fā)布消息:
private void watcherPaths() throws JMSException { jedis.subscribe(new JedisPubSub() { @Override public void onMessage(String channel, String message) { String logLevel = message; //日志級別更新處理 } },"LOGLEVEL"); }
SpringBoot內(nèi)置
SpringBoot2.0之后可以通過actuator動態(tài)調(diào)整日志級別,主要是通過暴露loggers這個endpoint來實現(xiàn),具體步驟如下:
- 需要引入actuator
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
- 暴露loggers
在application.properties中添加如下配置:
management.endpoints.web.exposure.include=loggers
- 查看日志級別
啟動服務(wù)可以通過:
http://[ip]:[port]/actuator/loggers
查看當(dāng)前項目每個包的日志級別:
{ levels: [ "OFF","ERROR","WARN","INFO","DEBUG","TRACE" ], loggers: { ROOT: { configuredLevel: "INFO", effectiveLevel: "INFO" }, ... }
- 動態(tài)修改日志級別
發(fā)送POST請求到:
http://[ip]:[port]/actuator/loggers/[包路徑]
需要在body中指定configuredLevel參數(shù);
比如修改整個項目日志級別為error:
http://[ip]:[port]/actuator/loggers/root
關(guān)于SpringBoot內(nèi)部是如何實現(xiàn)動態(tài)改變?nèi)罩炯墑e的,可以查看其實現(xiàn)核心類LoggersEndpoint:
@Endpoint(id = "loggers") public class LoggersEndpoint { private final LoggingSystem loggingSystem; @WriteOperation public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) { Assert.notNull(name, "Name must not be empty"); this.loggingSystem.setLogLevel(name, configuredLevel); } ... }
具體通過LoggingSystem來對日志系統(tǒng)動態(tài)改變級別,上面也介紹了主流使用的日志系統(tǒng),SpringBoot也都支持這些系統(tǒng),這是一個抽象類,具體實現(xiàn)類:
- JavaLoggingSystem
- Log4J2LoggingSystem
- LogbackLoggingSystem
- NoOpLoggingSystem
分別對應(yīng)了幾種日志系統(tǒng),這幾個類內(nèi)部其實也是調(diào)用上面介紹的方法去改變?nèi)罩炯墑e,當(dāng)然SpringBoot自動會識別出當(dāng)前使用的是哪個日志系統(tǒng),然后使用哪個LoggingSystem;
總結(jié)
大部分公司其實更多的還是使用配置中心的方式來動態(tài)改變?nèi)罩炯墑e,這種方式更加靈活,而且配置中心已經(jīng)成為很多公司的標(biāo)配組件,不光用來改變?nèi)罩炯墑e,所有有可能改變的參數(shù)都可以使用。
以上就是SpringBoot如何動態(tài)改變?nèi)罩炯墑e的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot 動態(tài)改變?nèi)罩炯墑e的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實現(xiàn)圖片上傳到服務(wù)器并把上傳的圖片讀取出來
在各大網(wǎng)站上都可以實現(xiàn)上傳頭像功能,可以選擇自己喜歡的圖片做頭像,從本地上傳,今天小編給大家分享Java實現(xiàn)圖片上傳到服務(wù)器并把上傳的圖片讀取出來,需要的朋友參考下2017-02-02Java基于ServletContextListener實現(xiàn)UDP監(jiān)聽
這篇文章主要介紹了Java基于ServletContextListener實現(xiàn)UDP監(jiān)聽,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12淺談Java中實現(xiàn)深拷貝的兩種方式—clone() & Serialized
這篇文章主要介紹了Java中實現(xiàn)深拷貝的兩種方式—clone() & Serialized,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03在mybatis執(zhí)行SQL語句之前進(jìn)行攔擊處理實例
本篇文章主要介紹了在mybatis執(zhí)行SQL語句之前進(jìn)行攔擊處理實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04springboot攔截器不攔截靜態(tài)資源,只攔截controller的實現(xiàn)方法
這篇文章主要介紹了springboot攔截器不攔截靜態(tài)資源,只攔截controller的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07