您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Springboot 动态改变Log级别

时间:2020-07-09 10:44:46  来源:  作者:

背景

作为程序猿,定位问题是我们的日常工作,而日志是我们定位问题非常重要的依据。传统方式定位问题时,往往是如下步骤:

  • 将日志级别设低,例如 DEBUG ;
  • 重启应用;
  • 复现问题,观察日志;

那么问题就来了,可不可以动态修改日志级别呢?(无需重启应用,就能立刻刷新)

答案是肯定的!

下面提供几个思路给大家参考。

使用 LoggingSystem 自行开发修改日志级别的接口

不废话,直接上代码

@Resource
        private LoggingSystem loggingSystem;

@PostMApping("/changeLogLevel")
public void changeLogLevel(@RequestParam("name") String name, @RequestParam("level") String level) {
  LogLevel logLevel = LogLevel.valueOf(level.toUpperCase());
  loggingSystem.setLogLevel(name, logLevel);
}

what?这么简单?是的,就是这么简单。

LoggingSystem 这个抽象类就是关键,其实后面所要介绍的几个修改思路(actuator,Apollo,mq)的底层也是基于它进行修改的。

如果大家对LoggingSystem这个类在底层究竟是如何实现动态修改日志级别感兴趣的话,请评论区留言,我抽时间再写一篇文章来详细说一下。

然后再说一下这种方式的优缺点吧。

优点:简单!

缺点:也很明显,只适合单机/生产机器不多的服务。如果你的服务有上百个节点,用这种方式来修改。。。

那有朋友会问,有没有适合多机集群的服务的修改方式?

那必须有啊,下面介绍一下思路二。

使用 Apollo + LoggingSystem

这种方式的前提是系统接入了Apollo。

也不废话,直接上代码吧。代码里也有注释。

@Configuration
public class LogLevelRefresher {
    private final static Logger log = LoggerFactory.getLogger(com.dylan.config.LoggingLevelRefresher.class);

    private static final String PREFIX = "logging.level.";
    private static final String ROOT = LoggingSystem.ROOT_LOGGER_NAME;

    @Resource
    private LoggingSystem loggingSystem;


    /**
     * 支持类配置
     */
    @PostConstruct
    private void init() {
      //要修改日志级别的key(包路径/类路径)
        String keyStr = ConfigCenterService.getAppProperty("log.changeKey", "logging.level.root,logging.level.com.dylan.config");
        Set<String> changedKeys = Arrays.stream(keyStr.split(",")).collect(Collectors.toSet());
        refreshLoggingLevels(changedKeys);
    }

    /**
    * 修改Apollo配置后的回调方法
    */
    @ApolloConfigChangeListener
    private void onChange(ConfigChangeEvent changeEvent) {
        refreshLoggingLevels(changeEvent.changedKeys());
    }

    private void refreshLoggingLevels(Set<String> changedKeys) {
        for (String key : changedKeys) {
            // key may be : logging.level.com.example.web
            if (StringUtils.startsWithIgnoreCase(key, PREFIX)) {
                String loggerName = PREFIX.equalsIgnoreCase(key) ? ROOT : key.substring(PREFIX.length());
                String strLevel = ConfigCenterService.getProperty(key, parentStrLevel(loggerName));
                LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
                loggingSystem.setLogLevel(loggerName, level);
              //打印一下信息,可以不用
                log(loggerName, strLevel);
            }
        }
    }

    private String parentStrLevel(String loggerName) {
        String parentLoggerName = loggerName.contains(".") ? loggerName.substring(0, loggerName.lastIndexOf(".") : ROOT;
        return loggingSystem.getLoggerConfiguration(parentLoggerName).getEffectiveLevel().name();
    }

    /**
     * 获取当前类的Logger对象有效日志级别对应的方法,进行日志输出。举例:
     * 如果当前类的EffectiveLevel为WARN,则获取的Method为 `org.slf4j.Logger#warn(JAVA.lang.String, java.lang.Object, java.lang.Object)`
     * 目的是为了输出`changed {} log level to:{}`这一行日志
     */
    private void log(String loggerName, String strLevel) {
        try {
            LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(log.getName());
            Method method = log.getClass().getMethod(loggerConfiguration.getEffectiveLevel().name().toLowerCase(), String.class, Object.class, Object.class);
            method.invoke(log, "changed {} log level to:{}", loggerName, strLevel);
        } catch (Exception e) {
            log.error("changed {} log level to:{} error", loggerName, strLevel, e);
        }
    }
}

大家可以看到,Apollo的方式最终也是LoggingSystem 这个类进行修改日志级别的操作。

那可能大家会问,Apollo在这里的作用是什么?

如果大家用过Apollo的话就会发现,在Apollo可视化管理系统中,每个系统都有一个实例列表,里面就是我们具体的应用地址。所以在这里你可以认为Apollo有类似注册中心的作用,在我们应用启动的时候,Apollo后台就会记录下来。

所以Apollo能实现集群的日志级别动态修改的原理就在这。是不是也很简单呢?

使用 MQ + LoggingSystem

 

如果你们的系统没有接入Apollo的话,那应该如何实现集群的日志级别动态修改呢?

MQ就是其中一个选择。我简单说一下实现思路吧,具体实现也很简单,就留给大家去动手实践啦。

