成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

日志配置熱更新技術實踐

開發 開發工具
一個簡單的日志配置熱更新嘗試,串聯起了logback的自定義配置加載原理,apollo的配置中心使用方法和事件監聽機制,以及springboot日志管理和自動裝配等知識點,希望大家能從中有所收獲!

 [[281868]]

一 為什么需要服務日志熱更新?

對于后端老鳥來說,一定遇到過這樣的場景:

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

But,在生產環境中,為了避免日志打印過量造成磁盤空間浪費,通常會將日志級別設定在INFO,并關閉一般情況用不到的日志輸出;

在不重啟服務的情況下,開啟本已經關閉的業務日志輸出,能不能搞的定呢?答案是當然沒問題。

二 需求分析

熟悉logback的同學此時肯定已經想到通過掃描監聽logback.xml文件配置變化來實現日志級別的調整,像如下這種方式:

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

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

三 做實驗

熟悉apollo文件管理的同學都知道,apollo通過推拉結合的方式將服務端存儲的應用配置文件緩存到本地是以properties的格式存儲的,如下面所示:

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<\!--單個文件切割閾值,超過生成新log文件-->\n\t\t\t<maxFileSize>200MB</maxFileSize>\n\t\t\t<\!--最大保留天數-->\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<\!--單個文件切割閾值,超過生成新log文件-->\n\t\t\t200MB\n\t\t\t<\!--最大保留天數-->\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的時候使用的是xml文件;

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

閱讀logback資料發現,JoranConfigurator支持我們以自定義的方式配置logback,

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

這里,我們在resources目錄下新建META-INF文件夾,添加spring.factories,內容如下:

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

這里我們定義一個LoggerRefresher,該類重寫loadDefaults和loadConfiguration方法,通過JoranConfigurator加載logback配置,并在configureByApollo中添加一個apollo事件監聽器,當發現logback.xml文件有變化時,重新執行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.  

至此已完成所有準備工作,運行demo程序,我的項目使用log4jdbc輸出sql,這里我通過修改apollo配置管理后臺jdbc日志配置,將sqltiming級別改為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"/> 

發起一個后端請求,查看控制臺日志輸出,有了!

  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 

驚不驚喜_,意不意外!

四 總結

一個簡單的日志配置熱更新嘗試,串聯起了logback的自定義配置加載原理,apollo的配置中心使用方法和事件監聽機制,以及springboot日志管理和自動裝配等知識點,希望大家能從中有所收獲!

【本文是51CTO專欄機構“舟譜數據”的原創文章,微信公眾號“舟譜數據( id: zhoupudata)”】

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

 

責任編輯:武曉燕 來源: 51CTO
相關推薦

2017-03-09 18:51:53

2025-02-10 00:14:00

2024-04-18 15:22:54

2022-01-05 10:28:11

前端開發技術

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工程師開發

2024-06-17 08:22:31

GenAI技術人工智能

2010-01-14 17:25:28

配置交換機堆疊

2020-06-02 16:33:52

Serverless 云函數Node

2012-01-13 15:48:21

IT技術人員

2013-09-16 14:23:19

2011-11-09 13:06:48

OpenFlow

2015-07-13 10:00:25

Android開發工具

2018-10-17 10:49:49

Kubernetes存儲處理

2014-11-05 10:55:48

云計算云技術

2025-01-21 11:46:26

2016-10-28 10:40:12

2015-12-07 16:32:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美久久久久久 | 欧美一级做性受免费大片免费 | 成人性生交a做片 | 午夜一级大片 | 欧美激情一区二区三区 | 久久欧美高清二区三区 | 亚洲精品欧美 | 男女羞羞视频在线 | 国产精品精品久久久 | 91精品一区 | 成人精品 | 日本免费一区二区三区 | 亚洲天堂日韩精品 | 欧美色综合 | 亚洲第一女人av | 国产丝袜一区二区三区免费视频 | 夜夜草| 中文字幕男人的天堂 | 亚洲一区二区三区在线 | 午夜视频网 | 欧美日韩久久精品 | 国产极品粉嫩美女呻吟在线看人 | 91资源在线 | 亚洲成人精品影院 | 日韩在线播放视频 | 亚洲欧美日韩在线 | 国产一区二区三区视频免费观看 | 日韩一区二区三区在线观看视频 | 久久久久久亚洲精品 | 免费看黄视频网站 | 国产精品日韩欧美一区二区三区 | 福利片在线 | 久久精品一区 | 91一区二区在线观看 | 蜜桃传媒一区二区 | 黄色日本片 | 日韩午夜 | 国产精品美女久久久久aⅴ国产馆 | 欧美一级欧美三级在线观看 | 欧美一区二区三区视频在线播放 | 欧美成人专区 |