zoukankan      html  css  js  c++  java
  • spring boot 源码解析52-actuate中MVCEndPoint解析

    今天有个别项目的jolokia的endpoint不能访问,调试源码发现:endpoint.enabled的开关导致的。

    关于Endpoint,

    Springboot Endpoint之二:Endpoint源码剖析


    之前的几篇文章分析了spring boot 中有关endpoint的实现,细心的朋友可以发现,在org.springframework.boot.actuate.endpoint.mvc 包下也有一系列的xxxEndpoint,这又是为什么呢?



    原因是: 我们很多情况下,都是访问接口的方式获取应用的监控,之前的分析是其实现的底层,要想实现通过接口访问,还需要对其进行包装一番,org.springframework.boot.actuate.endpoint.mvc 包下的实现就是干的这种事,下面,我们就来分析一下吧

    解析
    关于mvcEndPoint的类图如下,



    下面我们就来1个1个的来分析吧.

    MvcEndpoint
    MvcEndpoint –> 顶层接口,实现类允许使用@RequestMapping和完整的Spring MVC机制,但不能在类型级别使用@Controller或@RequestMapping,因为这将导致路径的双重映射,一次通过常规MVC处理程序映射,一次通过EndpointHandlerMapping。

    该类声明如下:

    public interface MvcEndpoint {

    // 禁用端点的响应实体
    ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<Map<String, String>>(
            Collections.singletonMap("message", "This endpoint is disabled"),
            HttpStatus.NOT_FOUND);

    // 返回端点的MVC路径
    String getPath();

    // 返回端点是否暴露敏感信息
    boolean isSensitive();

    // 返回端点暴露的类型。或者返回null 如果当前的MvcEndpoint 暴露的信息 不能视为一个传统的Endpoint(也就是我们之前分析的那一堆Endpoint)
    Class<? extends Endpoint> getEndpointType();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    AbstractMvcEndpoint
    AbstractMvcEndpoint继承了WebMvcConfigurerAdapter,实现了MvcEndpoint, EnvironmentAware接口,此时 AbstractMvcEndpoint 就持有了Environment,可以对spring mvc 做个性化设置.

    字段,构造器分别如下:

    // Endpoint 请求路径
    private String path;

    // endpoint是否可用
    private Boolean enabled;

    // 标识该endpoint 是否暴露了敏感数据,如果为true,访问该Endpoint需要进行校验
    private Boolean sensitive;

    // 是否默认敏感
    private final boolean sensitiveDefault;

    public AbstractMvcEndpoint(String path, boolean sensitive) {
        setPath(path);
        this.sensitiveDefault = sensitive;
    }

    public AbstractMvcEndpoint(String path, boolean sensitive, boolean enabled) {
        setPath(path);
        this.sensitiveDefault = sensitive;
        this.enabled = enabled;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    AbstractMvcEndpoint 实现了MvcEndpoint的方法,分别如下:

    getPath,直接返回其字段值即可,代码如下:

    public String getPath() {
    return this.path;
    }
    1
    2
    3
    isSensitive–> 默认返回false.代码如下:

    public boolean isSensitive() {
    // 默认返回false
    return EndpointProperties.isSensitive(this.environment, this.sensitive,
            this.sensitiveDefault);
    }
    1
    2
    3
    4
    5
    调用:

    public static boolean isSensitive(Environment environment, Boolean sensitive,
        boolean sensitiveDefault) {
    // 1. 如果sensitive 不等于null,则直接返回
    if (sensitive != null) {
        return sensitive;
    }
    // 2. 如果environment 不等于null 并且 environment中配置有endpoints.sensitive的属性,则
    // 返回其配置值
    if (environment != null
            && environment.containsProperty(ENDPOINTS_SENSITIVE_PROPERTY)) {
        return environment.getProperty(ENDPOINTS_SENSITIVE_PROPERTY, Boolean.class);
    }
    // 3. 返回给的默认值
    return sensitiveDefault;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    如果sensitive 不等于null,则直接返回
    如果environment 不等于null 并且 environment中配置有endpoints.sensitive的属性,则 返回其配置值
    返回给的默认值
    getEndpointType –> 默认返回null,代码如下:

    public Class<? extends Endpoint> getEndpointType() {
    return null;
    }
    1
    2
    3
    此外,该类还声明了1个方法,判断当前端点是否可用,默认返回true代码如下:

    public boolean isEnabled() {
        // 默认返回true
        return EndpointProperties.isEnabled(this.environment, this.enabled);
    }
    1
    2
    3
    4
    调用:

    public static boolean isEnabled(Environment environment, Boolean enabled) {
        // 1. 如果enabled 不为null,则进行返回.
        if (enabled != null) {
            return enabled;
        }
        // 2. 如果Environment 不等于null 并且Environment 配置有endpoints.enabled的属性,
        // 则返回其配置的值
        if (environment != null
                && environment.containsProperty(ENDPOINTS_ENABLED_PROPERTY)) {
            return environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class);
        }
        // 3. 默认为true
        return true;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    如果enabled 不为null,则进行返回.
    如果Environment 不等于null 并且Environment 配置有endpoints.enabled的属性,则返回其配置的值
    默认为true
    NamedMvcEndpoint
    NamedMvcEndpoint继承自MvcEndpoint,使一个MvcEndpoint可以包含一个逻辑名.不像getPath()–>它没有给用户一个机会去改变endpoint的名字.NamedMvcEndpoint 提供了一个一致的方式去引用一个endpoint.

    该类声明了1个方法,如下:

    // 返回一个逻辑名字,不能为null,空,字母数字组成
    String getName();
    1
    2
    AbstractNamedMvcEndpoint
    该类继承自AbstractMvcEndpoint,实现了NamedMvcEndpoint接口.
    字段,构造器如下:

    // Endpoint 名字
    private final String name;

    public AbstractNamedMvcEndpoint(String name, String path, boolean sensitive) {
        super(path, sensitive);
        Assert.hasLength(name, "Name must not be empty");
        this.name = name;
    }

    public AbstractNamedMvcEndpoint(String name, String path, boolean sensitive,
            boolean enabled) {
        super(path, sensitive, enabled);
        Assert.hasLength(name, "Name must not be empty");
        this.name = name;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    getName,只需返回name属性值即可,很简单,代码如下:

    public String getName() {
        return this.name;
    }
    1
    2
    3
    该类的子类有:

    AuditEventsMvcEndpoint
    DocsMvcEndpoint
    HalJsonMvcEndpoint
    HeapdumpMvcEndpoint
    JolokiaMvcEndpoint
    LogFileMvcEndpoint
    这些子类的实现我们后续进行分析…

    AbstractEndpointMvcAdapter
    AbstractEndpointMvcAdapter 实现了NamedMvcEndpoint接口,是MvcEndpoint的抽象基类
    字段,构造器如下:

    // 被代理的底层端点(端点子类
    private final E delegate;

    // 端点URL路径
    private String path;

    //
    public AbstractEndpointMvcAdapter(E delegate) {
        Assert.notNull(delegate, "Delegate must not be null");
        this.delegate = delegate;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    NamedMvcEndpoint接口方法实现如下:

    getName,返回被代理的Endpoint的id,代码如下:

    public String getName() {
    return this.delegate.getId();
    }
    1
    2
    3
    getPath –> 如果path不等于null,则直接返回,否则使用/+Endpoint的id,代码如下:

    public String getPath() {
    // 如果path不等于null,则直接返回,否则使用/+Endpoint的id
    return (this.path != null ? this.path : "/" + this.delegate.getId());
    }
    1
    2
    3
    4
    isSensitive–> 直接调用被代理的Endpoint的isSensitive方法即可.代码如下:

    public boolean isSensitive() {
    return this.delegate.isSensitive();
    }
    1
    2
    3
    getEndpointType –> 返回被代理的 Endpoint的类型即可,代码如下:

    public Class<? extends Endpoint> getEndpointType() {
    return this.delegate.getClass();
    }
    1
    2
    3
    AbstractEndpointMvcAdapter 还声明了2个方法,供子类使用

    getDisabledResponse–> 返回该相应当所代理的endpoint不可用时.代码如下:

    protected ResponseEntity<?> getDisabledResponse() {
    return MvcEndpoint.DISABLED_RESPONSE;
    }
    1
    2
    3
    即:

    ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<Map<String, String>>(
        Collections.singletonMap("message", "This endpoint is disabled"),
        HttpStatus.NOT_FOUND);
    1
    2
    3
    invoke–> 调用代理的Endpoint,并返回调用结果,代码如下:

    protected Object invoke() {
    if (!this.delegate.isEnabled()) {
        return getDisabledResponse();
    }
    return this.delegate.invoke();
    }
    1
    2
    3
    4
    5
    6
    端点不可用(禁用),则返回默认的不可用信息.当Endpoint被禁用时,是不会注册的.
    否则,调用Endpoint的invoke方法
    EndpointMvcAdapter
    EndpointMvcAdapter,继承自AbstractEndpointMvcAdapter,将Endpoint适配为MvcEndpoint. 构造器如下:

    public EndpointMvcAdapter(Endpoint<?> delegate) {
        super(delegate);
    }
    1
    2
    3
    该类覆写了invoke,使其能过被spring mvc 处理–> 暴露接口(关于这部分,我们后续有分析),代码如下:

    @Override
    @ActuatorGetMapping
    @ResponseBody
    public Object invoke() {
        return super.invoke();
    }
    1
    2
    3
    4
    5
    6
    其中@ActuatorGetMapping 就是@RequestMapping的封装,被该注解标注的方法,其请求方式为get,产生的数据格式为application/vnd.spring-boot.actuator.v1+json和application/json.代码如下:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @RequestMapping(method = RequestMethod.GET, produces = {
        ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
        MediaType.APPLICATION_JSON_VALUE })
    @interface ActuatorGetMapping {

    /**
     * Alias for {@link RequestMapping#value}.
     * @return the value
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    EnvironmentMvcEndpoint
    该类继承自EndpointMvcAdapter,实现了EnvironmentAware,因此,该类也就持有了Environment.

    字段,构造器如下:

    private Environment environment;

    public EnvironmentMvcEndpoint(EnvironmentEndpoint delegate) {
        super(delegate);
    }
    1
    2
    3
    4
    5
    此时, EnvironmentMvcEndpoint 也就持有了EnvironmentEndpoint的实例

    该类声明了1个被@ActuatorGetMapping注解的方法,value,代码如下:

    @ActuatorGetMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object value(@PathVariable String name) {
        if (!getDelegate().isEnabled()) {
            // Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
            // disabled
            return getDisabledResponse();
        }
        return new NamePatternEnvironmentFilter(this.environment).getResults(name);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @ActuatorGetMapping(“/{name:.*}”)与@PathVariable String name –> rest风格,将其name注入到方法的参数name中,匹配规则是任意字符
    @ResponseBody –> 返回json格式的数据
    @HypermediaDisabled–>表明该MvcEndpoint或者@RequestMapping注解的方法不会生成hypermedia 的响应.代码如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface HypermediaDisabled {
    }
    1
    2
    3
    4
    5
    其方法逻辑如下:

    如果EnvironmentEndpoint不可用,则返回Disabled Response.
    否则实例化NamePatternEnvironmentFilter,调用其getResults方法获得对应name的属性值.

    NamePatternEnvironmentFilter继承自NamePatternFilter.

    NamePatternFilter:

    NamePatternFilter–> 可以使用name正则表达式过滤源数据的实用工具类,用来检测检测名称是否是经典的“single value”键或正则表达式.子类必须实现getValue,getNames 方法.该类是1个泛型类,其泛型参数T 代表着原始数据的类型.

    字段,构造器如下:

    private static final String[] REGEX_PARTS = { "*", "$", "^", "+", "[" };
    private final T source;
    NamePatternFilter(T source) {
    this.source = source;
    }
    1
    2
    3
    4
    5
    声明了3个抽象方法:

    protected abstract void getNames(T source, NameCallback callback);
    protected abstract Object getValue(T source, String name);
    protected abstract Object getOptionalValue(T source, String name);
    1
    2
    3
    NamePatternFilter中声明了1个方法,代码如下:

    public Map<String, Object> getResults(String name) {
    // 1. 如果name含有 "*", "$", "^", "+", "[" ,则认为是一个正则表达式,将其返回Pattern.否则返回null
    Pattern pattern = compilePatternIfNecessary(name);
    if (pattern == null) {
    // 2. 如果pattern 等于null,则说明name 是一个普通字符串,调用getValue 这一抽象方法获得value,放入result后返回
    Object value = getValue(this.source, name);
    Map<String, Object> result = new HashMap<String, Object>();
    result.put(name, value);
    return result;
    }
    // 3. 实例化 ResultCollectingNameCallback
    ResultCollectingNameCallback resultCollector = new ResultCollectingNameCallback(
        pattern);
    // 4. 抽象方法
    getNames(this.source, resultCollector);
    // 5, 返回结果
    return resultCollector.getResults();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    如果name含有 “*”, “$”, “^”, “+”, “[” ,则认为是一个正则表达式,将其返回Pattern.否则返回null,代码如下:

    private Pattern compilePatternIfNecessary(String name) {
    for (String part : REGEX_PARTS) {
    if (name.contains(part)) {
    try {
        return Pattern.compile(name);
    }
    catch (PatternSyntaxException ex) {
        return null;
    }
    }
    }
    return null;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    如果pattern 等于null,则说明name 是一个普通字符串,调用getValue 这一抽象方法获得value,放入result后返回
    实例化 ResultCollectingNameCallback,该类实现了NameCallback接口,该接口只声明了如下方法:

    void addName(String name);
    1
    ResultCollectingNameCallback中的字段,构造器如下:

    // 将name 转换为正则所对应的对象
    private final Pattern pattern;
    // 结果集
    private final Map<String, Object> results = new LinkedHashMap<String, Object>();
    ResultCollectingNameCallback(Pattern pattern) {
    this.pattern = pattern;
    }
    1
    2
    3
    4
    5
    6
    7
    其addName方法如下:

    public void addName(String name) {
    // 1. 如果name 符合正则,则通过调用getOptionalValue 获得值后加入到results
    if (this.pattern.matcher(name).matches()) {
    Object value = getOptionalValue(NamePatternFilter.this.source, name);
    if (value != null) {
        this.results.put(name, value);
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    如果name 符合正则,则通过调用getOptionalValue 获得值后加入到results

    此外还声明了get方法,来暴露结果集,代码如下:

    public Map<String, Object> getResults() {
    return this.results;
    }
    1
    2
    3
    执行getNames方法,进行结果的收集

    返回结果
    NamePatternEnvironmentFilter

    继承了NamePatternFilter 接口,泛型参数为Environment
    抽象方法分别实现如下:

    getValue:

    protected Object getValue(Environment source, String name) {
    // 1. 获取值,如果没获取到,则抛出NoSuchPropertyException,否则,对其进行脱敏
    Object result = getValue(name);
    if (result == null) {
    throw new NoSuchPropertyException("No such property: " + name);
    }
    return ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    调用getValue方法获取值,如果没获取到,则抛出NoSuchPropertyException,否则,对其进行脱敏.

    getValue–>直接从environment 中获取值,代码如下:

    private Object getValue(String name) {
    // 直接从environment 中获取值,
    return ((EnvironmentEndpoint) getDelegate()).getResolver().getProperty(name,
        Object.class);
    }
    1
    2
    3
    4
    5
    getNames–> 遍历source中的PropertySources,将PropertySource的属性名依次的加入到ResultCollectingNameCallback中.代码如下:

    protected void getNames(Environment source, NameCallback callback) {
    if (source instanceof ConfigurableEnvironment) {
    // 遍历source中的PropertySources,将PropertySource的属性名依次的加入到ResultCollectingNameCallback中
    getNames(((ConfigurableEnvironment) source).getPropertySources(),
            callback);
    }
    }
    private void getNames(PropertySources propertySources, NameCallback callback) {
    for (PropertySource<?> propertySource : propertySources) {
    if (propertySource instanceof EnumerablePropertySource) {
        EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) propertySource;
        for (String name : source.getPropertyNames()) {
            callback.addName(name);
        }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    getOptionalValue,代码如下:

    protected Object getOptionalValue(Environment source, String name) {
    // 1. 获得name对应的属性值
    Object result = getValue(name);
    if (result != null) {
    // 2. 如果属性值存在则进行脱敏
    result = ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
    }
    // 3. 否则直接返回null
    return result;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    获得name对应的属性值
    如果属性值存在则进行脱敏
    否则直接返回null
    属性配置(有@ConfigurationProperties(prefix = “endpoints.env”)注解):

    endpoints.env.path=/env
    1
    自动装配:

    在EndpointWebMvcManagementContextConfiguration中进行了配置,如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(EnvironmentEndpoint.class)
    @ConditionalOnEnabledEndpoint("env")
    public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
        return new EnvironmentMvcEndpoint(delegate);
    }
    1
    2
    3
    4
    5
    6
    7
    @Bean –> 注册1个id为 environmentMvcEndpoint,类型为EnvironmentMvcEndpoint的bean
    @ConditionalOnMissingBean–>BeanFactory中不存在EnvironmentMvcEndpoint类型的bean时生效
    @ConditionalOnBean(EnvironmentEndpoint.class) –> 当BeanFactory中存在EnvironmentEndpoint类型的Bean时生效
    @ConditionalOnEnabledEndpoint(“env”)–> 如果配置有endpoints.env.enabled = true 或者endpoints.enabled= true 则该配置生效.关于此处的实现我们后续有文章进行分析.
    LoggersMvcEndpoint
    字段,构造器如下:

    private final LoggersEndpoint delegate;

    public LoggersMvcEndpoint(LoggersEndpoint delegate) {
        super(delegate);
        this.delegate = delegate;
    }
    1
    2
    3
    4
    5
    6
    该类声明了2个@ActuatorGetMapping(“/{name:.*}”)注解的方法:

    get,代码如下:

    @ActuatorGetMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object get(@PathVariable String name) {
    if (!this.delegate.isEnabled()) {
        // Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
        // disabled
        return getDisabledResponse();
    }
    LoggerLevels levels = this.delegate.invoke(name);
    return (levels == null ? ResponseEntity.notFound().build() : levels);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @ActuatorGetMapping(“/{name:.*}”) 与 @PathVariable String name–> rest风格,将其name注入到方法的参数name中,匹配规则是任意字符
    方法逻辑如下:

    如果LoggersEndpoint不可用,则返回默认的不可用消息
    否则,调用LoggersEndpoint#invoke 获得LoggerLevels.代码如下:

    public LoggerLevels invoke(String name) {
    Assert.notNull(name, "Name must not be null");
    LoggerConfiguration configuration = this.loggingSystem
        .getLoggerConfiguration(name);
    return (configuration == null ? null : new LoggerLevels(configuration));
    }
    1
    2
    3
    4
    5
    6
    调用LoggingSystem#getLoggerConfiguration 获得LoggerConfiguration
    如果LoggerConfiguration等于null,则返回null,否则,返回LoggerLevels
    由于spring boot 默认使用的是logback,因此,此处调用的是LogbackLoggingSystem中的实现,代码如下:

    public LoggerConfiguration getLoggerConfiguration(String loggerName) {
    return getLoggerConfiguration(getLogger(loggerName));
    }
    1
    2
    3
    调用getLogger 获得对应的logger,代码如下:

    private ch.qos.logback.classic.Logger getLogger(String name) {
    LoggerContext factory = getLoggerContext();
    if (StringUtils.isEmpty(name) || ROOT_LOGGER_NAME.equals(name)) {
    name = Logger.ROOT_LOGGER_NAME;
    }
    return factory.getLogger(name);
    }
    1
    2
    3
    4
    5
    6
    7
    获得LoggerContext
    如果name为空,或者name等于ROOT,则name赋值为ROOT
    根据name获得对应的Logger
    调用getLoggerConfiguration获得对应的LoggerConfiguration.

    private LoggerConfiguration getLoggerConfiguration(
    ch.qos.logback.classic.Logger logger) {
    if (logger == null) {
    return null;
    }
    LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel());
    LogLevel effectiveLevel = LEVELS
    .convertNativeToSystem(logger.getEffectiveLevel());
    String name = logger.getName();
    if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) {
    name = ROOT_LOGGER_NAME;
    }
    return new LoggerConfiguration(name, level, effectiveLevel);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    如果logger等于null,返回null
    根据logger对应的level,影响的Level分别获得LogLevel
    获得logger对应的name,如果name等于null,或者等于root,则将其赋值为root
    实例化LoggerConfiguration进行返回
    如果 LoggerLevels,则返回ResponseEntity–>状态码为404,否则,直接返回LoggerLevels
    set–>该方法用于设置logger的日志级别.代码如下:

    @ActuatorPostMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object set(@PathVariable String name,
        @RequestBody Map<String, String> configuration) {
    // 1. 如果不可用,则返回默认的不可用信息
    if (!this.delegate.isEnabled()) {
        // Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
        // disabled
        return getDisabledResponse();
    }
    // 2. 根据configuration获得LogLevel,然后对指定的logger设置日志级别
    LogLevel logLevel = getLogLevel(configuration);
    this.delegate.setLogLevel(name, logLevel);
    // 3. 返回ok
    return ResponseEntity.ok().build();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    逻辑如下:

    如果不可用,则返回默认的不可用信息
    根据configuration获得LogLevel,代码如下:

    private LogLevel getLogLevel(Map<String, String> configuration) {
    String level = configuration.get("configuredLevel");
    try {
    return (level == null ? null : LogLevel.valueOf(level.toUpperCase()));
    }
    catch (IllegalArgumentException ex) {
    throw new InvalidLogLevelException(level);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    获得configuredLevel对应的值
    如果没有配置,则返回null,否则,返回LogLevel,合法值有TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF.如果值不合法,抛出InvalidLogLevelException异常.由于InvalidLogLevelException类注有@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = “No such log level”),因此,当抛出该异常时,会返回相应的信息.
    对指定的logger设置日志级别,代码如下:

    public void setLogLevel(String name, LogLevel level) {
    Assert.notNull(name, "Name must not be empty");
    this.loggingSystem.setLogLevel(name, level);
    }
    1
    2
    3
    4
    默认情况下,会执行LogbackLoggingSystem#setLogLevel,代码如下:

    public void setLogLevel(String loggerName, LogLevel level) {
    ch.qos.logback.classic.Logger logger = getLogger(loggerName);
    if (logger != null) {
    logger.setLevel(LEVELS.convertSystemToNative(level));
    }
    }
    1
    2
    3
    4
    5
    6
    根据对应的logger名获得Logger
    将传入的loggerLevel转换为LogBack对应的日志级别
    修改日志级别
    返回ok
    至此,我们明白,要想修改日志级别,可以通过对/loggers/logger名,发送post请求,传入类上如下格式的参数:

    {"configuredLevel":"INFO"}
    1
    即可修改日志级别

    参数配置–>因为该类有@ConfigurationProperties(prefix = “endpoints.loggers”)注解:

    endpoints.loggers.path=/logfile # Endpoint path
    1
    自动化装配:

    在EndpointWebMvcManagementContextConfiguration中进行了声明,代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(LoggersEndpoint.class)
    @ConditionalOnEnabledEndpoint("loggers")
    public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {
        return new LoggersMvcEndpoint(delegate);
    }
    1
    2
    3
    4
    5
    6
    7
    @Bean –> 注册1个id为loggersMvcEndpoint,类型为LoggersMvcEndpoint的bean
    @ConditionalOnMissingBean–> 当BeanFactory中不包含类型为LoggersMvcEndpoint的bean时生效
    @ConditionalOnBean(LoggersEndpoint.class)–> 当BeanFactory中存在LoggersEndpoint类型的bean时生效
    @ConditionalOnEnabledEndpoint(“loggers”)–> 如果配置有endpoints. loggers.enabled = true 或者endpoints.enabled= true 则该配置生效
    ShutdownMvcEndpoint
    该类继承自EndpointMvcAdapter

    构造器如下:

    public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
        super(delegate);
    }
    1
    2
    3
    invoke 实现如下:

    @PostMapping(produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
            MediaType.APPLICATION_JSON_VALUE })
    @ResponseBody
    @Override
    public Object invoke() {
        if (!getDelegate().isEnabled()) {
            return new ResponseEntity<Map<String, String>>(
                    Collections.singletonMap("message", "This endpoint is disabled"),
                    HttpStatus.NOT_FOUND);
        }
        return super.invoke();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    如果ShutdownEndpoint不可用,则返回{message:This endpoint is disabled},否则,调用ShutdownEndpoint#invoke,关闭spring boot 程序

    参数配置–> 因为该类声明了@ConfigurationProperties(prefix = “endpoints.shutdown”):

    endpoints.shutdown.path= # Endpoint path.
    1
    自动装配:

    在EndpointWebMvcManagementContextConfiguration中进行了声明,代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(ShutdownEndpoint.class)
    @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
    public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
        return new ShutdownMvcEndpoint(delegate);
    }
    1
    2
    3
    4
    5
    6
    7
    @Bean –> 注册1个id为shutdownMvcEndpoint,类型为ShutdownMvcEndpoint的bean
    @ConditionalOnMissingBean–> 当BeanFactory中不包含类型为ShutdownMvcEndpoint的bean时生效
    @ConditionalOnBean(ShutdownEndpoint.class)–> 当BeanFactory中存在ShutdownEndpoint类型的bean时生效
    @ConditionalOnEnabledEndpoint(“shutdown”, enabledByDefault = false) –>如果配置有endpoints. shutdown.enabled = true则该配置生效,如果没有配置,该配置不生效
    MetricsMvcEndpoint
    MetricsMvcEndpoint–> 继承自EndpointMvcAdapter.

    字段构造器如下:

    private final MetricsEndpoint delegate;

    public MetricsMvcEndpoint(MetricsEndpoint delegate) {
        super(delegate);
        this.delegate = delegate;
    }
    1
    2
    3
    4
    5
    6
    该类声明了1个被@ActuatorGetMapping注解的方法,如下:

    @ActuatorGetMapping("/{name:.*}")
    @ResponseBody
    @HypermediaDisabled
    public Object value(@PathVariable String name) {
        if (!this.delegate.isEnabled()) {
            return getDisabledResponse();
        }
        return new NamePatternMapFilter(this.delegate.invoke()).getResults(name);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    如果不可用,则返回默认的不可用信息
    实例化NamePatternMapFilter ,之后调用其getResults方法,根据传入的name 获得对应的map,key –>name,value–>name所对应的Metric的值

    NamePatternMapFilter:

    继承了NamePatternFilter 接口,泛型参数为Map
    抽象方法实现如下:

    getValue–> 调用getOptionalValue获得MetricsEndpoint中Metrics名字为传入的值所对应的Metrics的值.,如果获取不到,则抛出NoSuchMetricException.代码如下:

    protected Object getValue(Map<String, ?> source, String name) {
        Object value = getOptionalValue(source, name);
        if (value == null) {
            throw new NoSuchMetricException("No such metric: " + name);
        }
        return value;
    }
    1
    2
    3
    4
    5
    6
    7
    NoSuchMetricException代码如下:

    @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
    public static class NoSuchMetricException extends RuntimeException {

        public NoSuchMetricException(String string) {
            super(string);
        }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    当抛出该异常时,返回的状态码为404,reason为No such metric

    getOptionalValue–> 获得MetricsEndpoint中Metrics名字为传入的值所对应的Metrics的值.代码如下:

    protected Object getOptionalValue(Map<String, ?> source, String name) {
        return source.get(name);
    }
    1
    2
    3
    getNames–>遍历MetricsEndpoint中返回的Metrics名,如果Metrics名符合正则的话(MetricsMvcEndpoint#value方法传入的是正则),则加入到ResultCollectingNameCallback的result中.代码如下:

    protected void getNames(Map<String, ?> source, NameCallback callback) {
        for (String name : source.keySet()) {
            try {
                callback.addName(name);
            }
            catch (NoSuchMetricException ex) {
                // Metric with null value. Continue.
            }
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    自动装配:

    声明在EndpointWebMvcManagementContextConfiguration中.代码如下:

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(MetricsEndpoint.class)
    @ConditionalOnEnabledEndpoint("metrics")
    public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
        return new MetricsMvcEndpoint(delegate);
    }
    1
    2
    3
    4
    5
    6
    7
    @Bean –> 注册1个id为metricsMvcEndpoint,类型为MetricsMvcEndpoint的bean
    @ConditionalOnMissingBean–> BeanFactory中不存在MetricsMvcEndpoint类型的bean时生效
    @ConditionalOnBean(MetricsEndpoint.class)–> BeanFactory中存在MetricsEndpoint类型的bean时生效
    @ConditionalOnEnabledEndpoint(“metrics”) –> 如果配置有endpoints. metrics.enabled = true 或者endpoints.enabled= true 则该配置生效
    ---------------------
    作者:一个努力的码农
    来源:CSDN
    原文:https://blog.csdn.net/qq_26000415/article/details/79220873
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    GridView 激发了未处理的事件“RowEditing”
    C#中字符串转换成枚举类型的方法
    UltraEdit 应用程序发生错误0xc0000417
    配置错误定义了重复的“system.web.extensions/scripting/scriptResourceHandler” 解决办法
    数据绑定中的一个问题"pages enableEventValidation="true""
    jquery json 格式教程
    Delphi的泛型学习
    Delphi取Ajax网页内容
    Delphi 中经常使用的正则表达式
    Delphi中的动态数组总结
  • 原文地址:https://www.cnblogs.com/duanxz/p/11290293.html
Copyright © 2011-2022 走看看