Add health endpoint 'show-components' support

Add a `show-components` property under `management.endpoint.health` and
`management.endpoint.health.group.<name>` that can be used to change
when components are displayed.

Prior to this commit it was only possible to set `show-details` which
offered an "all or nothing" approach to the resulting JSON. The new
switch allows component information to be displayed whilst still hiding
potentially sensitive details returned from the actual `HealthIndicator`.

Closes gh-15076
pull/18371/head
Phillip Webb 5 years ago
parent 69c561a69a
commit a94ab673a3

@ -24,6 +24,9 @@ The following table describes the structure of the response:
[cols="2,1,3"] [cols="2,1,3"]
include::{snippets}health/response-fields.adoc[] include::{snippets}health/response-fields.adoc[]
NOTE: The response fields above are for the V3 API.
If you need to return V2 JSON you should use an accept header or `application/vnd.spring-boot.actuator.v2+json`
[[health-retrieving-component]] [[health-retrieving-component]]

@ -19,7 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.health;
import java.util.Collection; import java.util.Collection;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.ShowDetails; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
@ -40,7 +40,9 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
private final HttpCodeStatusMapper httpCodeStatusMapper; private final HttpCodeStatusMapper httpCodeStatusMapper;
private final ShowDetails showDetails; private final Show showComponents;
private final Show showDetails;
private final Collection<String> roles; private final Collection<String> roles;
@ -49,14 +51,17 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
* @param members a predicate used to test for group membership * @param members a predicate used to test for group membership
* @param statusAggregator the status aggregator to use * @param statusAggregator the status aggregator to use
* @param httpCodeStatusMapper the HTTP code status mapper to use * @param httpCodeStatusMapper the HTTP code status mapper to use
* @param showComponents the show components setting
* @param showDetails the show details setting * @param showDetails the show details setting
* @param roles the roles to match * @param roles the roles to match
*/ */
AutoConfiguredHealthEndpointGroup(Predicate<String> members, StatusAggregator statusAggregator, AutoConfiguredHealthEndpointGroup(Predicate<String> members, StatusAggregator statusAggregator,
HttpCodeStatusMapper httpCodeStatusMapper, ShowDetails showDetails, Collection<String> roles) { HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails,
Collection<String> roles) {
this.members = members; this.members = members;
this.statusAggregator = statusAggregator; this.statusAggregator = statusAggregator;
this.httpCodeStatusMapper = httpCodeStatusMapper; this.httpCodeStatusMapper = httpCodeStatusMapper;
this.showComponents = showComponents;
this.showDetails = showDetails; this.showDetails = showDetails;
this.roles = roles; this.roles = roles;
} }
@ -67,9 +72,20 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
} }
@Override @Override
public boolean includeDetails(SecurityContext securityContext) { public boolean showComponents(SecurityContext securityContext) {
ShowDetails showDetails = this.showDetails; if (this.showComponents == null) {
switch (showDetails) { return showDetails(securityContext);
}
return getShowResult(securityContext, this.showComponents);
}
@Override
public boolean showDetails(SecurityContext securityContext) {
return getShowResult(securityContext, this.showDetails);
}
private boolean getShowResult(SecurityContext securityContext, Show show) {
switch (show) {
case NEVER: case NEVER:
return false; return false;
case ALWAYS: case ALWAYS:
@ -77,7 +93,7 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
case WHEN_AUTHORIZED: case WHEN_AUTHORIZED:
return isAuthorized(securityContext); return isAuthorized(securityContext);
} }
throw new IllegalStateException("Unsupported ShowDetails value " + showDetails); throw new IllegalStateException("Unsupported 'show' value " + show);
} }
private boolean isAuthorized(SecurityContext securityContext) { private boolean isAuthorized(SecurityContext securityContext) {

@ -31,7 +31,7 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.ShowDetails; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status;
import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HealthEndpointGroups;
@ -65,7 +65,8 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) {
ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext) ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext)
? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext; ? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext;
ShowDetails showDetails = properties.getShowDetails(); Show showComponents = properties.getShowComponents();
Show showDetails = properties.getShowDetails();
Set<String> roles = properties.getRoles(); Set<String> roles = properties.getRoles();
StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class); StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class);
if (statusAggregator == null) { if (statusAggregator == null) {
@ -76,18 +77,20 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()); httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping());
} }
this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper,
showDetails, roles); showComponents, showDetails, roles);
this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper, this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper,
showDetails, roles); showComponents, showDetails, roles);
} }
private Map<String, HealthEndpointGroup> createGroups(Map<String, Group> groupProperties, BeanFactory beanFactory, private Map<String, HealthEndpointGroup> createGroups(Map<String, Group> groupProperties, BeanFactory beanFactory,
StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper, StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper,
ShowDetails defaultShowDetails, Set<String> defaultRoles) { Show defaultShowComponents, Show defaultShowDetails, Set<String> defaultRoles) {
Map<String, HealthEndpointGroup> groups = new LinkedHashMap<String, HealthEndpointGroup>(); Map<String, HealthEndpointGroup> groups = new LinkedHashMap<String, HealthEndpointGroup>();
groupProperties.forEach((groupName, group) -> { groupProperties.forEach((groupName, group) -> {
Status status = group.getStatus(); Status status = group.getStatus();
ShowDetails showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails; Show showComponents = (group.getShowComponents() != null) ? group.getShowComponents()
: defaultShowComponents;
Show showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails;
Set<String> roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles; Set<String> roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles;
StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> {
if (!CollectionUtils.isEmpty(status.getOrder())) { if (!CollectionUtils.isEmpty(status.getOrder())) {
@ -104,7 +107,7 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
}); });
Predicate<String> members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude()); Predicate<String> members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude());
groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper,
showDetails, roles)); showComponents, showDetails, roles));
}); });
return Collections.unmodifiableMap(groups); return Collections.unmodifiableMap(groups);
} }

