本文主要研究下springboot2的LoggersEndpoint

实例

  • GET /actuator/loggers
{
  "levels": [
    "OFF",
    "ERROR",
    "WARN",
    "INFO",
    "DEBUG",
    "TRACE"
  ],
  "loggers": {
    "ROOT": {
      "configuredLevel": "INFO",
      "effectiveLevel": "INFO"
    },
    "com": {
      "configuredLevel": null,
      "effectiveLevel": "INFO"
    },
    "com.example": {
      "configuredLevel": null,
      "effectiveLevel": "INFO"
    },
    "com.example.config": {
      "configuredLevel": null,
      "effectiveLevel": "INFO"
    }
  }
}
  • GET /actuator/loggers/com.example
{
  "configuredLevel": null,
  "effectiveLevel": "INFO"
}
  • POST
curl -i -X POST -H 'Content-Type: application/json' -d '{"configuredLevel": "ERROR"}' http://localhost:8080/actuator/loggers/com.example
HTTP/1.1 204
Date: Wed, 25 Apr 2018 14:54:41 GMT

LoggersEndpointAutoConfiguration

spring-boot-actuator-autoconfigure-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfiguration.java

@Configuration
public class LoggersEndpointAutoConfiguration {

    @Bean
    @ConditionalOnBean(LoggingSystem.class)
    @Conditional(OnEnabledLoggingSystemCondition.class)
    @ConditionalOnMissingBean
    @ConditionalOnEnabledEndpoint
    public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem) {
        return new LoggersEndpoint(loggingSystem);
    }

    static class OnEnabledLoggingSystemCondition extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
                AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage
                    .forCondition("Logging System");
            String loggingSystem = System.getProperty(LoggingSystem.SYSTEM_PROPERTY);
            if (LoggingSystem.NONE.equals(loggingSystem)) {
                return ConditionOutcome.noMatch(message.because("system property "
                        + LoggingSystem.SYSTEM_PROPERTY + " is set to none"));
            }
            return ConditionOutcome.match(message.because("enabled"));
        }

    }

}
这里根据loggingSystem,来创建LoggersEndpoint;另外还使用了OnEnabledLoggingSystemCondition

LoggersEndpoint

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/logging/LoggersEndpoint.java

@Endpoint(id = "loggers")
public class LoggersEndpoint {

    private final LoggingSystem loggingSystem;

    /**
     * Create a new {@link LoggersEndpoint} instance.
     * @param loggingSystem the logging system to expose
     */
    public LoggersEndpoint(LoggingSystem loggingSystem) {
        Assert.notNull(loggingSystem, "LoggingSystem must not be null");
        this.loggingSystem = loggingSystem;
    }

    @ReadOperation
    public Map<String, Object> loggers() {
        Collection<LoggerConfiguration> configurations = this.loggingSystem
                .getLoggerConfigurations();
        if (configurations == null) {
            return Collections.emptyMap();
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("levels", getLevels());
        result.put("loggers", getLoggers(configurations));
        return result;
    }

    @ReadOperation
    public LoggerLevels loggerLevels(@Selector String name) {
        Assert.notNull(name, "Name must not be null");
        LoggerConfiguration configuration = this.loggingSystem
                .getLoggerConfiguration(name);
        return (configuration == null ? null : new LoggerLevels(configuration));
    }

    @WriteOperation
    public void configureLogLevel(@Selector String name,
            @Nullable LogLevel configuredLevel) {
        Assert.notNull(name, "Name must not be empty");
        this.loggingSystem.setLogLevel(name, configuredLevel);
    }

    private NavigableSet<LogLevel> getLevels() {
        Set<LogLevel> levels = this.loggingSystem.getSupportedLogLevels();
        return new TreeSet<>(levels).descendingSet();
    }

    private Map<String, LoggerLevels> getLoggers(
            Collection<LoggerConfiguration> configurations) {
        Map<String, LoggerLevels> loggers = new LinkedHashMap<>(configurations.size());
        for (LoggerConfiguration configuration : configurations) {
            loggers.put(configuration.getName(), new LoggerLevels(configuration));
        }
        return loggers;
    }

    /**
     * Levels configured for a given logger exposed in a JSON friendly way.
     */
    public static class LoggerLevels {

        private String configuredLevel;

        private String effectiveLevel;

        public LoggerLevels(LoggerConfiguration configuration) {
            this.configuredLevel = getName(configuration.getConfiguredLevel());
            this.effectiveLevel = getName(configuration.getEffectiveLevel());
        }

        private String getName(LogLevel level) {
            return (level == null ? null : level.name());
        }

        public String getConfiguredLevel() {
            return this.configuredLevel;
        }

        public String getEffectiveLevel() {
            return this.effectiveLevel;
        }

    }

}
通过loggingSystem.getLoggerConfigurations()获取Collection<LoggerConfiguration>,然后getLoggers方法将configurations转换为Map<String, LoggerLevels>

小结

LoggersEndpoint提供两个readOperation和一个writeOperation,分别用来读取和更改logger的level,非常实用。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...