spring boot 源碼解析52-actuate中MVCEndPoint解析

今天有個別項目的jolokia的endpoint不能訪問,調試源碼發現:endpoint.enabled的開關致使的。html

關於Endpoint,前端

Springboot Endpoint之二:Endpoint源碼剖析正則表達式


以前的幾篇文章分析了spring boot 中有關endpoint的實現,細心的朋友能夠發現,在org.springframework.boot.actuate.endpoint.mvc 包下也有一系列的xxxEndpoint,這又是爲何呢?spring

 



緣由是: 咱們不少狀況下,都是訪問接口的方式獲取應用的監控,以前的分析是其實現的底層,要想實現經過接口訪問,還須要對其進行包裝一番,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
版權聲明:本文爲博主原創文章,轉載請附上博文連接!json

相關文章
相關標籤/搜索