I want to add Actuator to my Spring application (not a Spring Boot). To be precise, I need to use actuators HealthCheck, so I'll be able to create beans of different health indicators and they would create complete healthcheck on /health.
At first I tried to add @EndpointAutoConfiguration to my @Configuration class, but there is beans conflicts, because I have my customs beans, that I really need, so there's no need in other Configurations other then HealthIndicatorAutoConfiguration
Possible solution can be creating custom HealthIndicator and HealthIndicatorCollector with critical fields. Also adding a servlet:
contextHandler.addServlet(new ServletHolder(platformHealthCheckServlet), "/healthcheck");
That collects all the custom HealthIndicators from context and creating nice status json at /healthcheck endpoint:
@Autowired
public CustomHealthCheckServlet(Map<String, CustomHealthIndicator> healthIndicatorMap, ObjectMapper objectMapper) {
this.healthIndicatorMap = Collections.unmodifiableMap(healthIndicatorMap);
this.mapper = objectMapper;
}
CustomHealthIndicator, which adds setCritical method:
public interface CustomHealthIndicator extends HealthIndicator {
void setCritical(boolean var1);
}
CustomAbstractHealthIndicator which overrides doHealthCheck method in AbstractHealthIndicator adding critical detail to its builder and creating its own abstract method doCheck:
public abstract class CustomAbstractHealthIndicator extends AbstractHealthIndicator
implements CustomHealthIndicator {
private boolean critical = false;
public CustomAbstractHealthIndicator() {
}
public void setCritical(boolean critical) {
this.critical = critical;
}
@Override
protected void doHealthCheck(Builder builder) throws Exception {
builder.withDetail("critical", this.critical);
this.doCheck(builder);
}
protected abstract Builder doCheck(Builder var1) throws Exception;
}
And there's another Custom class that aggregates health beans with calculating status for the whole application based on a critical status of every health, ignoring indicators that are not critical but might be useful for monitoring:
public class CustomHealthAggregator implements HealthAggregator {
private List<String> statusOrder;
public CustomHealthAggregator() {
setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP,
Status.UNKNOWN);
}
public void setStatusOrder(Status... statusOrder) {
String[] order = new String[statusOrder.length];
for (int i = 0; i < statusOrder.length; i++) {
order[i] = statusOrder[i].getCode();
}
setStatusOrder(Arrays.asList(order));
}
public void setStatusOrder(List<String> statusOrder) {
Assert.notNull(statusOrder, "StatusOrder must not be null");
this.statusOrder = statusOrder;
}
protected Status aggregateStatus(List<Status> candidates) {
// Only sort those status instances that we know about
List<Status> filteredCandidates = new ArrayList<Status>();
for (Status candidate : candidates) {
if (this.statusOrder.contains(candidate.getCode())) {
filteredCandidates.add(candidate);
}
}
// If no status is given return UP
if (filteredCandidates.isEmpty()) {
return Status.UP;
}
// Sort given Status instances by configured order
Collections.sort(filteredCandidates,
new StatusComparator(this.statusOrder));
return filteredCandidates.get(0);
}
@Override
public Health aggregate(Map<String, Health> healths) {
List<Status> statusCandidates = healths.entrySet()
.stream()
.filter(healthEntry ->
healthEntry.getValue().getDetails().get("critical").equals(true))
.map(e -> e.getValue().getStatus())
.collect(Collectors.toList());
Status status = aggregateStatus(statusCandidates);
Map<String, Object> details = new LinkedMap();
details.put("version", AppVersionUtil.getAppVersion());
details.putAll(aggregateDetails(healths));
return new Health.Builder(status, details).build();
}
protected Map<String, Object> aggregateDetails(Map<String, Health> healths)
{
return new LinkedHashMap<String, Object>(healths);
}
private class StatusComparator implements Comparator<Status> {
private final List<String> statusOrder;
StatusComparator(List<String> statusOrder) {
this.statusOrder = statusOrder;
}
@Override
public int compare(Status s1, Status s2) {
int i1 = this.statusOrder.indexOf(s1.getCode());
int i2 = this.statusOrder.indexOf(s2.getCode());
return (i1 < i2 ? -1 : (i1 == i2 ? s1.getCode().compareTo(s2.getCode()) : 1));
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With