|
|
|
|
移动端
创建专栏

日志配置热更新技术实践

本文来源:http://www.2233122.com/www_szhk_com/

太阳城娱乐网最快登入,也难怪,现在圈子如此高度的同质化,也只有每年一款的新能带给我们一丝惊喜了。12月8日晚间,21世纪经济报道记者从民生北京分行获悉,民生银行北京分行于12月5日向总行做专项汇报,总行责成北京分行成立专门工作小组,对涉事人员关某(北京分行中心商业区管理总部下属企业四中心副总经理)所涉事件进行调查和取证,依法依规进行严肃处理。同时摄像机存储支持最大64GBmicroSDHC/SDXC卡,本机存储可全天录像50个小时。但是由于印度政府突然废除大额纸币,印度的经济增速可能再次落后于中国。

我们先来说说前一个流派。新增抢票任务并输入抢票信息后,即可分享给亲朋好友帮忙抢票第三步:创建抢票任务后,“抢票帮”就会将抢票信息生成二维码(注意,此信息里面包括乘车人的身份证信息,请发给熟识的好友,避免信息泄露),并生成一个抢票页面。分类:大小:14.3M下载:找明医app,专业的医患交流平台。搜狗拼音输入法是基于搜索引擎技术的、特别适合网民使用的、新一代的输入法产品,由于采用了搜索引擎技术,输入速度有了质的飞跃,在词库的广度、词语的准确度上,搜狗输入法都远远领先于其他输入法,用户还可以通过互联网备份自己的个性化词库和配置信息。

  百度手机卫士是一款功能超强的手机安全软件,为用户免费提供系统优化、手机加速、垃圾清理、骚扰电话拦截、骚扰短信甄别、手机上网流量保护、流量监控、恶意软件查杀等优质服务。有迹象表明,在数据和分析工具方面的投入在增加。因此,中国企业需要在未来发展中不断谋求同行们对企业的认可,赢得客户们的信赖,一味的打“价格战”并不能从根本上提升中国企业的口碑和市场占有率。该摄像机可根据运营需求专向定制,可定制开发轻APP功能页面(提供官方模版),可开放定制HTML5自媒体展现页面,可独立申请逐步开放。

一个简单的日志配置热更新尝试,串联起了logback的自定义配置加载原理,apollo的配置中心使用方法和事件监听机制,以及springboot日志管理和自动装配等知识点,希望大家能从中有所收获!

作者:舟谱数据|2019-11-11 10:38

 

一 为什么需要服务日志热更新?

对于后端老鸟来说,一定遇到过这样的场景:

为了排查线上突发的问题,非常希望能够全面的看到请求在服务链路上的完整日志输出;

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)”】

太阳城娱乐网最快登入戳这里,看该作者更多好文

【编辑推荐】

  1. 太阳城娱乐网最快登入资深技术Leader肺腑忠告:如何成为技术大牛?
  2. 太阳城娱乐网最快登入管理 | 技术Leader:选OKR还是KPI?
  3. 计算机视觉技术深度解读之视频动作识别
  4. 大数据开发最火技术Kafka背后的“黑科技”
  5. 区块链的颠覆力量之一:底层技术
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢
申博娱乐城直营网 申博开户 www.99psb.com 百家乐支付宝充值 太阳城游戏官网 申博官网开户
申博代理加盟登入 申博手机版下载登入 申博开户直营网 申博现金充值登入 菲律宾太阳娱乐场登入 菲律宾申博娱乐官网
申博太阳城娱乐官网登入 菲律宾申博游戏登入 申博管理网登入 申博太阳城官网 申博游戏网直营网 申博娱乐网直营