Skip to content

Commit c1b295f

Browse files
jonatan-ivanovphilwebb
authored andcommitted
Log correlation IDs when Micrometer tracing is being used
Add support for logging correlation IDs with Logback or Log4J2 whenever Micrometer tracing is being used. The `LoggingSystemProperties` class now accepts a defualt value resolver which will be used whenever a value isn't in the environment. The `AbstractLoggingSystem` provides a resolver that supports the `logging.pattern.correlation` property and will return a value whenever `LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY` is set. Using `LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY` allows us to provide a consistent width for the correlation ID, even when it's missing from the MDC. The exact correlation pattern returned will depend on the `LoggingSytem` implementation. Currently Logback and Log4J2 are supported and both make use of a custom converter which delegates to a new `CorrelationIdFormatter` class. Closes gh-33280
1 parent b6120d5 commit c1b295f

File tree

30 files changed

+1086
-50
lines changed

30 files changed

+1086
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.tracing;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.env.EnvironmentPostProcessor;
21+
import org.springframework.boot.logging.LoggingSystem;
22+
import org.springframework.core.env.ConfigurableEnvironment;
23+
import org.springframework.core.env.Environment;
24+
import org.springframework.core.env.PropertySource;
25+
import org.springframework.util.ClassUtils;
26+
27+
/**
28+
* {@link EnvironmentPostProcessor} to add a {@link PropertySource} to support log
29+
* correlation IDs when Micrometer is present. Adds support for the
30+
* {@value LoggingSystem#EXPECT_CORRELATION_ID_PROPERTY} property by delegating to
31+
* {@code management.tracing.enabled}.
32+
*
33+
* @author Jonatan Ivanov
34+
* @author Phillip Webb
35+
*/
36+
class LogCorrelationEnvironmentPostProcessor implements EnvironmentPostProcessor {
37+
38+
@Override
39+
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
40+
if (ClassUtils.isPresent("io.micrometer.tracing.Tracer", application.getClassLoader())) {
41+
environment.getPropertySources().addLast(new LogCorrelationPropertySource(this, environment));
42+
}
43+
}
44+
45+
/**
46+
* Log correlation {@link PropertySource}.
47+
*/
48+
private static class LogCorrelationPropertySource extends PropertySource<Object> {
49+
50+
private static final String NAME = "logCorrelation";
51+
52+
private final Environment environment;
53+
54+
LogCorrelationPropertySource(Object source, Environment environment) {
55+
super(NAME, source);
56+
this.environment = environment;
57+
}
58+
59+
@Override
60+
public Object getProperty(String name) {
61+
if (name.equals(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY)) {
62+
return this.environment.getProperty("management.tracing.enabled", Boolean.class, Boolean.TRUE);
63+
}
64+
return null;
65+
}
66+
67+
}
68+
69+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# Failure Analyzers
22
org.springframework.boot.diagnostics.FailureAnalyzer=\
33
org.springframework.boot.actuate.autoconfigure.metrics.ValidationFailureAnalyzer
4+
5+
# Environment Post Processors
6+
org.springframework.boot.env.EnvironmentPostProcessor=\
7+
org.springframework.boot.actuate.autoconfigure.tracing.LogCorrelationEnvironmentPostProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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.tracing;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.SpringApplication;
22+
import org.springframework.boot.logging.LoggingSystem;
23+
import org.springframework.boot.test.util.TestPropertyValues;
24+
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
25+
import org.springframework.core.env.ConfigurableEnvironment;
26+
import org.springframework.core.env.StandardEnvironment;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Tests for {@link LogCorrelationEnvironmentPostProcessor}.
32+
*
33+
* @author Jonatan Ivanov
34+
* @author Phillip Webb
35+
*/
36+
class LogCorrelationEnvironmentPostProcessorTests {
37+
38+
private final ConfigurableEnvironment environment = new StandardEnvironment();
39+
40+
private final SpringApplication application = new SpringApplication();
41+
42+
private final LogCorrelationEnvironmentPostProcessor postProcessor = new LogCorrelationEnvironmentPostProcessor();
43+
44+
@Test
45+
void getExpectCorrelationIdPropertyWhenMicrometerPresentReturnsTrue() {
46+
this.postProcessor.postProcessEnvironment(this.environment, this.application);
47+
assertThat(this.environment.getProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, Boolean.class, false))
48+
.isTrue();
49+
}
50+
51+
@Test
52+
@ClassPathExclusions("micrometer-tracing-*.jar")
53+
void getExpectCorrelationIdPropertyWhenMicrometerMissingReturnsFalse() {
54+
this.postProcessor.postProcessEnvironment(this.environment, this.application);
55+
assertThat(this.environment.getProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, Boolean.class, false))
56+
.isFalse();
57+
}
58+
59+
@Test
60+
void getExpectCorrelationIdPropertyWhenTracingDisabledReturnsFalse() {
61+
TestPropertyValues.of("management.tracing.enabled=false").applyTo(this.environment);
62+
this.postProcessor.postProcessEnvironment(this.environment, this.application);
63+
assertThat(this.environment.getProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, Boolean.class, false))
64+
.isFalse();
65+
}
66+
67+
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,25 @@ Now open the Zipkin UI at `http://localhost:9411` and press the "Run Query" butt
6565
You should see one trace.
6666
Press the "Show" button to see the details of that trace.
6767