  1. 暴露一个修改日志级别的接口
  2. 这个接口要做的是使用producer来发送一个广播类型的MQ,注意了,是广播类型的
  3. 在consumer里面通过LoggingSystem进行日志级别的修改即可。

是不是很简单呢?

使用Springboot的 actuator 组件

其实这种方法和方式一是差不多的,只是actuator把接口通过端点Endpoints 的方式暴露出来。

至于什么是端点(Endpoints),我简单介绍一下吧。

  • 什么是端点

Endpoints 是 Actuator 的核心部分,它用来监视应用程序及交互,spring-boot-actuator中已经内置了非常多的Endpoints(health、info、beans、httptrace、shutdown等等),同时也允许我们扩展自己的端点。

  • 端点的分类

Endpoints 分成两类:原生端点和用户自定义端点;自定义端点主要是指扩展性,用户可以根据自己的实际应用,定义一些比较关心的指标,在运行期进行监控。

原生端点是在应用程序里提供的众多 restful api 接口,通过它们可以监控应用程序运行时的内部状况。

原生端点又可以分成三类:

  1. 应用配置类:可以查看应用在运行期间的静态信息:例如自动配置信息、加载的spring bean信息、yml文件配置信息、环境信息、请求映射信息;
  2. 度量指标类:主要是运行期间的动态信息,例如堆栈、请求链、一些健康指标、metrics信息等
  3. 操作控制类:主要是指shutdown,用户可以发送一个请求将应用的监控功能关闭。

我们这里修改配置文件用到的就是应用配置类的端点。

查看当前应用各包/类的日志级别

http://localhost:8080/actuator/loggers

可看到类似如下的结果:

{
	"levels": ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"],
	"loggers": {
		"ROOT": {
			"configuredLevel": "INFO",
			"effectiveLevel": "INFO"
		},		
		"com.itmuch.logging.TestController": {
			"configuredLevel": null,
			"effectiveLevel": "INFO"
		}
	}
	// ...省略
}

查看指定包/类日志详情

http://localhost:8080/actuator/loggers/com.dylan.logging.TestController

可看到类似如下的结果:

{"configuredLevel":null,"effectiveLevel":"INFO"}

修改日志级别

POST方式,json格式的参数

example:http://localhost:8080/actuator/loggers/com.dylan.controller.IncreaseAgentController

Springboot 动态改变Log级别

actuator修改日志级别

但这种方式和方式一有同样的局限性,就是只适合单机或者开发环境。如果想用这种方式的话可以接入Spring Boot Admin。通过后台的方式进行管理。



Tags:Springboot   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
我是一名程序员关注我们吧,我们会多多分享技术和资源。进来的朋友,可以多了解下青锋的产品,已开源多个产品的架构版本。Thymeleaf版(开源)1、采用技术: springboot、layui、Thymel...【详细内容】
2021-12-14  Tags: Springboot  点击:(21)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  Tags: Springboot  点击:(25)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  Tags: Springboot  点击:(56)  评论:(0)  加入收藏
1. 介绍1.1 介绍今天开始我们来学习Java操作MySQL数据库的技巧,Java操作MySQL是借助JdbcTemplate这个对象来实现的。JdbcTemplate是一个多数据库集中解决方案,而我们今天只讲...【详细内容】
2021-11-05  Tags: Springboot  点击:(30)  评论:(0)  加入收藏
SpringBoot中的Controller注册本篇将会以Servlet为切入点,通过源码来看web容器中的Controller是如何注册到HandlerMapping中。请求来了之后,web容器是如何根据请求路径找到对...【详细内容】
2021-11-04  Tags: Springboot  点击:(53)  评论:(0)  加入收藏
环境:Springboot2.4.11环境配置接下来的演示都是基于如下接口进行。@RestController@RequestMapping("/exceptions")public class ExceptionsController { @GetMapping(...【详细内容】
2021-10-11  Tags: Springboot  点击:(41)  评论:(0)  加入收藏
SpringBoot项目默认使用logback, 已经内置了 logback 的相关jar包,会从resource包下查找logback.xml, logback 文件格式范本 可直接复制使用,有控制台 info.log error.log三个...【详细内容】
2021-10-09  Tags: Springboot  点击:(50)  评论:(0)  加入收藏
环境:Springboot2.4.10当应用程序启动时,Spring Boot将自动从以下位置查找并加载application.properties和application.yaml文件: 从Classpath类路径classpath的根类路径classp...【详细内容】
2021-09-26  Tags: Springboot  点击:(78)  评论:(0)  加入收藏
搭建基础1. Intellij IDEA 2. jdk1.8 3. maven3.6.3搭建方式(1)在线创建项目Spring Boot 官方提供的一种创建方式,在浏览器中访问如下网址: https://start.spring.io/在打开的页...【详细内容】
2021-09-14  Tags: Springboot  点击:(78)  评论:(0)  加入收藏
最近开发项目的时候需要用到对象的属性拷贝,以前也有用过一些复制框架,比如spring的 BeanUtils.copyProperties等方式,但总是不尽如人意,最近发现使用orika进行对象拷贝挺好用的...【详细内容】
2021-08-27  Tags: Springboot  点击:(231)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(12)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(27)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(56)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条