@ -38,10 +38,15 @@ public abstract class HealthProperties {
@NestedConfigurationProperty @NestedConfigurationProperty
private final Status status = new Status(); private final Status status = new Status();
/**
* When to show components. If not specified the 'show-details' setting will be used.
*/
private Show showComponents;
/** /**
* When to show full health details. * When to show full health details.
*/ */
private ShowDetails showDetails = ShowDetails.NEVER; private Show showDetails = Show.NEVER;
/** /**
* Roles used to determine whether or not a user is authorized to be shown details. * Roles used to determine whether or not a user is authorized to be shown details.
@ -53,11 +58,19 @@ public abstract class HealthProperties {
return this.status; return this.status;
} }
public ShowDetails getShowDetails() { public Show getShowComponents() {
return this.showComponents;
}
public void setShowComponents(Show showComponents) {
this.showComponents = showComponents;
}
public Show getShowDetails() {
return this.showDetails; return this.showDetails;
} }
public void setShowDetails(ShowDetails showDetails) { public void setShowDetails(Show showDetails) {
this.showDetails = showDetails; this.showDetails = showDetails;
} }
@ -102,23 +115,23 @@ public abstract class HealthProperties {
} }
/** /**
* Options for showing details in responses from the {@link HealthEndpoint} web * Options for showing items in responses from the {@link HealthEndpoint} web
* extensions. * extensions.
*/ */
public enum ShowDetails { public enum Show {
/** /**
* Never show details in the response. * Never show the item in the response.
*/ */
NEVER, NEVER,
/** /**
* Show details in the response when accessed by an authorized user. * Show the item in the response when accessed by an authorized user.
*/ */
WHEN_AUTHORIZED, WHEN_AUTHORIZED,
/** /**
* Always show details in the response. * Always show the item in the response.
*/ */
ALWAYS ALWAYS

@ -146,7 +146,12 @@ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
} }
@Override @Override
public boolean includeDetails(SecurityContext securityContext) { public boolean showComponents(SecurityContext securityContext) {
return true;
}
@Override
public boolean showDetails(SecurityContext securityContext) {
return true; return true;
} }

@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.ShowDetails; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator; import org.springframework.boot.actuate.health.StatusAggregator;
@ -60,7 +60,7 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void isMemberWhenMemberPredicateMatchesAcceptsTrue() { void isMemberWhenMemberPredicateMatchesAcceptsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.isMember("albert")).isTrue(); assertThat(group.isMember("albert")).isTrue();
assertThat(group.isMember("arnold")).isTrue(); assertThat(group.isMember("arnold")).isTrue();
} }
@ -68,72 +68,134 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void isMemberWhenMemberPredicateRejectsReturnsTrue() { void isMemberWhenMemberPredicateRejectsReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.isMember("bert")).isFalse(); assertThat(group.isMember("bert")).isFalse();
assertThat(group.isMember("ernie")).isFalse(); assertThat(group.isMember("ernie")).isFalse();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsNeverReturnsFalse() { void showDetailsWhenShowDetailsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.NEVER, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
assertThat(group.includeDetails(SecurityContext.NONE)).isFalse(); assertThat(group.showDetails(SecurityContext.NONE)).isFalse();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsAlwaysReturnsTrue() { void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.includeDetails(SecurityContext.NONE)).isTrue(); assertThat(group.showDetails(SecurityContext.NONE)).isTrue();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(null); given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.includeDetails(this.securityContext)).isFalse(); assertThat(group.showDetails(this.securityContext)).isFalse();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.includeDetails(this.securityContext)).isTrue(); assertThat(group.showDetails(this.securityContext)).isTrue();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "root", "bossmode")); Arrays.asList("admin", "root", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.includeDetails(this.securityContext)).isTrue(); assertThat(group.showDetails(this.securityContext)).isTrue();
} }
@Test @Test
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "rot", "bossmode")); Arrays.asList("admin", "rot", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.includeDetails(this.securityContext)).isFalse(); assertThat(group.showDetails(this.securityContext)).isFalse();
}
@Test
void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() {
AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue();
AutoConfiguredHealthEndpointGroup neverGroup = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse();
}
@Test
void showComponentsWhenShowDetailsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet());
assertThat(group.showComponents(SecurityContext.NONE)).isFalse();
}
@Test
void showComponentsWhenShowDetailsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet());
assertThat(group.showComponents(SecurityContext.NONE)).isTrue();
}
@Test
void showComponentsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.showComponents(this.securityContext)).isFalse();
}
@Test
void showComponentsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showComponents(this.securityContext)).isTrue();
}
@Test
void showComponentsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "root", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.showComponents(this.securityContext)).isTrue();
}
@Test
void showComponentsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "rot", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.showComponents(this.securityContext)).isFalse();
} }
@Test @Test
void getStatusAggregatorReturnsStatusAggregator() { void getStatusAggregatorReturnsStatusAggregator() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator); assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator);
} }
@Test @Test
void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() { void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper); assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper);
} }

