2025/08/11

Spring Boot 3 Actuator

Actuator 一般用在 production 環境,使用 http endpoint 或 JMX 方式監控 app,然後抓取指標數值給資料監控平台。

以此順序介紹 Actuator -> Endpoints -> Metrics -> Grafana


要啟用 actuator,只需要加上 library

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

Endpoint

ref: Endpoints :: Spring Boot

endpoint 是用來監控 spring boot app,spring boot 內建很多endpoints,每個 endpoint 都可透過 http/JMX 方式暴露出去,大部分都是用 http。endpoint 會映射為 /actuator/${ID} ID 就是 endpoint 的 ID,ex: /actuator/health

啟動 app 後,瀏覽網址 http://localhost:8080/actuator/health

結果 UP 代表健康運作中

{
    "status": "UP"
}

spring boot 內建的 endpoints

name desc
auditevents 當前 app 的 audit event
beans 所有 spring beans
caches 可使用的 cache
conditions 設定類別上評估條件及匹配成功與否的原因
configprops 所有 @ConfigurationProperties list
env spring 環境中暴露的所有 properties
flyway 所有 flyway 遷移記錄
health 健康資訊
httpexchanges http exchange message(預設顯示最新的 100個)
info app basic info
integrationgraph spring integration graph
loggers 顯示/修改 logger 設定
liquibase 顯示 liquibase db migration
metrics metric infos
mappings 顯示所有 @RequestMapping
quartz 顯示 quartz jobs
scheduledtasks 顯示 scheduled tasks
sessions 當有 servlet-based web app 使用 Spring Session 時,查詢/刪除 user sessions
shutdown 關閉 app
startup 顯示啟動的資料
threaddump thread dump

使用 web app 時,還有以下這些

name desc
heapdump 回傳 heap dump。HotSpot VM 是回傳 HPROF-format file。OpenJ9 JVM 是回傳 PHD-format file
logfile log file content (需要設定 logging.file.name/logging.file.path)
prometheus 可被 prometheus server 提取的 metric

除了 shotdown 其他所有 endpoint 預設都是開啟的。可透過 management.endpoint.<id>.enabled 啟用/禁用 endpoint。

ex:

management:
  endpoint:
    shutdown:
      enabled: true

禁用所有 endpoint, 只啟用某一個 endpoint

management:
  endpoints:
    enable-by-default: false
  endpoint:
    shutdown:
      enabled: true

expose endpoint

啟用後,不一定能被存取。spring boot 3 預設只以 JMX, Web 方式暴露了 health

Property Default
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include health
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.include health
management:
  endpoints:
    jmx:
      exposure:
        include: "health,info"
    web:
      exposure:
        include: "*"
        exclude: "env,beans"

security

endpoint 提供的資訊需要安全保護

只要有 Spring Security library,就會自動保護所有 endpoints

實作 actuator 的 ManagementWebSecurityAutoConfiguration

註冊在 spring-boot-actuator-autoconfigure

修改 pom.xml 後

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

存取 http://localhost:8080/actuator/info 會自動轉到 Please sign in


如果不想使用 spring boot 預設機制,可改用 SecurityFilterChain

package com.test.actuator;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests((authorize) -> {
                    authorize.requestMatchers("/").permitAll()
                            .requestMatchers(EndpointRequest.to("health")).hasRole("ENDPOINT_ADMIN")
                            .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
                })
                //                .csrf(csrf -> csrf.disable())
                .csrf(csrf -> csrf.ignoringRequestMatchers(EndpointRequest.toAnyEndpoint()))
                .formLogin(withDefaults())
                .logout(logout -> logout.logoutUrl("/"))
                .build();
    }

    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("test").password("{noop}test")
                .roles("ENDPOINT_ADMIN", "ADMIN", "TEST").build());
        manager.createUser(User.withUsername("root").password("{noop}root")
                .roles("ADMIN").build());
        return manager;
    }

}

permit all

@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.securityMatcher(EndpointRequest.toAnyEndpoint());
        http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
        return http.build();
    }

}

customize endpoint mapping

預設 http://localhost:8080/actuator 可取得所有暴露的 endpoints

ex:

{
    "_links": {
        "self": {
            "href": "http://localhost:8080/actuator",
            "templated": false
        },
        "health-path": {
            "href": "http://localhost:8080/actuator/health/{*path}",
            "templated": true
        },
        "health": {
            "href": "http://localhost:8080/actuator/health",
            "templated": false
        }
    }
}

可用以下設定禁用

management:
  endpoints:
    web:
      discovery:
        enabled: false

也可以修改 mapping,這樣要改用 http://localhost:8080/act/hth 取得 health

management:
  endpoints:
    web:
      base-path: /act
      path-mapping:
        health: hth

也可以修改 web port,不使用 service 的 web port。

management:
  server:
    port: 8088
    address: 127.0.0.1

把 port 改為 -1 等同 expose.exclude: "*"

management:
  server:
    port: -1

實作自訂 endpoint

endpoint 啟用時,會自動設定

heath endpoint 是用 HealthEndpointAutoConfiguration。 然後用 @Import 找到不同 web 類型的 endpoint class

health endpoint 是用 HealthEndpoint 類別實作的,該類別有 @Endpoint 註解,且要註冊為 Bean。支援以 @JmxEndpoint @WebEndpoint 暴露方式

@ReadOperation @WriteOperation @DeleteOperation 對應不同的 http method: GET/POST/DELETE

POST request Content-Type 只接受 application/vnd.spring-boot.actuator.v2+json, application/json


