Skip to content

Commit 5b06224

Browse files
committed
Add property for common key/values on observations
- Deprecates 'management.metrics.tags.*' Closes gh-33241
1 parent 214f060 commit 5b06224

File tree

8 files changed

+171
-14
lines changed

8 files changed

+171
-14
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public Map<String, Boolean> getEnable() {
7979
return this.enable;
8080
}
8181

82+
@DeprecatedConfigurationProperty(replacement = "management.observations.key-values")
8283
public Map<String, String> getTags() {
8384
return this.tags;
8485
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4444
import org.springframework.context.annotation.Bean;
4545
import org.springframework.context.annotation.Configuration;
46+
import org.springframework.core.annotation.Order;
4647

4748
/**
4849
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
@@ -75,6 +76,12 @@ ObservationRegistry observationRegistry() {
7576
return ObservationRegistry.create();
7677
}
7778

79+
@Bean
80+
@Order(0)
81+
PropertiesObservationFilter propertiesObservationFilter(ObservationProperties properties) {
82+
return new PropertiesObservationFilter(properties);
83+
}
84+
7885
@Configuration(proxyBeanMethods = false)
7986
@ConditionalOnClass(MeterRegistry.class)
8087
@ConditionalOnMissingClass("io.micrometer.tracing.Tracer")

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationProperties.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.observation;
1818

19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
1922
import org.springframework.boot.context.properties.ConfigurationProperties;
2023

2124
/**
@@ -30,10 +33,23 @@ public class ObservationProperties {
3033

3134
private final Http http = new Http();
3235

36+
/**
37+
* Common key-values that are applied to every observation.
38+
*/
39+
private Map<String, String> keyValues = new LinkedHashMap<>();
40+
3341
public Http getHttp() {
3442
return this.http;
3543
}
3644

45+
public Map<String, String> getKeyValues() {
46+
return this.keyValues;
47+
}
48+
49+
public void setKeyValues(Map<String, String> keyValues) {
50+
this.keyValues = keyValues;
51+
}
52+
3753
public static class Http {
3854

3955
private final Client client = new Client();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2012-2023 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.Map.Entry;
20+
21+
import io.micrometer.common.KeyValues;
22+
import io.micrometer.observation.Observation.Context;
23+
import io.micrometer.observation.ObservationFilter;
24+
25+
/**
26+
* {@link ObservationFilter} to apply settings from {@link ObservationProperties}.
27+
*
28+
* @author Moritz Halbritter
29+
*/
30+
class PropertiesObservationFilter implements ObservationFilter {
31+
32+
private final ObservationFilter delegate;
33+
34+
PropertiesObservationFilter(ObservationProperties properties) {
35+
this.delegate = createDelegate(properties);
36+
}
37+
38+
@Override
39+
public Context map(Context context) {
40+
return this.delegate.map(context);
41+
}
42+
43+
private static ObservationFilter createDelegate(ObservationProperties properties) {
44+
if (properties.getKeyValues().isEmpty()) {
45+
return (context) -> context;
46+
}
47+
KeyValues keyValues = KeyValues.of(properties.getKeyValues().entrySet(), Entry::getKey, Entry::getValue);
48+
return (context) -> context.addLowCardinalityKeyValues(keyValues);
49+
}
50+
51+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfigurationTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,21 @@ void autoConfiguresObservationFilters() {
189189
});
190190
}
191191

192+
@Test
193+
void shouldSupplyPropertiesObservationFilterBean() {
194+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesObservationFilter.class));
195+
}
196+
197+
@Test
198+
void shouldApplyCommonKeyValuesToObservations() {
199+
this.contextRunner.withPropertyValues("management.observations.key-values.a=alpha").run((context) -> {
200+
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
201+
Observation.start("keyvalues", observationRegistry).stop();
202+
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
203+
assertThat(meterRegistry.get("keyvalues").tag("a", "alpha").timer().count()).isOne();
204+
});
205+
}
206+
192207
@Test
193208
void autoConfiguresGlobalObservationConventions() {
194209
this.contextRunner.withUserConfiguration(CustomGlobalObservationConvention.class).run((context) -> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2012-2023 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.common.KeyValue;
20+
import io.micrometer.common.KeyValues;
21+
import io.micrometer.observation.Observation.Context;
22+
import org.junit.jupiter.api.Test;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* Tests for {@link PropertiesObservationFilter}.
28+
*
29+
* @author Moritz Halbritter
30+
*/
31+
class PropertiesObservationFilterTests {
32+
33+
@Test
34+
void shouldDoNothingIfKeyValuesAreEmpty() {
35+
PropertiesObservationFilter filter = createFilter();
36+
Context mapped = mapContext(filter, "a", "alpha");
37+
assertThat(mapped.getLowCardinalityKeyValues()).containsExactly(KeyValue.of("a", "alpha"));
38+
}
39+
40+
@Test
41+
void shouldAddKeyValues() {
42+
PropertiesObservationFilter filter = createFilter("b", "beta");
43+
Context mapped = mapContext(filter, "a", "alpha");
44+
assertThat(mapped.getLowCardinalityKeyValues()).containsExactly(KeyValue.of("a", "alpha"),
45+
KeyValue.of("b", "beta"));
46+
}
47+
48+
private static Context mapContext(PropertiesObservationFilter filter, String... initialKeyValues) {
49+
Context context = new Context();
50+
context.addLowCardinalityKeyValues(KeyValues.of(initialKeyValues));
51+
return filter.map(context);
52+
}
53+
54+
private static PropertiesObservationFilter createFilter(String... keyValues) {
55+
ObservationProperties properties = new ObservationProperties();
56+
for (int i = 0; i < keyValues.length; i += 2) {
57+
properties.getKeyValues().put(keyValues[i], keyValues[i + 1]);
58+
}
59+
return new PropertiesObservationFilter(properties);
60+
}
61+
62+
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,19 +1100,8 @@ These use the global registry that is not Spring-managed.
11001100

11011101
[[actuator.metrics.customizing.common-tags]]
11021102
==== Common Tags
1103-
Common tags are generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others.
1104-
Commons tags are applied to all meters and can be configured, as the following example shows:
11051103

1106-
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
1107-
----
1108-
management:
1109-
metrics:
1110-
tags:
1111-
region: "us-east-1"
1112-
stack: "prod"
1113-
----
1114-
1115-
The preceding example adds `region` and `stack` tags to all meters with a value of `us-east-1` and `prod`, respectively.
1104+
You can configure common tags using the <<actuator#actuator.observability.common-key-values, configprop:management.observations.key-values[] property>>.
11161105

11171106
NOTE: The order of common tags is important if you use Graphite.
11181107
As the order of common tags cannot be guaranteed by using this approach, Graphite users are advised to define a custom `MeterFilter` instead.

spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ To create your own observations (which will lead to metrics and traces), you can
99

1010
include::code:MyCustomObservation[]
1111

12-
NOTE: Low cardinality tags will be added to metrics and traces, while high cardinality tags will only be added to traces.
12+
NOTE: Low cardinality key-values will be added to metrics and traces, while high cardinality key-values will only be added to traces.
1313

14-
Beans of type `ObservationPredicate`, `GlobalObservationConvention` and `ObservationHandler` will be automatically registered on the `ObservationRegistry`.
14+
Beans of type `ObservationPredicate`, `GlobalObservationConvention`, `ObservationFilter` and `ObservationHandler` will be automatically registered on the `ObservationRegistry`.
1515
You can additionally register any number of `ObservationRegistryCustomizer` beans to further configure the registry.
1616

1717
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
2121
Read more about it https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/[in the reference documentation].
2222
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.
2323

24+
[[actuator.observability.common-key-values]]
25+
=== Common Key-Values
26+
Common key-values are generally used for dimensional drill-down on the operating environment, such as host, instance, region, stack, and others.
27+
Commons key-values are applied to all observations as low cardinality key-values and can be configured, as the following example shows:
28+
29+
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
30+
----
31+
management:
32+
observations:
33+
key-values:
34+
region: "us-east-1"
35+
stack: "prod"
36+
----
37+
38+
The preceding example adds `region` and `stack` key-values to all observations with a value of `us-east-1` and `prod`, respectively.
39+
2440
The next sections will provide more details about logging, metrics and traces.

0 commit comments

Comments
 (0)