@ -89,12 +89,13 @@ class AutoConfiguredHealthEndpointGroupsTests {
@Test @Test
void createWhenNoDefinedBeansAdaptsProperties() { void createWhenNoDefinedBeansAdaptsProperties() {
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always", this.contextRunner.withPropertyValues("management.endpoint.health.show-components=always",
"management.endpoint.health.status.order=up,down", "management.endpoint.health.show-details=never", "management.endpoint.health.status.order=up,down",
"management.endpoint.health.status.http-mapping.down=200").run((context) -> { "management.endpoint.health.status.http-mapping.down=200").run((context) -> {
HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class);
HealthEndpointGroup primary = groups.getPrimary(); HealthEndpointGroup primary = groups.getPrimary();
assertThat(primary.includeDetails(SecurityContext.NONE)).isTrue(); assertThat(primary.showComponents(SecurityContext.NONE)).isTrue();
assertThat(primary.showDetails(SecurityContext.NONE)).isFalse();
assertThat(primary.getStatusAggregator().getAggregateStatus(Status.UP, Status.DOWN)) assertThat(primary.getStatusAggregator().getAggregateStatus(Status.UP, Status.DOWN))
.isEqualTo(Status.UP); .isEqualTo(Status.UP);
assertThat(primary.getHttpCodeStatusMapper().getStatusCode(Status.DOWN)).isEqualTo(200); assertThat(primary.getHttpCodeStatusMapper().getStatusCode(Status.DOWN)).isEqualTo(200);

@ -82,8 +82,8 @@ public class HealthEndpoint extends HealthEndpointSupport<HealthContributor, Hea
@Override @Override
protected HealthComponent aggregateContributions(ApiVersion apiVersion, Map<String, HealthComponent> contributions, protected HealthComponent aggregateContributions(ApiVersion apiVersion, Map<String, HealthComponent> contributions,
StatusAggregator statusAggregator, boolean includeDetails, Set<String> groupNames) { StatusAggregator statusAggregator, boolean showComponents, Set<String> groupNames) {
return getCompositeHealth(apiVersion, contributions, statusAggregator, includeDetails, groupNames); return getCompositeHealth(apiVersion, contributions, statusAggregator, showComponents, groupNames);
} }
} }

@ -35,12 +35,20 @@ public interface HealthEndpointGroup {
boolean isMember(String name); boolean isMember(String name);
/** /**
* Returns if {@link Health#getDetails() health details} should be included in the * Returns if {@link CompositeHealth#getComponents() health components} should be
* shown in the response.
* @param securityContext the endpoint security context
* @return {@code true} to shown details or {@code false} to hide them
*/
boolean showComponents(SecurityContext securityContext);
/**
* Returns if {@link Health#getDetails() health details} should be shown in the
* response. * response.
* @param securityContext the endpoint security context * @param securityContext the endpoint security context
* @return {@code true} to included details or {@code false} to hide them * @return {@code true} to shown details or {@code false} to hide them
*/ */
boolean includeDetails(SecurityContext securityContext); boolean showDetails(SecurityContext securityContext);
/** /**
* Returns the status aggregator that should be used for this group. * Returns the status aggregator that should be used for this group.

@ -60,25 +60,25 @@ abstract class HealthEndpointSupport<C, T> {
this.groups = groups; this.groups = groups;
} }
HealthResult<T> getHealth(ApiVersion apiVersion, SecurityContext securityContext, boolean alwaysIncludeDetails, HealthResult<T> getHealth(ApiVersion apiVersion, SecurityContext securityContext, boolean showAll, String... path) {
String... path) {
HealthEndpointGroup group = (path.length > 0) ? this.groups.get(path[0]) : null; HealthEndpointGroup group = (path.length > 0) ? this.groups.get(path[0]) : null;
if (group != null) { if (group != null) {
return getHealth(apiVersion, group, securityContext, alwaysIncludeDetails, path, 1); return getHealth(apiVersion, group, securityContext, showAll, path, 1);
} }
return getHealth(apiVersion, this.groups.getPrimary(), securityContext, alwaysIncludeDetails, path, 0); return getHealth(apiVersion, this.groups.getPrimary(), securityContext, showAll, path, 0);
} }
private HealthResult<T> getHealth(ApiVersion apiVersion, HealthEndpointGroup group, SecurityContext securityContext, private HealthResult<T> getHealth(ApiVersion apiVersion, HealthEndpointGroup group, SecurityContext securityContext,
boolean alwaysIncludeDetails, String[] path, int pathOffset) { boolean showAll, String[] path, int pathOffset) {
boolean includeDetails = alwaysIncludeDetails || group.includeDetails(securityContext); boolean showComponents = showAll || group.showComponents(securityContext);
boolean showDetails = showAll || group.showDetails(securityContext);
boolean isSystemHealth = group == this.groups.getPrimary() && pathOffset == 0; boolean isSystemHealth = group == this.groups.getPrimary() && pathOffset == 0;
boolean isRoot = path.length - pathOffset == 0; boolean isRoot = path.length - pathOffset == 0;
if (!includeDetails && !isRoot) { if (!showComponents && !isRoot) {
return null; return null;
} }
Object contributor = getContributor(path, pathOffset); Object contributor = getContributor(path, pathOffset);
T health = getContribution(apiVersion, group, contributor, includeDetails, T health = getContribution(apiVersion, group, contributor, showComponents, showDetails,
isSystemHealth ? this.groups.getNames() : null); isSystemHealth ? this.groups.getNames() : null);
return (health != null) ? new HealthResult<T>(health, group) : null; return (health != null) ? new HealthResult<T>(health, group) : null;
} }
@ -98,46 +98,47 @@ abstract class HealthEndpointSupport<C, T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private T getContribution(ApiVersion apiVersion, HealthEndpointGroup group, Object contributor, private T getContribution(ApiVersion apiVersion, HealthEndpointGroup group, Object contributor,
boolean includeDetails, Set<String> groupNames) { boolean showComponents, boolean showDetails, Set<String> groupNames) {
if (contributor instanceof NamedContributors) { if (contributor instanceof NamedContributors) {
return getAggregateHealth(apiVersion, group, (NamedContributors<C>) contributor, includeDetails, return getAggregateHealth(apiVersion, group, (NamedContributors<C>) contributor, showComponents,
groupNames); showDetails, groupNames);
} }
return (contributor != null) ? getHealth((C) contributor, includeDetails) : null; return (contributor != null) ? getHealth((C) contributor, showDetails) : null;
} }
private T getAggregateHealth(ApiVersion apiVersion, HealthEndpointGroup group, private T getAggregateHealth(ApiVersion apiVersion, HealthEndpointGroup group,
NamedContributors<C> namedContributors, boolean includeDetails, Set<String> groupNames) { NamedContributors<C> namedContributors, boolean showComponents, boolean showDetails,
Set<String> groupNames) {
Map<String, T> contributions = new LinkedHashMap<>(); Map<String, T> contributions = new LinkedHashMap<>();
for (NamedContributor<C> namedContributor : namedContributors) { for (NamedContributor<C> namedContributor : namedContributors) {
String name = namedContributor.getName(); String name = namedContributor.getName();
if (group.isMember(name)) { if (group.isMember(name)) {
T contribution = getContribution(apiVersion, group, namedContributor.getContributor(), includeDetails, T contribution = getContribution(apiVersion, group, namedContributor.getContributor(), showComponents,
null); showDetails, null);
contributions.put(name, contribution); contributions.put(name, contribution);
} }
} }
if (contributions.isEmpty()) { if (contributions.isEmpty()) {
return null; return null;
} }
return aggregateContributions(apiVersion, contributions, group.getStatusAggregator(), includeDetails, return aggregateContributions(apiVersion, contributions, group.getStatusAggregator(), showComponents,
groupNames); groupNames);
} }
protected abstract T getHealth(C contributor, boolean includeDetails); protected abstract T getHealth(C contributor, boolean includeDetails);
protected abstract T aggregateContributions(ApiVersion apiVersion, Map<String, T> contributions, protected abstract T aggregateContributions(ApiVersion apiVersion, Map<String, T> contributions,
StatusAggregator statusAggregator, boolean includeDetails, Set<String> groupNames); StatusAggregator statusAggregator, boolean showComponents, Set<String> groupNames);
protected final CompositeHealth getCompositeHealth(ApiVersion apiVersion, Map<String, HealthComponent> components, protected final CompositeHealth getCompositeHealth(ApiVersion apiVersion, Map<String, HealthComponent> components,
StatusAggregator statusAggregator, boolean includeDetails, Set<String> groupNames) { StatusAggregator statusAggregator, boolean showComponents, Set<String> groupNames) {
Status status = statusAggregator.getAggregateStatus( Status status = statusAggregator.getAggregateStatus(
components.values().stream().map(HealthComponent::getStatus).collect(Collectors.toSet())); components.values().stream().map(HealthComponent::getStatus).collect(Collectors.toSet()));
Map<String, HealthComponent> includedComponents = includeDetails ? components : null; Map<String, HealthComponent> instances = showComponents ? components : null;
if (groupNames != null) { if (groupNames != null) {
return new SystemHealth(apiVersion, status, includedComponents, groupNames); return new SystemHealth(apiVersion, status, instances, groupNames);
} }
return new CompositeHealth(apiVersion, status, includedComponents); return new CompositeHealth(apiVersion, status, instances);
} }
/** /**

@ -76,8 +76,8 @@ public class HealthEndpointWebExtension extends HealthEndpointSupport<HealthCont
} }
public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion, SecurityContext securityContext, public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion, SecurityContext securityContext,
boolean alwaysIncludeDetails, String... path) { boolean showAll, String... path) {
HealthResult<HealthComponent> result = getHealth(apiVersion, securityContext, alwaysIncludeDetails, path); HealthResult<HealthComponent> result = getHealth(apiVersion, securityContext, showAll, path);
if (result == null) { if (result == null) {
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND); return new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND);
} }
@ -94,8 +94,8 @@ public class HealthEndpointWebExtension extends HealthEndpointSupport<HealthCont
@Override @Override
protected HealthComponent aggregateContributions(ApiVersion apiVersion, Map<String, HealthComponent> contributions, protected HealthComponent aggregateContributions(ApiVersion apiVersion, Map<String, HealthComponent> contributions,
StatusAggregator statusAggregator, boolean includeDetails, Set<String> groupNames) { StatusAggregator statusAggregator, boolean showComponents, Set<String> groupNames) {
return getCompositeHealth(apiVersion, contributions, statusAggregator, includeDetails, groupNames); return getCompositeHealth(apiVersion, contributions, statusAggregator, showComponents, groupNames);
} }
} }

@ -78,9 +78,8 @@ public class ReactiveHealthEndpointWebExtension
} }
public Mono<WebEndpointResponse<? extends HealthComponent>> health(ApiVersion apiVersion, public Mono<WebEndpointResponse<? extends HealthComponent>> health(ApiVersion apiVersion,
SecurityContext securityContext, boolean alwaysIncludeDetails, String... path) { SecurityContext securityContext, boolean showAll, String... path) {
HealthResult<Mono<? extends HealthComponent>> result = getHealth(apiVersion, securityContext, HealthResult<Mono<? extends HealthComponent>> result = getHealth(apiVersion, securityContext, showAll, path);
alwaysIncludeDetails, path);
if (result == null) { if (result == null) {
return Mono.just(new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND)); return Mono.just(new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND));
} }
@ -99,10 +98,10 @@ public class ReactiveHealthEndpointWebExtension
@Override @Override
protected Mono<? extends HealthComponent> aggregateContributions(ApiVersion apiVersion, protected Mono<? extends HealthComponent> aggregateContributions(ApiVersion apiVersion,
Map<String, Mono<? extends HealthComponent>> contributions, StatusAggregator statusAggregator, Map<String, Mono<? extends HealthComponent>> contributions, StatusAggregator statusAggregator,
boolean includeDetails, Set<String> groupNames) { boolean showComponents, Set<String> groupNames) {
return Flux.fromIterable(contributions.entrySet()).flatMap(NamedHealthComponent::create) return Flux.fromIterable(contributions.entrySet()).flatMap(NamedHealthComponent::create)
.collectMap(NamedHealthComponent::getName, NamedHealthComponent::getHealth).map((components) -> this .collectMap(NamedHealthComponent::getName, NamedHealthComponent::getHealth).map((components) -> this
.getCompositeHealth(apiVersion, components, statusAggregator, includeDetails, groupNames)); .getCompositeHealth(apiVersion, components, statusAggregator, showComponents, groupNames));
} }
/** /**

@ -76,7 +76,7 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenPathIsEmptyUsesPrimaryGroup() { void getHealthWhenPathIsEmptyUsesPrimaryGroup() {
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false); false);
@ -86,7 +86,7 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenPathIsNotGroupReturnsResultFromPrimaryGroup() { void getHealthWhenPathIsNotGroupReturnsResultFromPrimaryGroup() {
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "test"); false, "test");
@ -96,7 +96,7 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenPathIsGroupReturnsResultFromGroup() { void getHealthWhenPathIsGroupReturnsResultFromGroup() {
this.registry.registerContributor("atest", createContributor(this.up)); this.registry.registerContributor("atest", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "alltheas", "atest"); false, "alltheas", "atest");
@ -105,7 +105,44 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenAlwaysIncludesDetailsIsFalseAndGroupIsTrueIncludesDetails() { void getHealthWhenAlwaysShowIsFalseAndGroupIsTrueShowsComponents() {
C contributor = createContributor(this.up);
C compositeContributor = createCompositeContributor(Collections.singletonMap("spring", contributor));
this.registry.registerContributor("test", compositeContributor);
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "test");
CompositeHealth health = (CompositeHealth) getHealth(result);
assertThat(health.getComponents()).containsKey("spring");
}
@Test
void getHealthWhenAlwaysShowIsFalseAndGroupIsFalseCannotAccessComponent() {
this.primaryGroup.setShowComponents(false);
C contributor = createContributor(this.up);
C compositeContributor = createCompositeContributor(Collections.singletonMap("spring", contributor));
this.registry.registerContributor("test", compositeContributor);
HealthEndpointSupport<C, T> endpoint = create(this.registry, this.groups);
HealthResult<T> rootResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false);
assertThat(((CompositeHealth) getHealth(rootResult)).getComponents()).isNullOrEmpty();
HealthResult<T> componentResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false, "test");
assertThat(componentResult).isNull();
}
@Test
void getHealthWhenAlwaysShowIsTrueShowsComponents() {
this.primaryGroup.setShowComponents(true);
C contributor = createContributor(this.up);
C compositeContributor = createCompositeContributor(Collections.singletonMap("spring", contributor));
this.registry.registerContributor("test", compositeContributor);
HealthEndpointSupport<C, T> endpoint = create(this.registry, this.groups);
HealthResult<T> rootResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false);
assertThat(((CompositeHealth) getHealth(rootResult)).getComponents()).containsKey("test");
HealthResult<T> componentResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false, "test");
assertThat(((CompositeHealth) getHealth(componentResult)).getComponents()).containsKey("spring");
}
@Test
void getHealthWhenAlwaysShowIsFalseAndGroupIsTrueShowsDetails() {
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "test"); false, "test");
@ -113,8 +150,8 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenAlwaysIncludesDetailsIsFalseAndGroupIsFalseIncludesNoDetails() { void getHealthWhenAlwaysShowIsFalseAndGroupIsFalseShowsNoDetails() {
this.primaryGroup.setIncludeDetails(false); this.primaryGroup.setShowDetails(false);
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthEndpointSupport<C, T> endpoint = create(this.registry, this.groups); HealthEndpointSupport<C, T> endpoint = create(this.registry, this.groups);
HealthResult<T> rootResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false); HealthResult<T> rootResult = endpoint.getHealth(ApiVersion.V3, SecurityContext.NONE, false);
@ -124,8 +161,8 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenAlwaysIncludesDetailsIsTrueIncludesDetails() { void getHealthWhenAlwaysShowIsTrueShowsDetails() {
this.primaryGroup.setIncludeDetails(false); this.primaryGroup.setShowDetails(false);
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, true, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, true,
"test"); "test");
@ -133,7 +170,7 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenCompositeReturnsAggregateResult() { void getHealthWhenCompositeReturnsAggregateResult() {
Map<String, C> contributors = new LinkedHashMap<>(); Map<String, C> contributors = new LinkedHashMap<>();
contributors.put("a", createContributor(this.up)); contributors.put("a", createContributor(this.up));
contributors.put("b", createContributor(this.down)); contributors.put("b", createContributor(this.down));
@ -148,14 +185,14 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenPathDoesNotExistReturnsNull() { void getHealthWhenPathDoesNotExistReturnsNull() {
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "missing"); false, "missing");
assertThat(result).isNull(); assertThat(result).isNull();
} }
@Test @Test
void getHealthResultWhenPathIsEmptyIncludesGroups() { void getHealthWhenPathIsEmptyIncludesGroups() {
this.registry.registerContributor("test", createContributor(this.up)); this.registry.registerContributor("test", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false); false);
@ -163,7 +200,7 @@ abstract class HealthEndpointSupportTests<R extends ContributorRegistry<C>, C, T
} }
@Test @Test
void getHealthResultWhenPathIsGroupDoesNotIncludesGroups() { void getHealthWhenPathIsGroupDoesNotIncludesGroups() {
this.registry.registerContributor("atest", createContributor(this.up)); this.registry.registerContributor("atest", createContributor(this.up));
HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE, HealthResult<T> result = create(this.registry, this.groups).getHealth(ApiVersion.V3, SecurityContext.NONE,
false, "alltheas"); false, "alltheas");

@ -33,7 +33,9 @@ class TestHealthEndpointGroup implements HealthEndpointGroup {
private final Predicate<String> memberPredicate; private final Predicate<String> memberPredicate;
private boolean includeDetails = true; private Boolean showComponents;
private boolean showDetails = true;
TestHealthEndpointGroup() { TestHealthEndpointGroup() {
this((name) -> true); this((name) -> true);
@ -49,12 +51,21 @@ class TestHealthEndpointGroup implements HealthEndpointGroup {
} }
@Override @Override
public boolean includeDetails(SecurityContext securityContext) { public boolean showComponents(SecurityContext securityContext) {
return this.includeDetails; return (this.showComponents != null) ? this.showComponents : this.showDetails;
}
void setShowComponents(Boolean showComponents) {
this.showComponents = showComponents;
}
@Override
public boolean showDetails(SecurityContext securityContext) {
return this.showDetails;
} }
void setIncludeDetails(boolean includeDetails) { void setShowDetails(boolean includeDetails) {
this.includeDetails = includeDetails; this.showDetails = includeDetails;
} }
@Override @Override

@ -623,7 +623,7 @@ The `@Endpoint` and `@WebEndpoint` annotations should be preferred whenever poss
=== Health Information === Health Information
You can use health information to check the status of your running application. You can use health information to check the status of your running application.
It is often used by monitoring software to alert someone when a production system goes down. It is often used by monitoring software to alert someone when a production system goes down.
The information exposed by the `health` endpoint depends on the `management.endpoint.health.show-details` property which can be configured with one of the following values: The information exposed by the `health` endpoint depends on the `management.endpoint.health.show-details` and `management.endpoint.health.show-components` properties which can be configured with one of the following values:
[cols="1, 3"] [cols="1, 3"]
|=== |===

Loading…
Cancel
Save