Reputation: 1319
I have a Spring Boot server application where exceptions are caught, logged and handled when possible and when not possible they are handled by @ControllerAdvice
as the last resort.
I want to add more logic for exception handling, for example, sending a metric to a monitoring solution on each exception. For example there's a Spring bean MetricsBean
which is a service which sends metrics. Using MetricsBean
is easy for errors which are handled by @ControllerAdvice
because it's also a Spring bean.
However using MetricsBean
is not as straightforward in such code:
try {
// business logic
} catch (Exception ex) {
logger.error(ex)
}
because that would mean manually autowiring MetricsBean
into each service class. Also logger
object is not a Spring bean.
So I have a dilemma how do I send a metric from inside or around logger.error()
method when the logger class itself is not a Spring bean. I'm wondering what the best practice is in such cases. Here're some of the options:
Exception
extending class which would be a Spring bean and will send a metric inside its constructor? (seems tedious and error-prone)logger.error()
method?I'm surprised that there's not an established solution for this pattern for such an established framework as Spring (most of the solutions focus on handling errors in @ControllerAdvice
).
I'd like the solution to be as generic as possible (point 3 seems to win there) but perhaps there's a pattern which I'm missing.
Upvotes: 0
Views: 1102
Reputation: 687
Not sure if it is the best practice but I would use a UtilityClass as a facade exposing a static method. In that method, wire the metrics registry using Metrics.globalRegistry
to avoid injection. This will avoid coupling the metric details from where it is used.
You can then either invoke the static method everywhere you are handling an exception (so besides logger.error(ex)
) or combine this with your third option with the pointcut.
Simple example:
@UtilityClass
public class ExceptionUtility {
public static void handleException(Throwable throwable) {
Counter.builder("some.name.exception")
.tag("name", throwable.getClass().getName())
.register(Metrics.globalRegistry)
.increment();
}
}
Usage:
try {
...
} catch (RestClientException restClientException) {
ExceptionUtility.handleException(restClientException);
}
Upvotes: 1