自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

日志配置熱更新技術(shù)實(shí)踐

開發(fā) 開發(fā)工具
一個(gè)簡(jiǎn)單的日志配置熱更新嘗試,串聯(lián)起了logback的自定義配置加載原理,apollo的配置中心使用方法和事件監(jiān)聽機(jī)制,以及springboot日志管理和自動(dòng)裝配等知識(shí)點(diǎn),希望大家能從中有所收獲!

 [[281868]]

一 為什么需要服務(wù)日志熱更新?

對(duì)于后端老鳥來說,一定遇到過這樣的場(chǎng)景:

為了排查線上突發(fā)的問題,非常希望能夠全面的看到請(qǐng)求在服務(wù)鏈路上的完整日志輸出;

But,在生產(chǎn)環(huán)境中,為了避免日志打印過量造成磁盤空間浪費(fèi),通常會(huì)將日志級(jí)別設(shè)定在INFO,并關(guān)閉一般情況用不到的日志輸出;

在不重啟服務(wù)的情況下,開啟本已經(jīng)關(guān)閉的業(yè)務(wù)日志輸出,能不能搞的定呢?答案是當(dāng)然沒問題。

二 需求分析

熟悉logback的同學(xué)此時(shí)肯定已經(jīng)想到通過掃描監(jiān)聽logback.xml文件配置變化來實(shí)現(xiàn)日志級(jí)別的調(diào)整,像如下這種方式:

  1. <configuration debug="true" scan="true" scanPeriod="1 seconds"

但通常情況下,你的業(yè)務(wù)服務(wù)是分布式部署的,后端節(jié)點(diǎn)有多臺(tái),如果一臺(tái)臺(tái)的去改,且不說運(yùn)維大哥未必就會(huì)同意給你生產(chǎn)機(jī)器文件的修改權(quán)限,即使可以,這么做未免有些過于“老實(shí)”了;有沒有一種可以集中管理日志配置,修改文件后再逐個(gè)分發(fā)給各節(jié)點(diǎn)的解決方案呢?沿著這個(gè)思路,自然而然就會(huì)聯(lián)想到配置中心,這里,我主要介紹攜程開源的apollo,同類的配置中心產(chǎn)品還有百度Disconf、阿里ACM和Spring Cloud Config,感興趣的自行研究。

三 做實(shí)驗(yàn)

熟悉apollo文件管理的同學(xué)都知道,apollo通過推拉結(jié)合的方式將服務(wù)端存儲(chǔ)的應(yīng)用配置文件緩存到本地是以properties的格式存儲(chǔ)的,如下面所示:

demo+dev+logback.xml.properties

  1. content=<?xml version\="1.0" encoding\="UTF-8"?>\n<configuration debug\="true">\n\t<property name\="encoding" value\="UTF-8"/>\n\n\t<appender name\="STDOUT" class\="ch.qos.logback.core.ConsoleAppender">\n\t\t<encoder class\="ch.qos.logback.classic.encoder.PatternLayoutEncoder">\n\t\t\t<pattern>%d{yyyy-MM-dd HH\:mm\:ss.SSS}|%X{requestId}|[%t] %-5level %logger{50} %line - %m%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<appender name\="FILE" class\="ch.qos.logback.core.rolling.RollingFileAppender">\n\t\t<file>logs/brm.log</file>\n\t\t<encoder class\="ch.qos.logback.classic.encoder.PatternLayoutEncoder">\n\t\t\t<pattern>%d{yyyy-MM-dd HH\:mm\:ss.SSS}|%X{requestId}|%X{requestSeq}|[%t] %-5level %logger{50} %line - %m%n</pattern>\n\t\t</encoder>\n\t\t<rollingPolicy class\="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">\n\t\t\t<fileNamePattern>logs/brm-%d{yyyy-MM-dd-HH}-%i.log</fileNamePattern>>\n\t\t\t<\!--單個(gè)文件切割閾值,超過生成新log文件-->\n\t\t\t<maxFileSize>200MB</maxFileSize>\n\t\t\t<\!--最大保留天數(shù)-->\n\t\t\t<maxHistory>336</maxHistory>\n\t\t</rollingPolicy>\n\t</appender>\n\n    <\!--log4jdbc -->\n    <logger name\="jdbc.sqltiming" level\="INFO"/>\n    <logger name\="jdbc.sqlonly" level\="OFF"/>\n    <logger name\="jdbc.audit" level\="OFF"/>\n    <logger name\="jdbc.resultset" level\="OFF"/>\n    <logger name\="jdbc.resultsettable" level\="OFF"/>\n    <logger name\="jdbc.connection" level\="OFF"/>\n        \n\t<root level\="INFO">\n\t\t<appender-ref ref\="STDOUT"/>\n\t\t<appender-ref ref\="FILE"/>\n\t</root>\n</configuration> 

