How do you register a custom health Status (e.g. FATAL) and wire it correctly into severity ordering and HTTP mapping?
answer
- Status = code + description
- Health.status(new Status("FATAL"))
- add to status.order (or it's least severe)
- add to status.http-mapping (or it's 200)
- 3 steps: emit, order, map
basics
~10 sCreate a new Status("FATAL") and return it from a HealthIndicator via Health.status(...). Then add FATAL to management.endpoint.health.status.order so it's ranked correctly, and to status.http-mapping so it returns the right HTTP code.
solid answer
~30 sA Status is just a code string plus optional description; the built-ins are UP, DOWN, OUT_OF_SERVICE, UNKNOWN. To add a custom one, construct new Status("FATAL", "...") and return it from a HealthIndicator using Health.status(...).build(). But defining it isn't enough: SimpleStatusAggregator treats any code not in the order list as least-severe, so you must add FATAL to management.endpoint.health.status.order (e.g. FATAL,DOWN,OUT_OF_SERVICE,UP,UNKNOWN) so it can win aggregation. And SimpleHttpCodeStatusMapper maps unmapped statuses to 200, so you must add management.endpoint.health.status.http-mapping.fatal=503 for probes to react. Miss either and the endpoint under-reacts: FATAL either loses to UP or returns 200 despite being fatal.
code
java · 30 linesimport org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
@Component
public class DataIntegrityHealthIndicator implements HealthIndicator {
public static final Status FATAL = new Status("FATAL", "unrecoverable");
@Override
public Health health() {
if (isCorrupted()) {
return Health.status(FATAL).withDetail("reason", "checksum mismatch").build();
}
return Health.up().build();
}
private boolean isCorrupted() { /* ... */ return false; }
}
/* application.yml:
management:
endpoint:
health:
status:
order: FATAL,DOWN,OUT_OF_SERVICE,UP,UNKNOWN
http-mapping:
fatal: 503
*/go deeper
Know that Status has built-in codes and you can create new ones.
Emit a custom status from a HealthIndicator.
Wire order + http-mapping and explain why both are required.
Weigh whether a custom status is justified vs. downstream consumer complexity and group-level config.
**What a Status is.** `org.springframework.boot.actuate.health.Status` is a simple value: a `code` (String) and an optional `description`. The framework predefines four constants: `Status.UP`, `Status.DOWN`, `Status.OUT_OF_SERVICE`, `Status.UNKNOWN`. A "custom status" is just any other code string you choose, e.g. `"FATAL"`, `"DEGRADED"`. **Step 1 — emit it from a contributor.** Implement `HealthIndicator` (or `AbstractHealthIndicator`) and build a `Health` carrying your custom status: ```java return Health.status(new Status("FATAL", "data corruption detected")).build(); ``` `Health.status(...)` accepts either a `Status` object or a code String. **Step 2 — rank it in the aggregator.** `SimpleStatusAggregator` sorts by the configured severity order and returns the most severe. Codes **absent** from the order list are placed *after* the known codes (least severe, alphabetical tiebreak). So a bare `FATAL` would *lose* to `UP`. Fix by declaring the full order most-severe-first: ```yaml management.endpoint.health.status.order: FATAL,DOWN,OUT_OF_SERVICE,UP,UNKNOWN ``` Now a single FATAL contributor makes the whole endpoint FATAL. **Step 3 — map it to an HTTP code.** `SimpleHttpCodeStatusMapper` maps any status without an explicit entry to **200**. So even after FATAL wins aggregation, the response would be `200 FATAL` — probes wouldn't react. Add: ```yaml management.endpoint.health.status.http-mapping.fatal: 503 ``` (status keys are case-insensitive.) **Putting it together — the two-property rule.** Registering a custom status is a **three-part** job: (1) emit it, (2) order it, (3) map it. The two config properties (`status.order` and `status.http-mapping`) are independent — one governs *which status wins*, the other governs *what code is returned*. Forgetting either is the classic bug. **Edge cases & gotchas.** - **Description isn't part of identity/severity.** Two `Status` objects are equal by code; the description is cosmetic and appears in the body only when details are shown. - **Custom status + show-details.** The custom code appears in the body's `status` field regardless of show-details; the *description*/details map appears only per show-details rules. - **Multiple custom statuses** each need their own order and mapping entries. - **Groups** can have their own aggregator/order, so a custom status might need ordering configured per group if you use health groups. - **Don't over-engineer.** Prefer the built-in four unless you genuinely need a distinct operational meaning (e.g. a status that pages on-call vs. one that merely degrades). Extra statuses complicate every downstream consumer.
- You emitted FATAL and added it to status.order, but /actuator/health still returns HTTP 200 when FATAL. What's missing?The http-mapping. SimpleHttpCodeStatusMapper maps unlisted statuses to 200; add management.endpoint.health.status.http-mapping.fatal=503.
- Does the Status description affect aggregation or the HTTP code?No. Status equality/ordering is by code only; the description is cosmetic and shown only in the details body. Ordering and mapping key off the code string.
saying these in an interview costs you the question
- Thinking emitting a custom Status is sufficient without ordering/mapping config
- Assuming custom statuses are automatically most-severe or auto-map to 503
- Confusing status.order with status.http-mapping