Skip to content

Commit c305331

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 spring-projectsgh-29666
1 parent 6865f1f commit c305331

File tree

14 files changed

+983
-3
lines changed

14 files changed

+983
-3
lines changed

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

+2
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,100 @@
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.List;
21+
22+
import io.micrometer.core.instrument.observation.MeterObservationHandler;
23+
import io.micrometer.observation.Observation.GlobalTagsProvider;
24+
import io.micrometer.observation.ObservationHandler;
25+
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
26+
import io.micrometer.observation.ObservationPredicate;
27+
import io.micrometer.observation.ObservationRegistry;
28+
import io.micrometer.tracing.handler.TracingObservationHandler;
29+
30+
/**
31+
* Installs predicates, handlers and tag providers into a {@link ObservationRegistry}.
32+
*
33+
* This class is used by {@link ObservationAutoConfiguration} when no
34+
* {@link TracingObservationHandler} is found on the classpath.
35+
*
36+
* @author Moritz Halbritter
37+
*/
38+
class NoTracingObservationRegistryCustomizer implements ObservationRegistryCustomizer<ObservationRegistry> {
39+
40+
private final List<ObservationPredicate> observationPredicates;
41+
42+
private final List<GlobalTagsProvider<?>> tagProviders;
43+
44+
private final List<ObservationHandler<?>> observationHandlers;
45+
46+
NoTracingObservationRegistryCustomizer(List<ObservationPredicate> observationPredicates,
47+
List<GlobalTagsProvider<?>> tagProviders, List<ObservationHandler<?>> observationHandlers) {
48+
this.observationPredicates = observationPredicates;
49+
this.tagProviders = tagProviders;
50+
this.observationHandlers = observationHandlers;
51+
}
52+
53+
@Override
54+
public void customize(ObservationRegistry registry) {
55+
registerObservationPredicates(registry);
56+
registerGlobalTagsProvider(registry);
57+
registerObservationHandlers(registry);
58+
}
59+
60+
private void registerObservationPredicates(ObservationRegistry registry) {
61+
for (ObservationPredicate observationPredicate : this.observationPredicates) {
62+
registry.observationConfig().observationPredicate(observationPredicate);
63+
}
64+
}
65+
66+
private void registerGlobalTagsProvider(ObservationRegistry registry) {
67+
for (GlobalTagsProvider<?> tagProvider : this.tagProviders) {
68+
registry.observationConfig().tagsProvider(tagProvider);
69+
}
70+
}
71+
72+
@SuppressWarnings("rawtypes")
73+
protected void registerObservationHandlers(ObservationRegistry registry) {
74+
List<ObservationHandler> meterHandlers = new ArrayList<>();
75+
for (ObservationHandler<?> observationHandler : this.observationHandlers) {
76+
if (ignoreHandler(observationHandler)) {
77+
continue;
78+
}
79+
if (observationHandler instanceof MeterObservationHandler) {
80+
meterHandlers.add(observationHandler);
81+
}
82+
else {
83+
registry.observationConfig().observationHandler(observationHandler);
84+
}
85+
}
86+
if (!meterHandlers.isEmpty()) {
87+
registry.observationConfig()
88+
.observationHandler(new FirstMatchingCompositeObservationHandler(meterHandlers));
89+
}
90+
}
91+
92+
protected boolean ignoreHandler(ObservationHandler<?> observationHandler) {
93+
return false;
94+
}
95+
96+
protected List<ObservationHandler<?>> getObservationHandlers() {
97+
return this.observationHandlers;
98+
}
99+
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
return new ObservationRegistryPostProcessor(observationRegistryCustomizers);
53+
}
54+
55+
@Bean
56+
@ConditionalOnMissingBean
57+
ObservationRegistry observationRegistry() {
58+
return ObservationRegistry.create();
59+
}
60+
61+
@Configuration(proxyBeanMethods = false)
62+
@ConditionalOnBean(MeterRegistry.class)
63+
static class MetricsConfiguration {
64+
65+
@Bean
66+
TimerObservationHandlerMeterRegistryCustomizer enableTimerObservationHandler(
67+
ObservationRegistry observationRegistry) {
68+
return new TimerObservationHandlerMeterRegistryCustomizer(observationRegistry);
69+
}
70+
71+
}
72+
73+
@Configuration(proxyBeanMethods = false)
74+
@ConditionalOnMissingClass("io.micrometer.tracing.handler.TracingObservationHandler")
75+
static class NoTracingConfiguration {
76+
77+
@Bean
78+
NoTracingObservationRegistryCustomizer noTracingObservationRegistryCustomizer(
79+
List<ObservationPredicate> observationPredicates, List<GlobalTagsProvider<?>> tagProviders,
80+
List<ObservationHandler<?>> observationHandlers) {
81+
return new NoTracingObservationRegistryCustomizer(observationPredicates, tagProviders, observationHandlers);
82+
}
83+
84+
}
85+
86+
@Configuration(proxyBeanMethods = false)
87+
@ConditionalOnClass(TracingObservationHandler.class)
88+
static class TracingConfiguration {
89+
90+
@Bean
91+
TracingObservationRegistryCustomizer tracingObservationRegistryCustomizer(
92+
List<ObservationPredicate> observationPredicates, List<GlobalTagsProvider<?>> tagProviders,
93+
List<ObservationHandler<?>> observationHandlers) {
94+
return new TracingObservationRegistryCustomizer(observationPredicates, tagProviders, observationHandlers);
95+
}
96+
97+
}
98+
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.ObservationRegistry;
22+
23+
import org.springframework.beans.factory.ObjectProvider;
24+
import org.springframework.boot.util.LambdaSafe;
25+
26+
/**
27+
* Configurer to apply {@link ObservationRegistryCustomizer customizers} to
28+
* {@link ObservationRegistry observation registries}.
29+
*
30+
* @author Moritz Halbritter
31+
*/
32+
class ObservationRegistryConfigurer {
33+
34+
private final ObjectProvider<ObservationRegistryCustomizer<?>> customizers;
35+
36+
ObservationRegistryConfigurer(ObjectProvider<ObservationRegistryCustomizer<?>> customizers) {
37+
this.customizers = customizers;
38+
}
39+
40+
void configure(ObservationRegistry registry) {
41+
customize(registry);
42+
}
43+
44+
@SuppressWarnings("unchecked")
45+
private void customize(ObservationRegistry registry) {
46+
LambdaSafe.callbacks(ObservationRegistryCustomizer.class, asOrderedList(this.customizers), registry)
47+
.withLogger(ObservationRegistryConfigurer.class).invoke((customizer) -> customizer.customize(registry));
48+
}
49+
50+
private <T> List<T> asOrderedList(ObjectProvider<T> provider) {
51+
return provider.orderedStream().toList();
52+
}
53+
54+
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
import org.springframework.beans.BeansException;
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.beans.factory.config.BeanPostProcessor;
24+
25+
/**
26+
* {@link BeanPostProcessor} that delegates to a lazily created
27+
* {@link ObservationRegistryConfigurer} to post-process {@link ObservationRegistry}
28+
* beans.
29+
*
30+
* @author Moritz Halbritter
31+
*/
32+
class ObservationRegistryPostProcessor implements BeanPostProcessor {
33+
34+
private final ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers;
35+
36+
private volatile ObservationRegistryConfigurer configurer;
37+
38+
ObservationRegistryPostProcessor(ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers) {
39+
this.observationRegistryCustomizers = observationRegistryCustomizers;
40+
}
41+
42+
@Override
43+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
44+
if (bean instanceof ObservationRegistry) {
45+
getConfigurer().configure((ObservationRegistry) bean);
46+
}
47+
return bean;
48+
}
49+
50+
private ObservationRegistryConfigurer getConfigurer() {
51+
if (this.configurer == null) {
52+
this.configurer = new ObservationRegistryConfigurer(this.observationRegistryCustomizers);
53+
}
54+
return this.configurer;
55+
}
56+
57+
}

0 commit comments

Comments
 (0)