Spring Boot 4 Actuator in production: endpoints, security, custom health indicators
Companion repository for the ankurm.com article. Every transcript in docs/output/ was produced by running this project; scripts/run-all.sh regenerates all of them. Verified against Spring Boot 4.1.1 / Framework 7.0.9 / Security 7.1.1 / Micrometer 1.17.1 / kafka-clients 4.2.1 on Temurin JDK 25.0.4.1+1.
This commit is contained in:
95
docs/08-diagnostics.md
Normal file
95
docs/08-diagnostics.md
Normal file
@@ -0,0 +1,95 @@
|
||||
[← 07 Groups and probes](07-groups-and-probes.md) · **08 · The diagnostics endpoint** · [09 Testing Actuator →](09-testing-actuator.md)
|
||||
|
||||
# 08 — The diagnostics endpoint
|
||||
|
||||
[`DiagnosticsEndpoint`](../src/main/java/com/ankurm/actuator/web/DiagnosticsEndpoint.java) is a
|
||||
custom `@Endpoint(id = "diag")` that prints the Actuator state no configuration file will tell
|
||||
you: which endpoints were actually discovered and mapped onto HTTP, what paths and methods each
|
||||
publishes, and which health contributors are registered.
|
||||
|
||||
**Delete it before you ship.** The list of exposed endpoints is itself reconnaissance.
|
||||
|
||||
## Why it exists
|
||||
|
||||
Reading `application.yaml` and predicting exposure is how people ship `/actuator/heapdump` to
|
||||
the internet. `management.endpoints.web.exposure.include: "*"` looks like it exposes everything;
|
||||
it does not expose `heapdump` or `shutdown` ([chapter 03](03-endpoint-catalogue.md)). Conditional
|
||||
endpoints like `httpexchanges` and `startup` are absent unless a specific bean exists. Ask the
|
||||
running application instead of predicting.
|
||||
|
||||
## Writing a custom endpoint
|
||||
|
||||
```java
|
||||
@Component
|
||||
@Endpoint(id = "diag")
|
||||
public class DiagnosticsEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> diagnostics() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
`@ReadOperation` maps to GET, `@WriteOperation` to POST, `@DeleteOperation` to DELETE. The id
|
||||
must be lowercase alphanumeric; it becomes the path segment and the JMX name. You still have to
|
||||
expose it — `include: health,info,diag` or `"*"`.
|
||||
|
||||
The interesting part is the three beans it injects:
|
||||
|
||||
| Bean | Gives you |
|
||||
|---|---|
|
||||
| `WebEndpointsSupplier` | every `ExposableWebEndpoint`, with its `WebOperation` predicates |
|
||||
| `HealthContributorRegistry` | the live contributor tree, composites included |
|
||||
| `Environment` | the effective values of the `management.*` keys |
|
||||
|
||||
`HealthContributorRegistry` is in `org.springframework.boot.health.registry` — new in Boot 4
|
||||
([chapter 02](02-boot-4-changes.md)). It iterates as `HealthContributors.Entry`, and nested
|
||||
`HealthContributors` are composites, which is why the collection is recursive.
|
||||
|
||||
## The output
|
||||
|
||||
From [`output/02-endpoint-catalogue.txt`](output/02-endpoint-catalogue.txt):
|
||||
|
||||
```json
|
||||
{
|
||||
"activeProfiles": ["exposeall", "open"],
|
||||
"serverPort": "8080",
|
||||
"managementPort": "(same as server.port)",
|
||||
"managementBasePath": "/actuator",
|
||||
"exposureInclude": "*",
|
||||
"healthShowDetails": "always",
|
||||
"exposedWebEndpointCount": 14,
|
||||
"exposedWebEndpoints": {
|
||||
"loggers": ["GET loggers", "GET loggers/{name}", "POST loggers/{name}"],
|
||||
"threaddump": ["GET threaddump", "GET threaddump"],
|
||||
...
|
||||
},
|
||||
"healthContributors": [
|
||||
"db (DataSourceHealthIndicator)",
|
||||
"diskSpace (DiskSpaceHealthIndicator)",
|
||||
"externalApi (ExternalApiHealthIndicator)",
|
||||
"kafka (KafkaHealthIndicator)",
|
||||
"livenessState (LivenessStateHealthIndicator)",
|
||||
"ordersDatabase (OrdersDatabaseHealthIndicator)",
|
||||
"ping (PingHealthIndicator)",
|
||||
"readinessState (ReadinessStateHealthIndicator)",
|
||||
"ssl (SslHealthIndicator)"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Two things worth pausing on.
|
||||
|
||||
`"loggers"` shows the `POST` operation explicitly. That is the write endpoint from
|
||||
[chapter 03](03-endpoint-catalogue.md), visible in a list rather than remembered.
|
||||
|
||||
`"threaddump"` appears **twice** with the same path. It publishes two operations that differ
|
||||
only in what they produce — `application/json` and `text/plain`. Content negotiation, not a bug.
|
||||
|
||||
Run the same endpoint under the `mgmtport` profile and `managementPort` reads `9001`,
|
||||
`managementBasePath` reads `/manage`, and the endpoint count is unchanged — the endpoints moved,
|
||||
they did not disappear. That is the fact that breaks path-string security rules
|
||||
([chapter 04](04-securing-actuator.md)).
|
||||
|
||||
---
|
||||
|
||||
[← 07](07-groups-and-probes.md) · **08** · [09 Testing Actuator →](09-testing-actuator.md)
|
||||
Reference in New Issue
Block a user