68-
TIP: You can include the current trace and span id in the logs by setting the `logging.pattern.level` property to `%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]`
68+
69+
70+
[[actuator.micrometer-tracing.logging]]
71+
=== Logging Correlation IDs
72+
Correlation IDs provide a helpful way to link lines in your log files to distributed traces.
73+
By default, as long as configprop:management.tracing.enabled[] has not been set to `false`, Spring Boot will include correlation IDs in your logs whenever you are using Micrometer tracing.
74+
75+
The default correlation ID is built from `traceId` and `spanId` https://logback.qos.ch/manual/mdc.html[MDC] values.
76+
For example, if Micrometer tracing has added an MDC `traceId` of `803B448A0489F84084905D3093480352` and an MDC `spanId` of `3425F23BB2432450` the log output will include the correlation ID `[803B448A0489F84084905D3093480352-3425F23BB2432450]`.
77+
78+
If you prefer to use a different format for your correlation ID, you can use the configprop:logging.pattern.correlation[] property to define one.
79+
For example, the following will provide a correlation ID for Logback in format previously used by Spring Sleuth:
80+
81+
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
82+
----
83+
logging:
84+
pattern:
85+
correlation: "[${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
86+
----
6987

7088

7189

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The following items are output:
3232
* Process ID.
3333
* A `---` separator to distinguish the start of actual log messages.
3434
* Thread name: Enclosed in square brackets (may be truncated for console output).
35+
* Correlation ID: If tracing is enabled (not shown in the sample above)
3536
* Logger name: This is usually the source class name (often abbreviated).
3637
* The log message.
3738

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import java.util.LinkedHashSet;
2323
import java.util.Map;
2424
import java.util.Set;
25+
import java.util.function.Function;
2526

2627
import org.springframework.core.env.Environment;
2728
import org.springframework.core.io.ClassPathResource;
@@ -174,7 +175,35 @@ protected final String getPackagedConfigFile(String fileName) {
174175
}
175176

176177
protected final void applySystemProperties(Environment environment, LogFile logFile) {
177-
new LoggingSystemProperties(environment).apply(logFile);
178+
new LoggingSystemProperties(environment, getDefaultValueResolver(environment), null).apply(logFile);
179+
}
180+
181+
/**
182+
* Return the default value resolver to use when resolving system properties.
183+
* @param environment the environment
184+
* @return the default value resolver
185+
* @since 3.2.0
186+
*/
187+
protected Function<String, String> getDefaultValueResolver(Environment environment) {
188+
String defaultLogCorrelationPattern = getDefaultLogCorrelationPattern();
189+
return (name) -> {
190+
if (StringUtils.hasLength(defaultLogCorrelationPattern)
191+
&& LoggingSystemProperty.CORRELATION_PATTERN.getApplicationPropertyName().equals(name)
192+
&& environment.getProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, Boolean.class, false)) {
193+
return defaultLogCorrelationPattern;
194+
}
195+
return null;
196+
};
197+
}
198+
199+
/**
200+
* Return the default log correlation pattern or {@code null} if log correlation
201+
* patterns are not supported.
202+
* @return the default log correlation pattern
203+
* @since 3.2.0
204+
*/
205+
protected String getDefaultLogCorrelationPattern() {
206+
return null;
178207
}
179208

180209
/**

0 commit comments

Comments
 (0)