forked from spring-projects/spring-boot
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for health indicator groups
Update the `HealthEndpoint` to support health groups. The `HealthEndpointSettings` interface has been replaced with `HealthEndpointGroups` which provides access to the primary group as well as an optional set of additional groups. Groups can be configured via properties and may have custom `StatusAggregator` and `HttpCodeStatusMapper` settings. Closes spring-projectsgh-14022 Co-authored-by: Stephane Nicoll <snicoll@pivotal.io>
- Loading branch information
Showing
36 changed files
with
1,598 additions
and
366 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
...gframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthContributorRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright 2012-2019 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.actuate.autoconfigure.health; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
|
||
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry; | ||
import org.springframework.boot.actuate.health.HealthContributor; | ||
import org.springframework.boot.actuate.health.HealthContributorRegistry; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* An auto-configured {@link HealthContributorRegistry} that ensures registered indicators | ||
* do not clash with groups names. | ||
* | ||
* @author Phillip Webb | ||
*/ | ||
class AutoConfiguredHealthContributorRegistry extends DefaultHealthContributorRegistry { | ||
|
||
private final Collection<String> groupNames; | ||
|
||
AutoConfiguredHealthContributorRegistry(Map<String, HealthContributor> contributors, | ||
Collection<String> groupNames) { | ||
super(contributors); | ||
this.groupNames = groupNames; | ||
contributors.keySet().forEach(this::assertDoesNotClashWithGroup); | ||
} | ||
|
||
@Override | ||
public void registerContributor(String name, HealthContributor contributor) { | ||
assertDoesNotClashWithGroup(name); | ||
super.registerContributor(name, contributor); | ||
} | ||
|
||
private void assertDoesNotClashWithGroup(String name) { | ||
Assert.state(!this.groupNames.contains(name), | ||
() -> "HealthContributor with name \"" + name + "\" clashes with group"); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
...springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Copyright 2012-2019 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.actuate.autoconfigure.health; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
import java.util.function.Supplier; | ||
|
||
import org.springframework.beans.factory.BeanFactory; | ||
import org.springframework.beans.factory.BeanFactoryUtils; | ||
import org.springframework.beans.factory.ListableBeanFactory; | ||
import org.springframework.beans.factory.NoSuchBeanDefinitionException; | ||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; | ||
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.Status; | ||
import org.springframework.boot.actuate.health.HealthEndpointGroup; | ||
import org.springframework.boot.actuate.health.HealthEndpointGroups; | ||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; | ||
import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper; | ||
import org.springframework.boot.actuate.health.SimpleStatusAggregator; | ||
import org.springframework.boot.actuate.health.StatusAggregator; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.context.ConfigurableApplicationContext; | ||
import org.springframework.util.CollectionUtils; | ||
import org.springframework.util.ObjectUtils; | ||
|
||
/** | ||
* Auto-configured {@link HealthEndpointGroups}. | ||
* | ||
* @author Phillip Webb | ||
*/ | ||
class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { | ||
|
||
private static Predicate<String> ALL = (name) -> true; | ||
|
||
private final HealthEndpointGroup primaryGroup; | ||
|
||
private final Map<String, HealthEndpointGroup> groups; | ||
|
||
/** | ||
* Create a new {@link AutoConfiguredHealthEndpointGroups} instance. | ||
* @param applicationContext the application context used to check for override beans | ||
* @param properties the health endpoint properties | ||
*/ | ||
AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { | ||
ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext) | ||
? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext; | ||
ShowDetails showDetails = properties.getShowDetails(); | ||
Set<String> roles = properties.getRoles(); | ||
StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class); | ||
if (statusAggregator == null) { | ||
statusAggregator = new SimpleStatusAggregator(properties.getStatus().getOrder()); | ||
} | ||
HttpCodeStatusMapper httpCodeStatusMapper = getNonQualifiedBean(beanFactory, HttpCodeStatusMapper.class); | ||
if (httpCodeStatusMapper == null) { | ||
httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()); | ||
} | ||
this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, | ||
showDetails, roles); | ||
this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper, | ||
showDetails, roles); | ||
} | ||
|
||
private Map<String, HealthEndpointGroup> createGroups(Map<String, Group> groupProperties, BeanFactory beanFactory, | ||
StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper, | ||
ShowDetails defaultShowDetails, Set<String> defaultRoles) { | ||
Map<String, HealthEndpointGroup> groups = new LinkedHashMap<String, HealthEndpointGroup>(); | ||
groupProperties.forEach((groupName, group) -> { | ||
Status status = group.getStatus(); | ||
ShowDetails showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails; | ||
Set<String> roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles; | ||
StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { | ||
if (!CollectionUtils.isEmpty(status.getOrder())) { | ||
return new SimpleStatusAggregator(status.getOrder()); | ||
} | ||
return defaultStatusAggregator; | ||
}); | ||
HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, | ||
groupName, () -> { | ||
if (!CollectionUtils.isEmpty(status.getHttpMapping())) { | ||
return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); | ||
} | ||
return defaultHttpCodeStatusMapper; | ||
}); | ||
Predicate<String> members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude()); | ||
groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, | ||
showDetails, roles)); | ||
}); | ||
return Collections.unmodifiableMap(groups); | ||
} | ||
|
||
private <T> T getNonQualifiedBean(ListableBeanFactory beanFactory, Class<T> type) { | ||
List<String> candidates = new ArrayList<>(); | ||
for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) { | ||
String[] aliases = beanFactory.getAliases(beanName); | ||
if (!BeanFactoryAnnotationUtils.isQualifierMatch( | ||
(qualifier) -> !qualifier.equals(beanName) && !ObjectUtils.containsElement(aliases, qualifier), | ||
beanName, beanFactory)) { | ||
candidates.add(beanName); | ||
} | ||
} | ||
if (candidates.isEmpty()) { | ||
return null; | ||
} | ||
if (candidates.size() == 1) { | ||
return beanFactory.getBean(candidates.get(0), type); | ||
} | ||
return beanFactory.getBean(type); | ||
} | ||
|
||
private <T> T getQualifiedBean(BeanFactory beanFactory, Class<T> type, String qualifier, Supplier<T> fallback) { | ||
try { | ||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, type, qualifier); | ||
} | ||
catch (NoSuchBeanDefinitionException ex) { | ||
return fallback.get(); | ||
} | ||
} | ||
|
||
@Override | ||
public HealthEndpointGroup getPrimary() { | ||
return this.primaryGroup; | ||
} | ||
|
||
@Override | ||
public Set<String> getNames() { | ||
return this.groups.keySet(); | ||
} | ||
|
||
@Override | ||
public HealthEndpointGroup get(String name) { | ||
return this.groups.get(name); | ||
} | ||
|
||
} |
55 changes: 55 additions & 0 deletions
55
...rk/boot/actuate/autoconfigure/health/AutoConfiguredReactiveHealthContributorRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright 2012-2019 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.actuate.autoconfigure.health; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
|
||
import org.springframework.boot.actuate.health.DefaultReactiveHealthContributorRegistry; | ||
import org.springframework.boot.actuate.health.HealthContributorRegistry; | ||
import org.springframework.boot.actuate.health.ReactiveHealthContributor; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* An auto-configured {@link HealthContributorRegistry} that ensures registered indicators | ||
* do not clash with groups names. | ||
* | ||
* @author Phillip Webb | ||
*/ | ||
class AutoConfiguredReactiveHealthContributorRegistry extends DefaultReactiveHealthContributorRegistry { | ||
|
||
private final Collection<String> groupNames; | ||
|
||
AutoConfiguredReactiveHealthContributorRegistry(Map<String, ReactiveHealthContributor> contributors, | ||
Collection<String> groupNames) { | ||
super(contributors); | ||
this.groupNames = groupNames; | ||
contributors.keySet().forEach(this::assertDoesNotClashWithGroup); | ||
} | ||
|
||
@Override | ||
public void registerContributor(String name, ReactiveHealthContributor contributor) { | ||
assertDoesNotClashWithGroup(name); | ||
super.registerContributor(name, contributor); | ||
} | ||
|
||
private void assertDoesNotClashWithGroup(String name) { | ||
Assert.state(!this.groupNames.contains(name), | ||
() -> "ReactiveHealthContributor with name \"" + name + "\" clashes with group"); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.