Custom HealthIndicator not invoked during startup - spring-boot

I implemented a custom HealthIndicator for our application, which is working fine.
I noticed that when I run the application through my IDE (tested with both IntelliJ and Eclipse), the HealthIndicator.health() method is invoked during startup.
However, when I run the application by using the JAR file itself, the HealthIndicator.health() method is not invoked during startup of the application.
Why is the HealthIndicator.health() method not invoked during startup when I run it as a JAR file, and shouldn't it behave similarly as when running it through the IDE?

This is actually not really a bug, but a side effect caused by your IDE. You should be aware that Actuator endpoints are not only exposed over HTTP, but also over JMX. If you take a look at the documentation, you'll also see that the health endpoint is enabled by default on both HTTP and JMX.
Additionally, most IDEs, including IntelliJ and Eclipse, will enable a JMX agent when running the application through the IDE itself. This means that when the application is started, a JMX connection is made, which will in turn trigger the custom health indicator.
You can verify this quite easily, for example let's assume the following health indicator:
#Bean
public HealthIndicator alwaysUpHealthIndicator() {
return () -> {
log.info("Indicator invoked");
return Health.up().withDetail("Foo", "Bar").build();
};
}
If you change your IntelliJ run configuration and disable Enable JMX agent, you'll notice that the message no longer appears in the log.
Likewise, if you disable the health JMX endpoint, you'll also notice that you won't get the additional message within the logs:
management.endpoints.jmx.exposure.exclude=health
This means that you shouldn't rely on the HealthIndicator being executed during the startup of your application. If you have code that should be executed when your application is starting up, consider using an ApplicationRunner or CommandLineRunner bean. For example:
#Bean
public ApplicationRunner applicationRunner() {
return args -> log.info("This will be invoked on startup");
}

I can't answer directly the question, but it looks like there is no real question here, if its a bug - submit a bug to spring boot team. Otherwise its just a statement that I fully agree with.
Either HelathIndicator health() method should be executed in both ways or none at all.
What you're describing sounds more like a weird bug, here is a dirty way to check what happens (remove it or course in production):
Inside the health method of the health indicator obtain a stack trace and print it on console.
Analyze the stack trace and check that its not a result of some /health invocation through the HTTP (for example, maybe your IDE is configured to automatically call actuator's health upon start, who knows)

Related

How Do I Access The Spring Boot Startup Actuator During A Test

I would like to record the startup information about my application during a Spring Boot test. I have the startup actuator configured and working in Spring Boot 'bootrun' mode. However, when I try to access that actuator during a test using a TestRestTemplate, I get a 404 error.
I have written an example program that demonstrates the problem. The issue isn't with acutators overall as I have the metrics and health actuators working in the same test. Just the startup actuator.
The example code is on GitHub
I have a solution for this so I thought I would post it. For complete details, see the original repo in GitHub and check the solution branch.
One possible way to enable ApplicationStartup data collection during a Spring Boot Test is to create a ContextCustomizer. This allows you to get into the testing context early enough to record all of the data that you are looking for. The ContextCustomizer should have a single static BufferingApplicationStartup that it registers as a singleton bean into the test context's bean factory. It also needs to set the bean factory's ApplicationStartup because that will be passed to the SpringApplication just before it is run.
Here is the snippet of the customizer that holds the key:
#Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Object possibleSingleton = beanFactory.getSingleton(BEAN_NAME);
// The only way it wouldn't be an instance of a BufferingApplicationStartup is if it is null or we haven't
// run yet (and it is the DefaultApplicationStartup). In either case, jam our BufferingApplicationStartup
// in here.
if(!(possibleSingleton instanceof BufferingApplicationStartup)) {
beanFactory.registerSingleton(BEAN_NAME, APPLICATION_STARTUP);
beanFactory.setApplicationStartup(APPLICATION_STARTUP);
}
}
When you do this, make sure you implement a good equals and hashCode for your customizer or else you will break the test context caching and you will refresh your test context with every test class. Since the only relevant part of the customizer is the static BufferingApplicationStartup, I chose to return its hashcode.
Finally, don't forget to add your ContextCustomizerFactory to the src/test/resources/META-INF/spring.factories or else the rest of the Spring Boot testing support won't see your customizer.
Once this is all setup, you can access the Startup Actuator endpoint just like you would any other actuator.