新增 User.java

package com.test.actuator;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;

}

TestEndpoint.java

package com.test.actuator.endpoint;

import com.test.actuator.User;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

@Component
@WebEndpoint(id = "test")
public class TestEndpoint {

    @ReadOperation
    public User getUser(@Selector Integer id) {
        return new User(id, "james", 18);
    }

    @WriteOperation
    public User updateUser(int id, @Nullable String name, @Nullable Integer age) {
        User user = getUser(id);
        user.setName(StringUtils.defaultIfBlank(name, user.getName()));
        user.setAge(ObjectUtils.defaultIfNull(age, user.getAge()));
        return user;
    }

}

注意要 expose endpoint

management:
  endpoints:
    web:
      exposure:
        include: "*"

瀏覽網址 http://localhost:8080/actuator/test/1

結果

{"id":1,"name":"james","age":18}

POST/DELETE 預設會回應 403,因為 spring boot 預設會打開 CSRF

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests((authorize) -> {
                    authorize.requestMatchers("/").permitAll()
                            .requestMatchers(EndpointRequest.to("health")).hasRole("ENDPOINT_ADMIN")
                            .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
                })
                .csrf(csrf -> csrf.disable())
//                .csrf(csrf -> csrf.ignoringRequestMatchers(EndpointRequest.toAnyEndpoint()))
                .formLogin(withDefaults())
                .logout(logout -> logout.logoutUrl("/"))
                .build();
    }

enpoint 也支援 CORS 設定

management:
  endpoints:
    web:
      cors:
        allowed-origins: "https://test.com"
        allowed-methods: "GET,POST"

Observability

系統外部觀測正在運作的內部狀態的能力,spring boot 3 透過 Micrometer 提高可觀測性,支援 Micrometer 1.10+

引用可觀測 API,並自動設定 micrometer 追蹤,支援 Brave, OpenTelemetry, Zipkin, Wavefront

使用 micrometer API 完成觀測後,可將數據交給 Zipkin


Metrics

/actuator/metrics 指標,包含了 JVM, system, tomcat, Logger, Spring MVC...

預設不會 expose

瀏覽網址 http://localhost:8080/actuator/metrics 可查閱所有 metrics

http://localhost:8080/actuator/metrics/jvm.memory.max 就是取得 jvm.memory.max 的資訊


可自訂 metric

package com.test.actuator.metrics;

import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.binder.MeterBinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class MetricsConfig {

    @Bean
    public MeterBinder initDate(Environment env) {
        return (registry) -> Gauge.builder("init.date", this::date).register(registry);
    }

    @Bean
    public MeterBinder systemDate(Environment env) {
        return (registry) -> Gauge.builder("system.date", this::date).register(registry);
    }

    private Number date() {
        return 2024.11;
    }

}

網址 http://localhost:8080/actuator/metrics/init.date

結果

{
    "name":"init.date",
    "measurements":[
        {
            "statistic":"VALUE",
            "value":2024.11
        }
    ],
    "availableTags":[

    ]
}

Tracing

  • 使用 OpenTelemetry 結合 Zipkin 或 Wavefront

  • 使用 OpenZipkin Brave 結合 Zipkin 或 Wavefront

OpenTelemetry 可生成/收集/匯出觀測資料 metrics, logs, traces,他只會收集資料,分析跟展示要用其他軟體

Zipkin 是 Twitter 開源的分佈式追蹤系統,可收集解決服務的延遲問題的時間資料。如果 log 有 Trace ID,可直接展示。

       <!-- 將 Micrometer Observation API 橋接到 OpenTelemetry -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-otel</artifactId>
        </dependency>

        <!-- 向 Zipkin 報告跟蹤資訊 -->
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-exporter-zipkin</artifactId>
        </dependency>

採樣設定,預設為 0.1,也就是只採樣 10% 的 request

management:
  tracing:
    sampling:
      probability: 1.0

在 log 顯示 Trace ID, Span ID

logging:
  pattern:
    level: ${spring.application.name:},%X{traceId:-},%X{spanId:-}

Spring Boot Admin

GitHub - codecentric/spring-boot-admin: Admin UI for administration of spring boot applications

這個是社群開發的

  • 有個 server 提供 Spring Boot Actuators UI

  • 有個 client 註冊到 server,並能存取所有 actuator endpoints.

ref: Spring Boot Admin – Getting started

配置 Admin Server

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

在 application 加上 @EnableAdminServer

@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAdminApplication.class, args);
    }
}

配置 Client

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

加上設定

spring:
  boot:
    admin:
      client:
        url: http://localhost:8080
management:
  endpoints:
    web:
      expose:
        include: '*'
  info:
    env:
      enabled: true

禁用所有安全機制

@Configuration
public static class SecurityPermitAllConfig {
    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) {
        return http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests.anyRequest().permitAll())
            .csrf().disable().build();
    }
}

網址 http://localhost:8088/


參考 Spring Boot Admin server notification 可設定通知

Prometheus

要在 pom.xml 加上 library

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>${micrometer-registry-prometheus.version}</version>
        </dependency>

spring boot 就會提供 endpoint /actuator/prometheus 可 pull 資料


加上 libary

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>simpleclient_pushgateway</artifactId>
            <version>${simpleclient_pushgateway.version}</version>
        </dependency>

修改設定

management:
  prometheus:
    metrics:
      export:
        pushgateway:
          enabled: true

可 push 資料


Prometheus 通常結合 Grafana 使用

沒有留言:

張貼留言