HH\:mm\:ss.SSS}|%X{requestId}|%X{requestSeq}|[%t] %-5level %logger{50} %line - %m%n \n\t\t\n\t\t\n\t\t\tlogs/brm-%d{yyyy-MM-dd-HH}-%i.log>\n\t\t\t<\!--單個(gè)文件切割閾值,超過生成新log文件-->\n\t\t\t200MB\n\t\t\t<\!--最大保留天數(shù)-->\n\t\t\t336\n\t\t\n\t\n\n <\!--log4jdbc -->\n \n \n \n \n \n \n \n\t\n\t\t\n\t\t\n\t\n

而我們通常在配置logback的時(shí)候使用的是xml文件;

因此,我們要想辦法讓logback能夠加載context的內(nèi)存值信息。

閱讀logback資料發(fā)現(xiàn),JoranConfigurator支持我們以自定義的方式配置logback,

而springboot是通過LoggingSystem來加載管理日志系統(tǒng)的;如果我能在springboot啟動(dòng)的時(shí)候指定我自定義的日志加載類,問題便迎刃而解。

這里,我們?cè)趓esources目錄下新建META-INF文件夾,添加spring.factories,內(nèi)容如下:

  1. org.springframework.context.ApplicationContextInitializer = com.zhoupu.zplog.refresher.LoggerRefresher 
  2. org.springframework.boot.env.EnvironmentPostProcessor = com.zhoupu.zplog.refresher.LoggerRefresher 

這里我們定義一個(gè)LoggerRefresher,該類重寫loadDefaults和loadConfiguration方法,通過JoranConfigurator加載logback配置,并在configureByApollo中添加一個(gè)apollo事件監(jiān)聽器,當(dāng)發(fā)現(xiàn)logback.xml文件有變化時(shí),重新執(zhí)行configureByApollo方法刷新日志配置。

核心代碼部分如下:

  1. package com.zhoupu.zplog.refresher; 
  2.  
  3. import ch.qos.logback.classic.LoggerContext; 
  4. import ch.qos.logback.classic.joran.JoranConfigurator; 
  5. import ch.qos.logback.core.joran.spi.JoranException; 
  6. import com.ctrip.framework.apollo.Config; 
  7. import com.ctrip.framework.apollo.ConfigChangeListener; 
  8. import com.ctrip.framework.apollo.ConfigService; 
  9. import com.ctrip.framework.apollo.model.ConfigChangeEvent; 
  10. import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants; 
  11. import org.slf4j.ILoggerFactory; 
  12. import org.slf4j.Logger; 
  13. import org.slf4j.LoggerFactory; 
  14. import org.springframework.boot.SpringApplication; 
  15. import org.springframework.boot.env.EnvironmentPostProcessor; 
  16. import org.springframework.context.ApplicationContextInitializer; 
  17. import org.springframework.context.ConfigurableApplicationContext; 
  18. import org.springframework.core.Ordered; 
  19. import org.springframework.core.env.ConfigurableEnvironment; 
  20. import org.springframework.util.StringUtils; 
  21.  
  22. import javax.xml.parsers.DocumentBuilder; 
  23. import javax.xml.parsers.DocumentBuilderFactory; 
  24. import java.io.ByteArrayInputStream; 
  25. import java.io.UnsupportedEncodingException; 
  26.  
  27. /** 
  28.  * 
  29.  * @author vigor 
  30.  * @date 2019/6/14 上午11:27 
  31.  */ 
  32.  
  33. public class LoggerRefresher implements ApplicationContextInitializer<ConfigurableApplicationContext>, EnvironmentPostProcessor, Ordered { 
  34.     private static final Logger log = LoggerFactory.getLogger(LoggerRefresher.class); 
  35.  
  36.     private boolean loadFlag = false
  37.  
  38.     @Override 
  39.     public void initialize(ConfigurableApplicationContext context) { 
  40.         ConfigurableEnvironment environment = context.getEnvironment(); 
  41.         load(environment); 
  42.  
  43.     } 
  44.  
  45.     @Override 
  46.     public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { 
  47.         load(environment); 
  48.     } 
  49.  
  50.     @Override 
  51.     public int getOrder() { 
  52.         return 1; 
  53.     } 
  54.  
  55.     private void load(ConfigurableEnvironment environment) { 
  56.         if (!loadFlag) { 
  57.             environment.getPropertySources().forEach(ps -> { 
  58.                 if (PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME.equals(ps.getName())) { 
  59.                     configureByApollo(); 
  60.                     loadFlag = true
  61.                 } 
  62.             }); 
  63.         } 
  64.     } 
  65.  
  66.     private void configureByApollo() { 
  67.         Config config = ConfigService.getConfig("logback.xml"); 
  68.  
  69.         String content = config.getProperty("content"""); 
  70.         if (StringUtils.isEmpty(content) || !validateXML(content)) { 
  71.             return
  72.         } 
  73.  
  74.         config.addChangeListener(new ConfigChangeListener() { 
  75.             @Override 
  76.             public void onChange(ConfigChangeEvent changeEvent) { 
  77.                 configureByApollo(); 
  78.             } 
  79.  
  80.             @Override 
  81.             public boolean equals(Object obj) { 
  82.                 if (this == obj) { 
  83.                     return true
  84.                 } 
  85.                 if (this.getClass().equals(obj.getClass())) { 
  86.                     return true
  87.                 } 
  88.                 return false
  89.             } 
  90.  
  91.             @Override 
  92.             public int hashCode() { 
  93.                 return 1; 
  94.             } 
  95.         }); 
  96.  
  97.         ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); 
  98.         LoggerContext loggerContext = (LoggerContext) loggerFactory; 
  99.         loggerContext.reset(); 
  100.         JoranConfigurator configurator = new JoranConfigurator(); 
  101.         configurator.setContext(loggerContext); 
  102.         try { 
  103.             configurator.doConfigure(new ByteArrayInputStream(content.getBytes("utf-8"))); 
  104.             log.warn("*****************************logback configureByApollo success!********************************"); 
  105.         } catch (JoranException e) { 
  106.             e.printStackTrace(); 
  107.         } catch (UnsupportedEncodingException e) { 
  108.             e.printStackTrace(); 
  109.         } 
  110.     } 
  111.  
  112.     private boolean validateXML(String xml){ 
  113.         boolean isValidated = true
  114.         try { 
  115.             DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 
  116.             DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); 
  117.             builder.parse(new ByteArrayInputStream(xml.getBytes("utf-8"))); 
  118.         } catch (Exception e) { 
  119.             log.error("apollo logback config error = {}", e); 
  120.             isValidated = false
  121.         } 
  122.         return isValidated; 
  123.     } 
  124.  