Proper way to get Spring Boot health status from inside

I have an external requirement that I provide an endpoint to tell the load balancer to send traffic to my app. Much like the Kubernetes "readiness" probe, but it has to be a certain format and path, so I can just give them the actuator health endpoint.
In the past I've used the HealthEndpoint and called health(), but that doesn't work for reactive apps. Is there a more flexible way to see if the app is "UP"? At this level I don't care if it's reactive or servlet, I just want to know what Spring Boot says about the app.
I haven't found anything like this, most articles talk about calling /actuator/health, but that isn't what I need.
Edit:
Just a bit more detail, I have to return a certain string "NS_ENABLE" if it's good. There are certain conditions where I return "NS_DISABLE", so I can't just not return anything, which would normally make sense.
Also, I really like how Spring Boot does the checking for me. I'd rather not re-implement all those checks.
Edit 2: My final solution
The answers below got me very far along even though it wasn't my final solution, so I wanted to give a hint to my final understanding.
It turns out that the HealthEndpoint works for reactive apps just as well as servlet apps, you just have to wrap them in Mono.
How do we define health of any web servers?
We look at how our dependent services are, we check the status of Redis, MySQL, MongoDB, ElasticSearch, and other databases, this's what actuator does internally.
Actuator checks the status of different databases and based on that it returns Up/Down.
You can implement your own methods that would check the health of dependent services.
Redis is healthy or not can be checked using ping command
MySQL can be verified using SELECT 1 command or run some query that should always success like SHOW TABLES
Similarly, you can implement a health check for other services. If you find all required services are up then you can declare up otherwise down.
What about shutdown triggers? Whenever your server receives a shutdown signal than no matter what's the state of your dependent services, you should always say down, so that upstream won't send a call to this instance.
Edit
The health of the entire spring app can be checked programmatically by autowiring one or more beans from the Actuator module.
#Controller
public class MyHealthController{
#Autowired private HealthEndpoint healthEndpoint;
#GetMapping("health")
public Health health() {
Health health = healthEndpoint.health();
return healthEndpoint.health();
}
}
There're other beans related to health check, we can auto wire required beans. Some of the beans provide the health of the respective component, we can combine the health of each component using HealthAggregator to get the final Health. All registered health indicator components can be accessed via HealthIndicatorRegistry.
#Controller
public class MyHealthController{
#Autowired private HealthAggregator healthAggregator;
#Autowired private HealthIndicatorRegistry healthIndicatorRegistry;
#GetMapping("health")
public Health health() {
Map<String, Health> health = new HashMap<>();
for (Entry<String, HealthIndicator> entry : healthIndicatorRegistry.getAll().entrySet()) {
health.put(entry.getKey(), entry.getValue().health());
}
return healthAggregator.aggregate(health);
}
}
NOTE: Reactive component has its own health indicator. Useful classes are ReactiveHealthIndicatorRegistry, ReactiveHealthIndicator etc
Simple solution is to write your own health endpoint instead of depending on Spring.
Spring Boot provides you production-ready endpoints but if it doesn't satisfy your purpose, write your end-point. It will just return "UP" in response. If the service is down, it will not return anything.
Here's the spring boot documentation on writing reactive health endpoints. Folow the guide and should be enough for your usecase.
They also document on how to write liveliness and Readiness of your application.
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#reactive-health-indicators

How to disable Rabbit health check via Configuration

