Skip to content

Commit 6189f81

Browse files
committed
Add auto-configuration for Micrometer 2.0.0 Observation API
- Adds a ObservationRegistry bean - Add support for ObservationRegistryCustomizers - Enables timer creation for observations if micrometer-core is on the classpath - Registers ObservationPredicate, GlobalTagsProvider and ObservationHandler on the MeterRegistry - Applies grouping to the ObservationHandlers: MeterObservationHandler are added to a FirstMatchingCompositeObservationHandler - If micrometer-tracing is on the classpath, the TracingObservationHandler are added to a FirstMatchingCompositeObservationHandler See gh-29666
1 parent e325115 commit 6189f81

File tree

17 files changed

+1092
-3
lines changed

17 files changed

+1092
-3
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ dependencies {
4747
optional("com.zaxxer:HikariCP")
4848
optional("io.dropwizard.metrics:metrics-jmx")
4949
optional("io.lettuce:lettuce-core")
50+
optional("io.micrometer:micrometer-observation")
5051
optional("io.micrometer:micrometer-core")
52+
optional("io.micrometer:micrometer-tracing-api")
5153
optional("io.micrometer:micrometer-binders")
5254
optional("io.micrometer:micrometer-registry-appoptics")
5355
optional("io.micrometer:micrometer-registry-atlas") {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import java.util.List;
20+
21+
import io.micrometer.observation.ObservationHandler;
22+
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
23+
import io.micrometer.observation.ObservationRegistry;
24+
25+
/**
26+
* Installs {@link ObservationHandler} into an {@link ObservationRegistry}.
27+
*
28+
* Uses an {@link ObservationHandlerGrouping} strategy to group the handlers. All handlers
29+
* which belong to a group are added with their group members into a
30+
* {@link FirstMatchingCompositeObservationHandler}. All handlers without a group are
31+
* added directly to the {@link ObservationRegistry}. The group numbers are ordered
32+
* ascending, meaning lower numbers are added first to the {@link ObservationRegistry}.
33+
*
34+
* @author Moritz Halbritter
35+
*/
36+
class HandlersObservationRegistryCustomizer implements ObservationRegistryCustomizer<ObservationRegistry> {
37+
38+
private final List<ObservationHandler<?>> observationHandlers;
39+
40+
private final ObservationHandlerGrouping observationHandlerGrouping;
41+
42+
HandlersObservationRegistryCustomizer(List<ObservationHandler<?>> observationHandlers,
43+
ObservationHandlerGrouping observationHandlerGrouping) {
44+
this.observationHandlers = observationHandlers;
45+
this.observationHandlerGrouping = observationHandlerGrouping;
46+
}
47+
48+
@Override
49+
public void customize(ObservationRegistry registry) {
50+
this.observationHandlerGrouping.apply(this.observationHandlers, registry.observationConfig());
51+
}
52+
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.List;
22+
23+
import io.micrometer.core.instrument.observation.MeterObservationHandler;
24+
import io.micrometer.observation.ObservationHandler;
25+
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
26+
import io.micrometer.observation.ObservationRegistry.ObservationConfig;
27+
28+
/**
29+
* {@link ObservationHandlerGrouping} used by {@link ObservationAutoConfiguration} if
30+
* micrometer-tracing is not on the classpath.
31+
*
32+
* Groups all {@link MeterObservationHandler} into a
33+
* {@link FirstMatchingCompositeObservationHandler}. All other handlers are added to the
34+
* {@link ObservationConfig} directly.
35+
*
36+
* @author Moritz Halbritter
37+
*/
38+
class NoTracingObservationHandlerGrouping implements ObservationHandlerGrouping {
39+
40+
@Override
41+
public void apply(Collection<ObservationHandler<?>> handlers, ObservationConfig config) {
42+
List<ObservationHandler<?>> meterObservationHandlers = new ArrayList<>();
43+
for (ObservationHandler<?> handler : handlers) {
44+
if (handler instanceof MeterObservationHandler<?>) {
45+
meterObservationHandlers.add(handler);
46+
}
47+
else {
48+
config.observationHandler(handler);
49+
}
50+
}
51+
52+
if (!meterObservationHandlers.isEmpty()) {
53+
config.observationHandler(
54+
new FirstMatchingCompositeObservationHandler(castToRawType(meterObservationHandlers)));
55+
}
56+
}
57+
58+
@SuppressWarnings({ "unchecked", "rawtypes" })
59+
private List<ObservationHandler> castToRawType(List<ObservationHandler<?>> handlers) {
60+
// See https://github.com/micrometer-metrics/micrometer/issues/3064
61+
return (List) handlers;
62+
}
63+
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import java.util.List;
20+
21+
import io.micrometer.core.instrument.MeterRegistry;
22+
import io.micrometer.observation.Observation.GlobalTagsProvider;
23+
import io.micrometer.observation.ObservationHandler;
24+
import io.micrometer.observation.ObservationPredicate;
25+
import io.micrometer.observation.ObservationRegistry;
26+
import io.micrometer.tracing.handler.TracingObservationHandler;
27+
28+
import org.springframework.beans.factory.ObjectProvider;
29+
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.AutoConfiguration;
31+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
32+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
33+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
34+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
35+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
36+
import org.springframework.context.annotation.Bean;
37+
import org.springframework.context.annotation.Configuration;
38+
39+
/**
40+
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
41+
*
42+
* @author Moritz Halbritter
43+
* @since 3.0.0
44+
*/
45+
@AutoConfiguration(after = CompositeMeterRegistryAutoConfiguration.class)
46+
@ConditionalOnClass(ObservationRegistry.class)
47+
public class ObservationAutoConfiguration {
48+
49+
@Bean
50+
static ObservationRegistryPostProcessor observationRegistryPostProcessor(
51+
ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers,
52+
ObjectProvider<ObservationPredicate> observationPredicates,
53+
ObjectProvider<GlobalTagsProvider<?>> tagProviders) {
54+
return new ObservationRegistryPostProcessor(observationRegistryCustomizers, observationPredicates,
55+
tagProviders);
56+
}
57+
58+
@Bean
59+
@ConditionalOnMissingBean
60+
ObservationRegistry observationRegistry() {
61+
return ObservationRegistry.create();
62+
}
63+
64+
@Bean
65+
@ConditionalOnMissingBean
66+
HandlersObservationRegistryCustomizer handlersObservationRegistryCustomizer(
67+
List<ObservationHandler<?>> observationHandlers, ObservationHandlerGrouping observationHandlerGrouping) {
68+
return new HandlersObservationRegistryCustomizer(observationHandlers, observationHandlerGrouping);
69+
}
70+
71+
@Configuration(proxyBeanMethods = false)
72+
@ConditionalOnBean(MeterRegistry.class)
73+
static class MetricsConfiguration {
74+
75+
@Bean
76+
TimerObservationHandlerObservationRegistryCustomizer enableTimerObservationHandler(
77+
MeterRegistry meterRegistry) {
78+
return new TimerObservationHandlerObservationRegistryCustomizer(meterRegistry);
79+
}
80+
81+
}
82+
83+
@Configuration(proxyBeanMethods = false)
84+
@ConditionalOnMissingClass("io.micrometer.tracing.handler.TracingObservationHandler")
85+
static class NoTracingConfiguration {
86+
87+
@Bean
88+
ObservationHandlerGrouping noTracingObservationHandlerGrouping() {
89+
return new NoTracingObservationHandlerGrouping();
90+
}
91+
92+
}
93+
94+
@Configuration(proxyBeanMethods = false)
95+
@ConditionalOnClass(TracingObservationHandler.class)
96+
static class TracingConfiguration {
97+
98+
@Bean
99+
ObservationHandlerGrouping tracingObservationHandlerGrouping() {
100+
return new TracingObservationHandlerGrouping();
101+
}
102+
103+
}
104+
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import java.util.Collection;
20+
21+
import io.micrometer.observation.ObservationHandler;
22+
import io.micrometer.observation.ObservationRegistry.ObservationConfig;
23+
24+
/**
25+
* Strategy to apply {@link ObservationHandler ObservationHandlers} to an
26+
* {@link ObservationConfig}.
27+
*
28+
* @author Moritz Halbritter
29+
* @since 3.0.0
30+
*/
31+
public interface ObservationHandlerGrouping {
32+
33+
/**
34+
* Applies the given list of {@code handlers} to the given {@code config}.
35+
* @param handlers the list of observation handlers
36+
* @param config the config to apply the handlers to
37+
*/
38+
void apply(Collection<ObservationHandler<?>> handlers, ObservationConfig config);
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import java.util.List;
20+
21+
import io.micrometer.observation.Observation.GlobalTagsProvider;
22+
import io.micrometer.observation.ObservationPredicate;
23+
import io.micrometer.observation.ObservationRegistry;
24+
25+
import org.springframework.beans.factory.ObjectProvider;
26+
import org.springframework.boot.util.LambdaSafe;
27+
28+
/**
29+
* Configurer to apply {@link ObservationRegistryCustomizer customizers} to
30+
* {@link ObservationRegistry observation registries}. Installs
31+
* {@link ObservationPredicate observation predicates} and {@link GlobalTagsProvider
32+
* global tag providers} into the {@link ObservationRegistry}.
33+
*
34+
* @author Moritz Halbritter
35+
*/
36+
class ObservationRegistryConfigurer {
37+
38+
private final ObjectProvider<ObservationRegistryCustomizer<?>> customizers;
39+
40+
private final ObjectProvider<ObservationPredicate> observationPredicates;
41+
42+
private final ObjectProvider<GlobalTagsProvider<?>> tagProviders;
43+
44+
ObservationRegistryConfigurer(ObjectProvider<ObservationRegistryCustomizer<?>> customizers,
45+
ObjectProvider<ObservationPredicate> observationPredicates,
46+
ObjectProvider<GlobalTagsProvider<?>> tagProviders) {
47+
this.customizers = customizers;
48+
this.observationPredicates = observationPredicates;
49+
this.tagProviders = tagProviders;
50+
}
51+
52+
void configure(ObservationRegistry registry) {
53+
registerObservationPredicates(registry);
54+
registerGlobalTagsProvider(registry);
55+
customize(registry);
56+
}
57+
58+
private void registerObservationPredicates(ObservationRegistry registry) {
59+
this.observationPredicates.orderedStream().forEach(
60+
(observationPredicate) -> registry.observationConfig().observationPredicate(observationPredicate));
61+
}
62+
63+
private void registerGlobalTagsProvider(ObservationRegistry registry) {
64+
this.tagProviders.orderedStream()
65+
.forEach((tagProvider) -> registry.observationConfig().tagsProvider(tagProvider));
66+
}
67+
68+
@SuppressWarnings("unchecked")
69+
private void customize(ObservationRegistry registry) {
70+
LambdaSafe.callbacks(ObservationRegistryCustomizer.class, asOrderedList(this.customizers), registry)
71+
.withLogger(ObservationRegistryConfigurer.class).invoke((customizer) -> customizer.customize(registry));
72+
}
73+
74+
private <T> List<T> asOrderedList(ObjectProvider<T> provider) {
75+
return provider.orderedStream().toList();
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.observation;
18+
19+
import io.micrometer.observation.ObservationRegistry;
20+
21+
/**
22+
* Callback interface that can be used to customize auto-configured
23+
* {@link ObservationRegistry ObservationRegistries}.
24+
*
25+
* @param <T> the registry type to customize
26+
* @author Moritz Halbritter
27+
* @since 3.0.0
28+
*/
29+
@FunctionalInterface
30+
public interface ObservationRegistryCustomizer<T extends ObservationRegistry> {
31+
32+
/**
33+
* Customize the given {@code registry}.
34+
* @param registry the registry to customize
35+
*/
36+
void customize(T registry);
37+
38+
}

0 commit comments

Comments
 (0)