Add property for common key/values on observations

- Deprecates 'management.metrics.tags.*'

Closes gh-33241
pull/35890/head
Moritz Halbritter 1 year ago
parent 214f06083b
commit 5b06224af5

@ -79,6 +79,7 @@ public class MetricsProperties {
return this.enable;
}
@DeprecatedConfigurationProperty(replacement = "management.observations.key-values")
public Map<String, String> getTags() {
return this.tags;
}

@ -43,6 +43,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
@ -75,6 +76,12 @@ public class ObservationAutoConfiguration {
return ObservationRegistry.create();
}
@Bean
@Order(0)
PropertiesObservationFilter propertiesObservationFilter(ObservationProperties properties) {
return new PropertiesObservationFilter(properties);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")

@ -16,6 +16,9 @@
package org.springframework.boot.actuate.autoconfigure.observation;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@ -30,10 +33,23 @@ public class ObservationProperties {
private final Http http = new Http();
/**
* Common key-values that are applied to every observation.
*/
private Map<String, String> keyValues = new LinkedHashMap<>();
public Http getHttp() {
return this.http;
}
public Map<String, String> getKeyValues() {
return this.keyValues;
}
public void setKeyValues(Map<String, String> keyValues) {
this.keyValues = keyValues;
}
public static class Http {
private final Client client = new Client();

@ -0,0 +1,51 @@
/*
* Copyright 2012-2023 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.Map.Entry;
import io.micrometer.common.KeyValues;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationFilter;
/**
* {@link ObservationFilter} to apply settings from {@link ObservationProperties}.
*
* @author Moritz Halbritter
*/
class PropertiesObservationFilter implements ObservationFilter {
private final ObservationFilter delegate;
PropertiesObservationFilter(ObservationProperties properties) {
this.delegate = createDelegate(properties);
}
@Override
public Context map(Context context) {
return this.delegate.map(context);
}
private static ObservationFilter createDelegate(ObservationProperties properties) {
if (properties.getKeyValues().isEmpty()) {
return (context) -> context;
}
KeyValues keyValues = KeyValues.of(properties.getKeyValues().entrySet(), Entry::getKey, Entry::getValue);
return (context) -> context.addLowCardinalityKeyValues(keyValues);
}
}

@ -189,6 +189,21 @@ class ObservationAutoConfigurationTests {
});
}
@Test
void shouldSupplyPropertiesObservationFilterBean() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesObservationFilter.class));
}
@Test
void shouldApplyCommonKeyValuesToObservations() {
this.contextRunner.withPropertyValues("management.observations.key-values.a=alpha").run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("keyvalues", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.get("keyvalues").tag("a", "alpha").timer().count()).isOne();
});
}
@Test
void autoConfiguresGlobalObservationConventions() {
this.contextRunner.withUserConfiguration(CustomGlobalObservationConvention.class).run((context) -> {

@ -0,0 +1,62 @@
/*
* Copyright 2012-2023 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.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.observation.Observation.Context;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PropertiesObservationFilter}.
*
* @author Moritz Halbritter
*/
class PropertiesObservationFilterTests {
@Test
void shouldDoNothingIfKeyValuesAreEmpty() {
PropertiesObservationFilter filter = createFilter();
Context mapped = mapContext(filter, "a", "alpha");
assertThat(mapped.getLowCardinalityKeyValues()).containsExactly(KeyValue.of("a", "alpha"));
}
@Test
void shouldAddKeyValues() {
PropertiesObservationFilter filter = createFilter("b", "beta");
Context mapped = mapContext(filter, "a", "alpha");
assertThat(mapped.getLowCardinalityKeyValues()).containsExactly(KeyValue.of("a", "alpha"),
KeyValue.of("b", "beta"));
}
private static Context mapContext(PropertiesObservationFilter filter, String... initialKeyValues) {
Context context = new Context();
context.addLowCardinalityKeyValues(KeyValues.of(initialKeyValues));
return filter.map(context);
}
private static PropertiesObservationFilter createFilter(String... keyValues) {
ObservationProperties properties = new ObservationProperties();
for (int i = 0; i < keyValues.length; i += 2) {
properties.getKeyValues().put(keyValues[i], keyValues[i + 1]);
}
return new PropertiesObservationFilter(properties);
}
}

@ -1100,19 +1100,8 @@ These use the global registry that is not Spring-managed.
[[actuator.metrics.customizing.common-tags]]
==== Common Tags
Common tags are generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others.
Commons tags are applied to all meters and can be configured, as the following example shows:
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
management:
metrics:
tags:
region: "us-east-1"
stack: "prod"
----
The preceding example adds `region` and `stack` tags to all meters with a value of `us-east-1` and `prod`, respectively.
You can configure common tags using the <<actuator#actuator.observability.common-key-values, configprop:management.observations.key-values[] property>>.
NOTE: The order of common tags is important if you use Graphite.
As the order of common tags cannot be guaranteed by using this approach, Graphite users are advised to define a custom `MeterFilter` instead.

@ -9,9 +9,9 @@ To create your own observations (which will lead to metrics and traces), you can
include::code:MyCustomObservation[]
NOTE: Low cardinality tags will be added to metrics and traces, while high cardinality tags will only be added to traces.
NOTE: Low cardinality key-values will be added to metrics and traces, while high cardinality key-values will only be added to traces.
Beans of type `ObservationPredicate`, `GlobalObservationConvention` and `ObservationHandler` will be automatically registered on the `ObservationRegistry`.
Beans of type `ObservationPredicate`, `GlobalObservationConvention`, `ObservationFilter` and `ObservationHandler` will be automatically registered on the `ObservationRegistry`.
You can additionally register any number of `ObservationRegistryCustomizer` beans to further configure the registry.
For more details please see the https://micrometer.io/docs/observation[Micrometer Observation documentation].
@ -21,4 +21,20 @@ For JDBC, the https://github.com/jdbc-observations/datasource-micrometer[Datasou
Read more about it https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/[in the reference documentation].
For R2DBC, the https://github.com/spring-projects-experimental/r2dbc-micrometer-spring-boot[Spring Boot Auto Configuration for R2DBC Observation] creates observations for R2DBC query invocations.
[[actuator.observability.common-key-values]]
=== Common Key-Values
Common key-values are generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others.
Commons key-values are applied to all observations as low cardinality key-values and can be configured, as the following example shows:
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
management:
observations:
key-values:
region: "us-east-1"
stack: "prod"
----
The preceding example adds `region` and `stack` key-values to all observations with a value of `us-east-1` and `prod`, respectively.
The next sections will provide more details about logging, metrics and traces.

Loading…
Cancel
Save