Skip to content

Commit

Permalink
Add auto-configuration for Micrometer 2.0.0 Observation API
Browse files Browse the repository at this point in the history
- 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

Closes gh-29666
  • Loading branch information
mhalbritter committed Apr 5, 2022
1 parent 4a8901b commit 5ab9112
Show file tree
Hide file tree
Showing 17 changed files with 1,102 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ dependencies {
optional("com.zaxxer:HikariCP")
optional("io.dropwizard.metrics:metrics-jmx")
optional("io.lettuce:lettuce-core")
optional("io.micrometer:micrometer-observation")
optional("io.micrometer:micrometer-core")
optional("io.micrometer:micrometer-tracing-api")
optional("io.micrometer:micrometer-binders")
optional("io.micrometer:micrometer-registry-appoptics")
optional("io.micrometer:micrometer-registry-atlas") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2012-2022 the original author or 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 org.springframework.boot.actuate.autoconfigure.observation;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.observation.Observation.GlobalTagsProvider;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.handler.TracingObservationHandler;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
*
* @author Moritz Halbritter
* @since 3.0.0
*/
@AutoConfiguration(after = CompositeMeterRegistryAutoConfiguration.class)
@ConditionalOnClass(ObservationRegistry.class)
public class ObservationAutoConfiguration {

@Bean
static ObservationRegistryPostProcessor observationRegistryPostProcessor(
ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers,
ObjectProvider<ObservationPredicate> observationPredicates,
ObjectProvider<GlobalTagsProvider<?>> tagProviders,
ObjectProvider<ObservationHandler<?>> observationHandlers,
ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping) {
return new ObservationRegistryPostProcessor(observationRegistryCustomizers, observationPredicates, tagProviders,
observationHandlers, observationHandlerGrouping);
}

@Bean
@ConditionalOnMissingBean
ObservationRegistry observationRegistry() {
return ObservationRegistry.create();
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MeterRegistry.class)
static class MetricsConfiguration {

@Bean
TimerObservationHandlerObservationRegistryCustomizer enableTimerObservationHandler(
MeterRegistry meterRegistry) {
return new TimerObservationHandlerObservationRegistryCustomizer(meterRegistry);
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("io.micrometer.tracing.handler.TracingObservationHandler")
static class NoTracingConfiguration {

@Bean
ObservationHandlerGrouping noTracingObservationHandlerGrouping() {
return new OnlyMetricsObservationHandlerGrouping();
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(TracingObservationHandler.class)
static class TracingConfiguration {

@Bean
ObservationHandlerGrouping tracingObservationHandlerGrouping() {
return new TracingObservationHandlerGrouping();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2012-2022 the original author or 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 org.springframework.boot.actuate.autoconfigure.observation;

import java.util.Collection;

import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry.ObservationConfig;

/**
* Strategy to apply {@link ObservationHandler ObservationHandlers} to an
* {@link ObservationConfig}.
*
* @author Moritz Halbritter
*/
interface ObservationHandlerGrouping {

/**
* Applies the given list of {@code handlers} to the given {@code config}.
* @param handlers the list of observation handlers
* @param config the config to apply the handlers to
*/
void apply(Collection<ObservationHandler<?>> handlers, ObservationConfig config);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2012-2022 the original author or 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 org.springframework.boot.actuate.autoconfigure.observation;

import java.util.List;

import io.micrometer.observation.Observation.GlobalTagsProvider;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.util.LambdaSafe;

/**
* Configurer to apply {@link ObservationRegistryCustomizer customizers} to
* {@link ObservationRegistry observation registries}. Installs
* {@link ObservationPredicate observation predicates} and {@link GlobalTagsProvider
* global tag providers} into the {@link ObservationRegistry}. Also uses a
* {@link ObservationHandlerGrouping} to group handlers, which are then added to the
* {@link ObservationRegistry}.
*
* @author Moritz Halbritter
*/
class ObservationRegistryConfigurer {

private final ObjectProvider<ObservationRegistryCustomizer<?>> customizers;

private final ObjectProvider<ObservationPredicate> observationPredicates;

private final ObjectProvider<GlobalTagsProvider<?>> tagProviders;

private final ObjectProvider<ObservationHandler<?>> observationHandlers;

private final ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping;

ObservationRegistryConfigurer(ObjectProvider<ObservationRegistryCustomizer<?>> customizers,
ObjectProvider<ObservationPredicate> observationPredicates,
ObjectProvider<GlobalTagsProvider<?>> tagProviders,
ObjectProvider<ObservationHandler<?>> observationHandlers,
ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping) {
this.customizers = customizers;
this.observationPredicates = observationPredicates;
this.tagProviders = tagProviders;
this.observationHandlers = observationHandlers;
this.observationHandlerGrouping = observationHandlerGrouping;
}

void configure(ObservationRegistry registry) {
registerObservationPredicates(registry);
registerGlobalTagsProvider(registry);
registerHandlers(registry);
customize(registry);
}

private void registerHandlers(ObservationRegistry registry) {
this.observationHandlerGrouping.getObject().apply(asOrderedList(this.observationHandlers),
registry.observationConfig());
}

private void registerObservationPredicates(ObservationRegistry registry) {
this.observationPredicates.orderedStream().forEach(
(observationPredicate) -> registry.observationConfig().observationPredicate(observationPredicate));
}

private void registerGlobalTagsProvider(ObservationRegistry registry) {
this.tagProviders.orderedStream()
.forEach((tagProvider) -> registry.observationConfig().tagsProvider(tagProvider));
}

@SuppressWarnings("unchecked")
private void customize(ObservationRegistry registry) {
LambdaSafe.callbacks(ObservationRegistryCustomizer.class, asOrderedList(this.customizers), registry)
.withLogger(ObservationRegistryConfigurer.class).invoke((customizer) -> customizer.customize(registry));
}

private <T> List<T> asOrderedList(ObjectProvider<T> provider) {
return provider.orderedStream().toList();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2012-2022 the original author or 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 org.springframework.boot.actuate.autoconfigure.observation;

import io.micrometer.observation.ObservationRegistry;

/**
* Callback interface that can be used to customize auto-configured
* {@link ObservationRegistry observation registries}.
*
* @param <T> the registry type to customize
* @author Moritz Halbritter
* @since 3.0.0
*/
@FunctionalInterface
public interface ObservationRegistryCustomizer<T extends ObservationRegistry> {

/**
* Customize the given {@code registry}.
* @param registry the registry to customize
*/
void customize(T registry);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2012-2022 the original author or 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 org.springframework.boot.actuate.autoconfigure.observation;

import io.micrometer.observation.Observation.GlobalTagsProvider;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
* {@link BeanPostProcessor} that delegates to a lazily created
* {@link ObservationRegistryConfigurer} to post-process {@link ObservationRegistry}
* beans.
*
* @author Moritz Halbritter
*/
class ObservationRegistryPostProcessor implements BeanPostProcessor {

private final ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers;

private final ObjectProvider<ObservationPredicate> observationPredicates;

private final ObjectProvider<GlobalTagsProvider<?>> tagProviders;

private final ObjectProvider<ObservationHandler<?>> observationHandlers;

private final ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping;

private volatile ObservationRegistryConfigurer configurer;

ObservationRegistryPostProcessor(ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers,
ObjectProvider<ObservationPredicate> observationPredicates,
ObjectProvider<GlobalTagsProvider<?>> tagProviders,
ObjectProvider<ObservationHandler<?>> observationHandlers,
ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping) {
this.observationRegistryCustomizers = observationRegistryCustomizers;
this.observationPredicates = observationPredicates;
this.tagProviders = tagProviders;
this.observationHandlers = observationHandlers;
this.observationHandlerGrouping = observationHandlerGrouping;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ObservationRegistry) {
getConfigurer().configure((ObservationRegistry) bean);
}
return bean;
}

private ObservationRegistryConfigurer getConfigurer() {
if (this.configurer == null) {
this.configurer = new ObservationRegistryConfigurer(this.observationRegistryCustomizers,
this.observationPredicates, this.tagProviders, this.observationHandlers,
this.observationHandlerGrouping);
}
return this.configurer;
}

}
Loading

0 comments on commit 5ab9112

Please sign in to comment.