I would like to disable the rabbit health check in my default RabbitMockConfiguration.
We have a Configuration that is imported via #Import. Unfortunately the Configuration does not prevent the health check from being added to the health indicator as that happens once spring-rabbit is in the classpath.
We have the workaround, that we add a properties file in every service using that Configuration, which disables the property management.health.rabbit.enabled, but for us it would be much nicer to be able to disable that heathcheck on configuration level.
I thought about the tests with #TestPropertySource(properties = ["management.health.rabbit.enabled=false"]), but I could not find an equivalent to use for the a #Configuration, as #PropertySource expects a location for a properties file and does not accept single properties.
Any idea what we can do?
Spring boot version: 2.2.4
Spring amqp version: 2.2.3
Spring Version: 5.2.3
If you want to change the behaviour of the health check, I'd rather override the health check so that it states Rabbit is in mock mode.
To do so, just create a HealthIndicator bean named rabbitHealthIndicator:
#Bean
public HealthIndicator rabbitHealthIndicator() {
return () -> Health.up().withDetail("version", "mock").build();
}
This has the effect of switching the production one and exposes the fact the app is running with a mock.
I guess you should add 'ApplicationListener' and add the implementation to 'src/main/resources/META-INF/spring.factories' to your module with MockReddisConfiguration. This is described in more detail here

In Spring Boot how do you register custom converters that are available when parsing application configuration?

In a Spring Boot application how do you register custom converts to be used when processing application configuration?
I have made a custom convert (org.springframework.core.convert.converter.Converter) so it can be used by the ApplicationConversionService/Binder to parse #ConfiguraitonProperties defined in application.properties and application.yaml configuration files but do not know how to register it.
I have tried the solution here https://stackoverflow.com/a/41205653/45708 but it creates an instance of my converter after the application configuration parameters have been processed.
I ran into this issue myself recently. From what I can tell, the key issue is that binding to configuration properties occurs very early in the Spring startup process, before the Application Context is fully initialized. Therefore the usual methods for registering a converter are not reliable. In fact the ConversionService used for configuration binding appear to be a one-off and not really connected to the ConversionService that is stored in the Application Context.
I was able to get something working but it feels like a hack, as it relies on internal implementation details that may work today but not tomorrow. In any case, this is the code I used:
((ApplicationConversionService) ApplicationConversionService.getSharedInstance()).addConverter(myCustomConverter);
The trick I found was to make sure this gets called as soon as possible at application startup so that it gets called before the configuration binding where it's needed. I put it in a #PostConstruct block inside my main #SpringBootApplication class as this seemed to get invoked early on, at least in my case.

#EventHandler retry logic and DistributedCommandBus setup

1st Question:
So i am using Spring Eureka and the DistributedCommandBus set via the following:
public CommandRouter springCloudCommandRouter(DiscoveryClient discoveryClient, Registration localServiceInstance) { ... }
public CommandBusConnector springHttpCommandBusConnector(#Qualifier("localSegment") CommandBus localSegment, RestOperations restOperations, Serializer serializer) { .. }
public DistributedCommandBus springCloudDistributedCommandBus(CommandRouter commandRouter, CommandBusConnector commandBusConnector) { ... }
and my question for this part is how can i show that this is working? I have two K8 pods running the above code and have seen one run the #CommandHandler and the other run the #EventSourcingEvent but did not see anything in the logs to give any indication that it is using the bus.
Just want to be able to show that it is "working" as i have been asked to do so.
the Eureka part is working and i see all the info from said dashboard.
Edit - removed 2nd question to ask in another thread
To keep focus with my answer, I'll only provide a suggestion for your first question, which summarizes to:
How can I point out my DistributedCommandBus set up with Eureka is actually routing the commands to different instances?
I would suggest to set up some logging around this.
That way, you could log when the message is dispatched from Node 1 and when it is handled by Node 2.
Ideal for this would be to register the LoggingInterceptor as a MessageHandlerInterceptor and MessageDispatchInterceptor.
To do so, you will have to register it on the DistributedCommandBus, but also on the "local segment" CommandBus. The DistributedCommandBus will be in charge of dispatching it and thus calling the LoggingInterceptor upon dispatching. The local segment/CommandBus is in charge of providing the command to a Command Handler in the right JVM and as such will call the LoggingInterceptor upon handling.
The sole downside to this, is that the LoggingInterceptor will be a handler and dispatch interceptor as off Axon Framework release 4.2.
Thus, for now, you will have to do with it only being a Handler Interceptor.
However, this would suffice as well, as the LoggingInterceptor will only log upon handling the command.
This would then only occur on the Node which actually handles the command.
Hope this helps!

Resources