至此已完成所有準(zhǔn)備工作,運(yùn)行demo程序,我的項(xiàng)目使用log4jdbc輸出sql,這里我通過修改apollo配置管理后臺(tái)jdbc日志配置,將sqltiming級(jí)別改為INFO:

  1. <!--log4jdbc --> 
  2.     <logger name="jdbc.sqltiming" level="INFO"/> 
  3.     <logger name="jdbc.sqlonly" level="OFF"/> 
  4.     <logger name="jdbc.audit" level="OFF"/> 
  5.     <logger name="jdbc.resultset" level="OFF"/> 
  6.     <logger name="jdbc.resultsettable" level="OFF"/> 
  7.     <logger name="jdbc.connection" level="OFF"/> 

發(fā)起一個(gè)后端請(qǐng)求,查看控制臺(tái)日志輸出,有了!

  1. 2019-11-08 10:11:27.794|1fe97e7dcfeb4fc2810d8a7a706fad2a||[http-nio-8062-exec-3] INFO  jdbc.sqltiming 357 - SELECT id, row_state, created_at, updated_at, created_by, updated_by, business_id, contact_name, 
  2. role, mobile, contact_type FROM t_business_contact WHERE row_state = 0 AND business_id = 1000006 

驚不驚喜_,意不意外!

四 總結(jié)

一個(gè)簡(jiǎn)單的日志配置熱更新嘗試,串聯(lián)起了logback的自定義配置加載原理,apollo的配置中心使用方法和事件監(jiān)聽機(jī)制,以及springboot日志管理和自動(dòng)裝配等知識(shí)點(diǎn),希望大家能從中有所收獲!

【本文是51CTO專欄機(jī)構(gòu)“舟譜數(shù)據(jù)”的原創(chuàng)文章,微信公眾號(hào)“舟譜數(shù)據(jù)( id: zhoupudata)”】

戳這里,看該作者更多好文

 

責(zé)任編輯:武曉燕 來源: 51CTO
相關(guān)推薦

2017-03-09 18:51:53

2025-02-10 00:14:00

2024-04-18 15:22:54

2022-01-05 10:28:11

前端開發(fā)技術(shù)

2023-07-31 09:59:17

JavaJVMAgent

2009-05-12 17:54:44

LinuxOS更新MIT

2021-07-27 22:30:15

Windows 11Windows微軟

2022-02-14 11:14:34

Java工程師開發(fā)

2024-06-17 08:22:31

GenAI技術(shù)人工智能

2018-10-17 10:49:49

Kubernetes存儲(chǔ)處理

2020-06-02 16:33:52

Serverless 云函數(shù)Node

2010-01-14 17:25:28

配置交換機(jī)堆疊

2025-01-21 11:46:26

2013-09-16 14:23:19

2016-10-28 10:40:12

2012-01-13 15:48:21

IT技術(shù)人員

2014-11-05 10:55:48

云計(jì)算云技術(shù)

2021-08-03 08:35:36

Vuex數(shù)據(jù)熱更新

2021-04-19 10:45:52

Webpack熱更新前端

2015-07-13 10:00:25

Android開發(fā)工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)