How do you add custom data to /actuator/info by writing your own InfoContributor?
answer
- implement InfoContributor, register as @Component bean
- builder.withDetail(key, value) / withDetails(map)
- InfoEndpoint auto-aggregates all beans
- runs on every request — keep cheap, no throws
- MapInfoContributor for a static map
basics
~10 sCreate a Spring bean implementing InfoContributor and override contribute(Info.Builder builder), calling builder.withDetail(key, value) to add fields. Because InfoEndpoint aggregates all such beans, your block is merged into the response automatically.
solid answer
~40 sFor dynamic or computed info that static info.* properties can't express, implement InfoContributor. Register the class as a Spring bean (e.g. @Component) and implement contribute(Info.Builder builder), calling builder.withDetail("key", value) — values can be nested Maps/objects and are serialized to JSON. Since InfoEndpoint iterates every InfoContributor bean and merges their Info.Builder output, your contribution appears alongside build/git/env automatically; no registration wiring is needed. Order rarely matters because keys are merged, though colliding top-level keys can overwrite. Keep contribute() cheap and side-effect-free — it runs on every request to /actuator/info — and never expose secrets. Typical uses: feature-flag snapshot, active region, cache stats, or a computed 'healthy since' timestamp.
code
java · 23 linesimport org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class FeatureFlagInfoContributor implements InfoContributor {
private final FeatureFlags flags;
FeatureFlagInfoContributor(FeatureFlags flags) {
this.flags = flags;
}
@Override
public void contribute(Info.Builder builder) {
// Merged into /actuator/info under a "features" key
builder.withDetail("features", Map.of(
"enabled", flags.enabledNames(),
"count", flags.enabledNames().size()
));
}
}go deeper
Know a custom class implementing InfoContributor can add fields.
Write one with @Component and withDetail, and know it's auto-aggregated.
Reason about per-request execution cost, exception safety, key collisions, and MapInfoContributor.
Set conventions for what belongs in info, caching strategy, and guardrails against leaking sensitive data.
## Why write one The env contributor handles **static** metadata via `info.*` properties. When you need **dynamic or computed** data — a value read from a bean, a live count, a feature-flag snapshot — you implement `org.springframework.boot.actuate.info.InfoContributor` yourself. ## The mechanics 1. Implement the interface and register it as a bean: ```java @Component public class RegionInfoContributor implements InfoContributor { private final RegionService regionService; RegionInfoContributor(RegionService regionService) { this.regionService = regionService; } @Override public void contribute(Info.Builder builder) { builder.withDetail("deployment", Map.of( "region", regionService.currentRegion(), "activeFeatures", regionService.enabledFeatureFlags() )); } } ``` 2. `InfoEndpoint` (auto-configured by Actuator) collects **all** `InfoContributor` beans and calls `contribute` on each, accumulating into a single `Info.Builder`. Your `deployment` block is merged with `build`, `git`, etc. No extra registration is needed — being a bean is enough. ## Info.Builder API - `withDetail(String key, Object value)` — add one entry (value serialized via Jackson, so nested `Map`/POJO works). - `withDetails(Map<String, Object>)` — add many at once. The builder is per-request; contributions do not persist between calls. ## Execution semantics & gotchas - **Called on every request** to `/actuator/info`. Keep `contribute()` fast, non-blocking, and free of heavy I/O; cache expensive values. - **No exceptions, please** — a contributor that throws can break the whole endpoint response; guard external calls. - **Key collisions** — if two contributors add the same top-level key, the later one can overwrite; namespace your keys. - **Ordering** — contributors implement no guaranteed order by default; if you need deterministic override behavior you can order beans, but prefer unique keys instead. - **Security** — the response is often exposed to operators/monitoring; never place credentials, tokens, or PII in `withDetail`. ## Alternative: MapInfoContributor For a fixed map you can register a `MapInfoContributor(Map<String, Object>)` bean instead of writing a class — handy for simple static-but-code-provided data. ## When to use Reach for a custom `InfoContributor` when the metadata is computed at runtime or sourced from application beans. For plain constants, prefer `info.*` properties (less code, same result).
- Your custom contributor makes a slow DB call in contribute(). What's the risk and the fix?contribute() runs on every /actuator/info request, so the slow call is repeated and can hang the endpoint under load. Cache the value (e.g. refresh periodically) and keep contribute() cheap.
- How does Spring know to call your contributor without you registering it anywhere special?InfoEndpoint is auto-configured with all InfoContributor beans injected as a list; being a Spring bean is sufficient — it iterates them and merges their Info.Builder output.
saying these in an interview costs you the question
- Manually wiring the contributor into InfoEndpoint (unnecessary — bean injection is automatic)
- Doing heavy I/O or blocking calls in contribute() without caching
- Letting contribute() throw and break the endpoint
- Putting secrets/credentials into withDetail