Skip to content

Commit

Permalink
@MetricOptions to allow filtering AbstractMethodTagger beans to a…
Browse files Browse the repository at this point in the history
…pply to metrics (#764)

Follow up to #753

New annotation MetricOptions to hold metadata for additional information that might be used in building metrics
  • Loading branch information
hrothwell authored May 28, 2024
1 parent 67a4086 commit b8df881
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2017-2019 original 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 io.micronaut.configuration.metrics.annotation;

import io.micronaut.configuration.metrics.aggregator.AbstractMethodTagger;
import io.micronaut.core.annotation.Experimental;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Holds metadata about metric options to apply
*
* @author Haiden Rothwell
* @since 5.6.0
*/
@Documented
@Experimental
@Retention(RUNTIME)
@Target({METHOD})
public @interface MetricOptions {
/**
* @return array of {@link io.micronaut.configuration.metrics.aggregator.AbstractMethodTagger} to apply to metrics for method.
* Only utilized for filtering if {@link #filterTaggers()} is true
*/
Class<? extends AbstractMethodTagger>[] taggers() default {};

/**
* @return whether to filter taggers using {@link #taggers()} array
*/
boolean filterTaggers() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.micronaut.aop.MethodInterceptor;
import io.micronaut.aop.MethodInvocationContext;
import io.micronaut.configuration.metrics.aggregator.AbstractMethodTagger;
import io.micronaut.configuration.metrics.annotation.MetricOptions;
import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Nullable;
Expand All @@ -34,6 +35,7 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -148,13 +150,16 @@ public Object intercept(MethodInvocationContext<Object, Object> context) {
}

private void doCount(AnnotationMetadata metadata, String metricName, @Nullable Throwable e, MethodInvocationContext<Object, Object> context) {
List<Class<? extends AbstractMethodTagger>> taggers = Arrays.asList(metadata.classValues(MetricOptions.class, "taggers"));
boolean filter = metadata.booleanValue(MetricOptions.class, "filterTaggers").orElse(false);
Counter.builder(metricName)
.tags(metadata.stringValues(Counted.class, "extraTags"))
.tags(
methodTaggers.isEmpty() ? Collections.emptyList() :
methodTaggers
.stream()
.flatMap(b -> b.getTags(context).stream())
.filter(t -> !filter || taggers.contains(t.getClass()))
.flatMap(t -> t.getTags(context).stream())
.toList()
)
.description(metadata.stringValue(Counted.class, "description").orElse(null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.micronaut.aop.MethodInterceptor;
import io.micronaut.aop.MethodInvocationContext;
import io.micronaut.configuration.metrics.aggregator.AbstractMethodTagger;
import io.micronaut.configuration.metrics.annotation.MetricOptions;
import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationValue;
Expand All @@ -43,6 +44,7 @@
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -212,6 +214,9 @@ private void stopTimed(String metricName, Timer.Sample sample,
try {
final String description = metadata.stringValue("description").orElse(null);
final String[] tags = metadata.stringValues("extraTags");
final AnnotationMetadata annotationMetadata = context.getAnnotationMetadata();
final List<Class<? extends AbstractMethodTagger>> taggers = Arrays.asList(annotationMetadata.classValues(MetricOptions.class, "taggers"));
final boolean filter = annotationMetadata.booleanValue(MetricOptions.class, "filterTaggers").orElse(false);
final double[] percentiles = metadata.doubleValues("percentiles");
final boolean histogram = metadata.isTrue("histogram");
final Timer timer = Timer.builder(metricName)
Expand All @@ -221,7 +226,8 @@ private void stopTimed(String metricName, Timer.Sample sample,
methodTaggers.isEmpty() ? Collections.emptyList() :
methodTaggers
.stream()
.flatMap(b -> b.getTags(context).stream())
.filter(t -> !filter || taggers.contains(t.getClass()))
.flatMap(b -> b.getTags(context).stream())
.toList()
)
.tags(EXCEPTION_TAG, exceptionClass)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package io.micronaut.configuration.metrics.annotation

import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.search.MeterNotFoundException
import io.micronaut.context.ApplicationContext
import spock.lang.Specification
import spock.util.concurrent.PollingConditions

import java.util.function.Consumer

import static java.util.concurrent.TimeUnit.MILLISECONDS

class CountedAnnotationSpec extends Specification {

void "test counted annotation usage"() {
Expand Down Expand Up @@ -86,4 +89,28 @@ class CountedAnnotationSpec extends Specification {
cleanup:
ctx.close()
}

void "taggers are filtered if filter present"(){
given:
ApplicationContext ctx = ApplicationContext.run()
CountedTarget tt = ctx.getBean(CountedTarget)
MeterRegistry registry = ctx.getBean(MeterRegistry)

when:
Integer result = tt.maxWithOptions(4, 10)
registry.get("counted.test.maxWithOptions.blocking").tags("method", "maxWithOptions", "parameters", "a b").counter()

then:
thrown(MeterNotFoundException)

when:
def timer = registry.get("counted.test.maxWithOptions.blocking").tags("method", "maxWithOptions").counter()

then:
result == 10
timer.count() == 1

cleanup:
ctx.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.micronaut.configuration.metrics.annotation

import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.search.MeterNotFoundException
import io.micronaut.context.ApplicationContext
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
Expand Down Expand Up @@ -99,4 +100,29 @@ class TimeAnnotationSpec extends Specification {
cleanup:
ctx.close()
}

void "taggers are filtered if filter present"(){
given:
ApplicationContext ctx = ApplicationContext.run()
TimedTarget tt = ctx.getBean(TimedTarget)
MeterRegistry registry = ctx.getBean(MeterRegistry)

when:
Integer result = tt.maxWithOptions(4, 10)
registry.get("timed.test.maxWithOptions.blocking").tags("method", "maxWithOptions", "parameters", "a b").timer()

then:
thrown(MeterNotFoundException)

when:
def timer = registry.get("timed.test.maxWithOptions.blocking").tags("method", "maxWithOptions").timer()

then:
result == 10
timer.count() == 1
timer.totalTime(MILLISECONDS) > 0

cleanup:
ctx.close()
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.configuration.metrics.annotation;

import io.micrometer.core.annotation.Counted;
import io.micronaut.configuration.metrics.aggregator.MethodTaggerExample;
import jakarta.inject.Singleton;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -15,6 +16,12 @@ Integer max(int a, int b) {
return Math.max(a, b);
}

@Counted("counted.test.maxWithOptions.blocking")
@MetricOptions(taggers = {MethodTaggerExample.class}, filterTaggers = true)
Integer maxWithOptions(int a, int b) {
return Math.max(a, b);
}

@Counted("counted.test.max.blocking")
Integer error(int a, int b) {
throw new NumberFormatException("cannot");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.micronaut.configuration.metrics.annotation;

import io.micrometer.core.annotation.Timed;
import io.micronaut.configuration.metrics.aggregator.MethodTaggerExample;
import jakarta.inject.Singleton;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -15,6 +16,12 @@ Integer max(int a, int b) {
return Math.max(a, b);
}

@Timed("timed.test.maxWithOptions.blocking")
@MetricOptions(taggers = {MethodTaggerExample.class}, filterTaggers = true)
Integer maxWithOptions(int a, int b) {
return Math.max(a, b);
}

@Timed("timed.test.repeated1")
@Timed("timed.test.repeated2")
Integer repeated(int a, int b) {
Expand Down

0 comments on commit b8df881

Please sign in to comment.