spring cloud 动态调整logger日志等级

114 阅读1分钟

前言

调整日志等级有很多方法 比如: spring-boot-actuator or spring-boot-admin 今天来说个 spring-cloud的方法

一、添加依赖


<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <spring-boot.version>2.7.4</spring-boot.version>
    <spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
</dependency>

二、 配置

spring.application.name = dynamic
server.port = 808

management.server.port = 8081
management.endpoints.web.exposure.include = *
# 支持post请求
management.endpoint.env.post.enabled = true
# http://localhost:8081/management/
management.endpoints.web.basePath = /management
management.endpoints.jmx.exposure.include = *

三、actuator 查看日志等级

image.png

四、修改 org.springframework的日志等级

curl --location 'localhost:8081/management/env' \
--header 'Content-Type: application/json' \
--data '{
    "name":"logging.level.org.springframework",
    "value":"debug"
}'

image.png 修改成功

image.png

五、原理

核心类
org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder
org.springframework.cloud.context.environment.EnvironmentManager
org.springframework.cloud.logging.LoggingRebinder

1、日志初始化

org.springframework.boot.context.logging.LoggingApplicationListener#initialize

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
   getLoggingSystemProperties(environment).apply();
   this.logFile = LogFile.get(environment);
   if (this.logFile != null) {
      this.logFile.applyToSystemProperties();
   }
   this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
   initializeEarlyLoggingLevel(environment);
   initializeSystem(environment, this.loggingSystem, this.logFile);
   initializeFinalLoggingLevels(environment, this.loggingSystem);
   registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

2、在调用 actuator接口 localhost:8081/management/env 会进入 org.springframework.cloud.context.environment.WritableEnvironmentEndpointWebExtension#write

@WriteOperation
public Object write(String name, String value) {
   this.environment.setProperty(name, value);
   return Collections.singletonMap(name, value);
}

把最新的值 set environment中 org.springframework.cloud.context.environment.EnvironmentManager#setProperty

@ManagedOperation
public void setProperty(String name, String value) {

   if (!this.environment.getPropertySources().contains(MANAGER_PROPERTY_SOURCE)) {
      synchronized (this.map) {
         if (!this.environment.getPropertySources().contains(MANAGER_PROPERTY_SOURCE)) {
            MapPropertySource source = new MapPropertySource(MANAGER_PROPERTY_SOURCE, this.map);
            this.environment.getPropertySources().addFirst(source);
         }
      }
   }

   if (!value.equals(this.environment.getProperty(name))) {
      this.map.put(name, value);
      publish(new EnvironmentChangeEvent(this.publisher, Collections.singleton(name)));
   }

}

先去判断值是否和老的不相等 不相等 会把新的值 放到缓存中,然后发送 EnvironmentChangeEvent 事件

3、Logging事件监听 org.springframework.cloud.logging.LoggingRebinder#onApplicationEvent

@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
   if (this.environment == null) {
      return;
   }
   LoggingSystem system = LoggingSystem.get(LoggingSystem.class.getClassLoader());
   setLogLevels(system, this.environment);
}

protected void setLogLevels(LoggingSystem system, Environment environment) {
   Map<String, String> levels = Binder.get(environment).bind("logging.level", STRING_STRING_MAP)
         .orElseGet(Collections::emptyMap);
   for (Entry<String, String> entry : levels.entrySet()) {
      setLogLevel(system, environment, entry.getKey(), entry.getValue().toString());
   }
}

levels=[org.springframework=info, root=debug]

Map<String, String> levels = Binder.get(environment).bind("logging.level", STRING_STRING_MAP) 这个代码会在Environment 找到所有的 logging.level 的配置然后迭代修改而我实际可能就改了一个