In Quarkus, the @Counted
annotation is implemented as a CDI (Contexts and Dependency Injection) interceptor. It requires the annotated object to be a managed bean (such that it can be wrapped in a proxied object to allow method invocations to be intercepted).
An exception isn't usually a bean (and you don't usually want to create/manage exceptions using a CDI bean lifecycle). If I attempt your example, CDI wants more information so that it can construct the intercepted bean (which is clearly not your intent):
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type java.lang.String and qualifiers [@Default]
- java member: ....CountedException#<init>()
- declared on CLASS bean [types=[java.io.Serializable, java.lang.Exception, java.lang.Throwable, ....CountedException, java.lang.RuntimeException, java.lang.Object], qualifiers=[@Default, @Any], target=....CountedException]
What I believe you want to do is simply count how many times this kind of exception is created. For this, you should skip trying to use annotations, and just use a counter directly, e.g. using the Micrometer extension:
public class CountedException extends RuntimeException {
public CountedException(final String errorMsg, final Throwable exception) {
super(errorMsg, exception);
Metrics.counter("errorCounterMetric").increment();
}
}
Using MP Metrics is similar:
public class CountedException extends RuntimeException {
public CountedException(final String errorMsg, final Throwable exception) {
super(errorMsg, exception);
MetricRegistries.get(Type.APPLICATION).counter("errorCounterMetric").inc();
}
}
Same outcome, but you avoid lifecycle confusion around beans.
As an interesting aside, this would allow you to record more information about why these exceptions are being created.
For example (using micrometer naming conventions, too):
public class CountedException extends RuntimeException {
public CountedException(final String errorMsg, final Throwable exception) {
super(errorMsg, exception);
String cause = exception == null ? "null" : exception.getClass().getSimpleName();
Metrics.counter("error.counter.metric", "cause", cause).increment();
}
}
or MP Metrics:
public class CountedException extends RuntimeException {
public CountedException(final String errorMsg, final Throwable exception) {
super(errorMsg, exception);
String cause = exception == null ? "null" : exception.getClass().getSimpleName();
MetricRegistries.get(Type.APPLICATION).counter("errorCounterMetric", new Tag("cause", cause)).inc();
}
}
This should still have a nicely bounded cardinality, but (making something up) you would get a view of how often this exception was created to wrap a NullPointerException, or an IOException, or a null, or...