Skip to content

Commit

Permalink
Micrometer Observation Support (#585)
Browse files Browse the repository at this point in the history
This PR instruments: https://micrometer.io/docs/observation for metrics and traces.

---------

Co-authored-by: Denis Stepanov <[email protected]>
Co-authored-by: Graeme Rocher <[email protected]>
  • Loading branch information
3 people authored Jul 18, 2023
1 parent d5ec9f3 commit b437b8c
Show file tree
Hide file tree
Showing 39 changed files with 2,804 additions and 3 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
projectVersion=5.0.2-SNAPSHOT
projectVersion=5.1.0-SNAPSHOT
projectGroup=io.micronaut.micrometer

micronautSerializationVersion=1.3.2
Expand Down
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ spock = "2.3-groovy-4.0"

managed-metrics-core = '4.2.19'
managed-micrometer = '1.11.1'
managed-micrometer-tracing = '1.1.2'
managed-new-relic-registry = '0.10.0'
reflections = '0.10.2'

Expand All @@ -20,6 +21,7 @@ hdr-histogram = '2.1.12'
micronaut-cache = "4.0.1"
micronaut-r2dbc = "5.0.1"
micronaut-rxjava2 = "2.0.1"
micronaut-reactor = '3.0.0'
micronaut-serde = "2.0.1"
micronaut-sql = "5.0.1"
micronaut-validation = "4.0.0"
Expand All @@ -32,6 +34,7 @@ micronaut-validation = { module = "io.micronaut.validation:micronaut-validation-
micronaut-cache = { module = "io.micronaut.cache:micronaut-cache-bom", version.ref = "micronaut-cache" }
micronaut-r2dbc = { module = "io.micronaut.r2dbc:micronaut-r2dbc-bom", version.ref = "micronaut-r2dbc" }
micronaut-rxjava2 = { module = "io.micronaut.rxjava2:micronaut-rxjava2-bom", version.ref = "micronaut-rxjava2" }
micronaut-reactor = { module = 'io.micronaut.reactor:micronaut-reactor-bom', version.ref = 'micronaut-reactor'}
micronaut-serde = { module = "io.micronaut.serde:micronaut-serde-bom", version.ref = "micronaut-serde" }
micronaut-sql = { module = "io.micronaut.sql:micronaut-sql-bom", version.ref = "micronaut-sql" }
groovy-json = { module = "org.apache.groovy:groovy-json" }
Expand All @@ -43,6 +46,9 @@ hdr-histogram = { module = 'org.hdrhistogram:HdrHistogram', version.ref = 'hdr-h
managed-metrics-core = { module = 'io.dropwizard.metrics:metrics-core', version.ref = 'managed-metrics-core' }
managed-micrometer-registry-new-relic-telemetry = { module = 'com.newrelic.telemetry:micrometer-registry-new-relic', version.ref = 'managed-new-relic-registry' }
managed-micrometer-core = { module = 'io.micrometer:micrometer-core', version.ref = 'managed-micrometer' }
managed-micrometer-tracing = { module = 'io.micrometer:micrometer-tracing', version.ref = 'managed-micrometer-tracing' }
managed-micrometer-observation = { module = 'io.micrometer:micrometer-observation', version.ref = 'managed-micrometer' }
managed-micrometer-observation-test = { module = 'io.micrometer:micrometer-observation-test', version.ref = 'managed-micrometer' }

micrometer-registry-appoptics = { module = 'io.micrometer:micrometer-registry-appoptics', version.ref = 'managed-micrometer' }
micrometer-registry-atlas = { module = 'io.micrometer:micrometer-registry-atlas', version.ref = 'managed-micrometer' }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2017-2021 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.micrometer.annotation.processing;

import io.micrometer.observation.annotation.Observed;
import io.micronaut.aop.InterceptorBinding;
import io.micronaut.aop.InterceptorKind;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.inject.annotation.TypedAnnotationMapper;
import io.micronaut.inject.visitor.VisitorContext;

import java.util.Collections;
import java.util.List;

/**
* Adds interceptor binding to {@link Observed}.
*
* @since 3.4.0
* @author Nemanja Mikic
*/
public class ObservedAnnotationMapper implements TypedAnnotationMapper<Observed> {
@Override
public Class<Observed> annotationType() {
return Observed.class;
}

@Override
public List<AnnotationValue<?>> map(AnnotationValue<Observed> annotation, VisitorContext visitorContext) {
return Collections.singletonList(
AnnotationValue.builder(InterceptorBinding.class)
.value(Observed.class)
.member("kind", InterceptorKind.AROUND)
.build()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
io.micronaut.micrometer.annotation.processing.CountedAnnotationMapper
io.micronaut.micrometer.annotation.processing.TimedSetAnnotationMapper
io.micronaut.micrometer.annotation.processing.TimedSetAnnotationMapper
io.micronaut.micrometer.annotation.processing.ObservedAnnotationMapper
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,23 @@ class Test {
bean instanceof Intercepted
}


void 'test map observed annotation'() {
given:
def context = buildContext('test.Test', '''
package test;
@jakarta.inject.Singleton
class Test {
@io.micrometer.observation.annotation.Observed(name="foo")
void test() {
}
}
''')
def bean = getBean(context, "test.Test")

expect:
bean instanceof Intercepted
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,25 @@ class Test {
cleanup:
context.close()
}

void 'test map observed annotation'() {
given:
def context = buildContext('test.Test', '''
package test;
@io.micrometer.observation.annotation.Observed(name="foo")
@jakarta.inject.Singleton
class Test {
}
''')
def bean = context.getBean(context.classLoader.loadClass( "test.Test"))

expect:
bean instanceof Intercepted

cleanup:
context.close()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
@Filter("${micronaut.metrics.http.client.path:/**}")
@RequiresMetrics
@Requires(property = WebMetricsPublisher.ENABLED, notEquals = FALSE)
@Requires(condition = WebMetricsClientCondition.class)
public class ClientRequestMetricRegistryFilter implements HttpClientFilter {

private static final String HOST_HEADER = "host";

private final MeterRegistry meterRegistry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
@Filter("${micronaut.metrics.http.path:/**}")
@RequiresMetrics
@Requires(property = WebMetricsPublisher.ENABLED, notEquals = FALSE)
@Requires(condition = WebMetricsServerCondition.class)
public class ServerRequestMeterRegistryFilter implements HttpServerFilter {

private static final String ATTRIBUTE_KEY = "micronaut.filter." + ServerRequestMeterRegistryFilter.class.getSimpleName();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2017-2023 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.binder.web;

import io.micronaut.context.condition.Condition;
import io.micronaut.context.condition.ConditionContext;
import io.micronaut.core.reflect.ClassUtils;

/**
* A custom {@link Condition} that defines if {@link ClientRequestMetricRegistryFilter} should be created.
*/
public class WebMetricsClientCondition implements Condition {
@Override
public boolean matches(ConditionContext context) {
boolean isClassPresent = ClassUtils.isPresent("io.micronaut.micrometer.observation.http.client.ObservationClientFilter", context.getBeanContext().getClassLoader());

if (!context.containsProperty("micrometer.observation.http.client.enabled") && isClassPresent) {
return false;
}

return !context.containsProperty("micrometer.observation.client.server.enabled") || !context.getProperty("micrometer.observation.http.client.enabled", Boolean.class).orElse(false);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2017-2023 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.binder.web;

import io.micronaut.context.condition.Condition;
import io.micronaut.context.condition.ConditionContext;
import io.micronaut.core.reflect.ClassUtils;

/**
* A custom {@link Condition} that defines if {@link ServerRequestMeterRegistryFilter} should be created.
*/
public class WebMetricsServerCondition implements Condition {
@Override
public boolean matches(ConditionContext context) {
boolean isClassPresent = ClassUtils.isPresent("io.micronaut.micrometer.observation.http.server.ObservationServerFilter", context.getBeanContext().getClassLoader());

if (!context.containsProperty("micrometer.observation.http.server.enabled") && isClassPresent) {
return false;
}

return !context.containsProperty("micrometer.observation.server.server.enabled") || !context.getProperty("micrometer.observation.http.server.enabled", Boolean.class).orElse(false);
}
}
37 changes: 37 additions & 0 deletions micrometer-observation-http/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
plugins {
id 'io.micronaut.build.internal.micrometer-module'
}

dependencies {
annotationProcessor mn.micronaut.graal

api projects.micronautMicrometerAnnotation
api projects.micronautMicrometerObservation

implementation libs.managed.micrometer.observation
compileOnly mn.micronaut.http.server.netty
implementation mn.reactor
compileOnly(libs.managed.micrometer.tracing)

testAnnotationProcessor mn.micronaut.inject.java
testAnnotationProcessor projects.micronautMicrometerAnnotation
testImplementation mn.micronaut.http.client
testImplementation mn.micronaut.http.server.netty
testImplementation mnReactor.micronaut.reactor
testImplementation mnReactor.micronaut.reactor.http.client
testImplementation mnRxjava2.micronaut.rxjava2
testImplementation mnRxjava2.micronaut.rxjava2.http.client
testImplementation mnSerde.micronaut.serde.jackson
testImplementation libs.managed.micrometer.observation.test
testImplementation projects.micronautMicrometerCore
testImplementation(libs.managed.micrometer.tracing)
}

micronautBuild {
binaryCompatibility {
def dash = version.indexOf('-')
def v = dash > 0 ? version.substring(0, dash) : version
def (major, minor, patch) = v.split('[.]').collect { it.toInteger() }
enabled = major > 5 || (major == 5 && minor > 1) || (major == 5 && minor == 1 && patch > 0)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2017-2023 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.micrometer.observation.http;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Nullable;

import java.util.function.Predicate;

/**
* Abstract filter used for Micrometer Observation.
*/
@Internal
public abstract class AbstractObservationFilter {

public static final String CLIENT_PATH = "${micrometer.observation.http.client.path:/**}";
public static final String SERVER_PATH = "${micrometer.observation.http.server.path:/**}";
public static final String MICROMETER_OBSERVATION_ATTRIBUTE_KEY = "micrometer.observation";

@Nullable
private final Predicate<String> pathExclusionTest;

/**
* Configure observation in the filter for span creation and propagation across
* arbitrary transports.
*
* @param pathExclusionTest for excluding URI paths from observation
*/
protected AbstractObservationFilter(@Nullable Predicate<String> pathExclusionTest) {
this.pathExclusionTest = pathExclusionTest;
}

/**
* Tests if the defined path should be excluded from observation.
*
* @param path the path
* @return {@code true} if the path should be excluded
*/
protected boolean shouldExclude(@Nullable String path) {
return pathExclusionTest != null && path != null && pathExclusionTest.test(path);
}
}
Loading

0 comments on commit b437b8c

Please sign in to comment.