Skip to content

Commit c0066c8

Browse files
committed
add spring starter declarative config span logging
1 parent 8ab82e5 commit c0066c8

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging;
7+
8+
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
12+
import io.opentelemetry.instrumentation.logging.internal.AbstractSpanLoggingCustomizerProvider;
13+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelEnabled;
14+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider;
15+
import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider;
16+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
17+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
18+
import org.springframework.context.annotation.Bean;
19+
import org.springframework.context.annotation.Conditional;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
/**
23+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
24+
* any time.
25+
*/
26+
@Conditional(OtelEnabled.class)
27+
@ConditionalOnClass(LoggingSpanExporter.class)
28+
@Configuration
29+
public class DeclarativeConfigLoggingExporterAutoConfiguration {
30+
@Bean
31+
public DeclarativeConfigurationCustomizerProvider spanLoggingCustomizerProvider() {
32+
return new SpanLoggingCustomizerProvider();
33+
}
34+
35+
public static class SpanLoggingCustomizerProvider extends AbstractSpanLoggingCustomizerProvider {
36+
37+
@Override
38+
protected boolean isEnabled(OpenTelemetryConfigurationModel model) {
39+
DeclarativeConfigProperties instrumentation =
40+
SdkConfigProvider.create(model).getInstrumentationConfig();
41+
if (instrumentation == null) {
42+
return false;
43+
}
44+
45+
return instrumentation
46+
.getStructured("java", empty())
47+
.getStructured("spring_starter", empty())
48+
.getBoolean("debug", false);
49+
}
50+
}
51+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging;
7+
8+
import static io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.DeclarativeConfigLoggingExporterAutoConfiguration.SpanLoggingCustomizerProvider;
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
12+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
13+
import java.io.ByteArrayInputStream;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.function.Function;
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.CsvSource;
20+
21+
class SpanLoggingCustomizerProviderTest {
22+
23+
@ParameterizedTest
24+
@CsvSource({
25+
"true, false, true",
26+
"false, false, false",
27+
", false, false", // empty value means property is not set
28+
"invalid, false, false",
29+
"true, true, true",
30+
})
31+
void addSpanLoggingExporter(String propertyValue, boolean alreadyAdded, boolean expected) {
32+
String debug =
33+
propertyValue == null
34+
? ""
35+
: "instrumentation/development: \n"
36+
+ " java: \n"
37+
+ " spring_starter: \n"
38+
+ " debug: "
39+
+ propertyValue;
40+
41+
String yaml =
42+
alreadyAdded
43+
? "file_format: \"1.0-rc.1\"\n"
44+
+ "tracer_provider:\n"
45+
+ " processors:\n"
46+
+ " - simple:\n"
47+
+ " exporter:\n"
48+
+ " console: {}\n"
49+
: "file_format: \"1.0-rc.1\"\n" + debug;
50+
51+
OpenTelemetryConfigurationModel model =
52+
applyCustomizer(
53+
DeclarativeConfiguration.parse(
54+
new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))),
55+
new SpanLoggingCustomizerProvider());
56+
57+
String console = "ConsoleExporterModel";
58+
if (expected) {
59+
assertThat(model.toString()).containsOnlyOnce(console);
60+
} else {
61+
assertThat(model.toString()).doesNotContain(console);
62+
}
63+
}
64+
65+
private static OpenTelemetryConfigurationModel applyCustomizer(
66+
OpenTelemetryConfigurationModel model, SpanLoggingCustomizerProvider provider) {
67+
List<Function<OpenTelemetryConfigurationModel, OpenTelemetryConfigurationModel>> customizers =
68+
new ArrayList<>();
69+
provider.customize(c -> customizers.add(c));
70+
for (Function<OpenTelemetryConfigurationModel, OpenTelemetryConfigurationModel> customizer :
71+
customizers) {
72+
model = customizer.apply(model);
73+
}
74+
return model;
75+
}
76+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.tooling;
7+
8+
import com.google.auto.service.AutoService;
9+
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
10+
import io.opentelemetry.instrumentation.logging.internal.AbstractSpanLoggingCustomizerProvider;
11+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider;
12+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
13+
14+
/** Adds span logging exporter for debug mode */
15+
@AutoService(DeclarativeConfigurationCustomizerProvider.class)
16+
public class SpanLoggingCustomizerProvider extends AbstractSpanLoggingCustomizerProvider {
17+
18+
@Override
19+
protected boolean isEnabled(OpenTelemetryConfigurationModel model) {
20+
// read from system properties as it's an early init property and the config bridge is not
21+
// available here
22+
return ConfigPropertiesUtil.getBoolean("otel.javaagent.debug", false);
23+
}
24+
}

sdk-autoconfigure-support/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ group = "io.opentelemetry.instrumentation"
77

88
dependencies {
99
api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
10+
api("io.opentelemetry:opentelemetry-sdk-extension-incubator")
1011

1112
compileOnly("com.google.code.findbugs:annotations")
1213
testCompileOnly("com.google.code.findbugs:annotations")
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package io.opentelemetry.instrumentation.logging.internal;
2+
3+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer;
4+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider;
5+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ConsoleExporterModel;
6+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
7+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel;
8+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel;
9+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel;
10+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel;
11+
12+
/**
13+
* Adds span logging exporter for debug mode
14+
*
15+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
16+
* at any time.
17+
*/
18+
public abstract class AbstractSpanLoggingCustomizerProvider
19+
implements DeclarativeConfigurationCustomizerProvider {
20+
21+
protected abstract boolean isEnabled(OpenTelemetryConfigurationModel model);
22+
23+
@Override
24+
public void customize(DeclarativeConfigurationCustomizer customizer) {
25+
customizer.addModelCustomizer(
26+
model -> {
27+
maybeEnableLoggingExporter(model);
28+
return model;
29+
});
30+
}
31+
32+
private void maybeEnableLoggingExporter(OpenTelemetryConfigurationModel model) {
33+
if (!isEnabled(model)) {
34+
return;
35+
}
36+
// don't install another instance if the user has already explicitly requested it.
37+
if (loggingExporterIsAlreadyConfigured(model)) {
38+
return;
39+
}
40+
TracerProviderModel tracerProvider = model.getTracerProvider();
41+
if (tracerProvider == null) {
42+
tracerProvider = new TracerProviderModel();
43+
model.withTracerProvider(tracerProvider);
44+
}
45+
SpanProcessorModel processor =
46+
new SpanProcessorModel()
47+
.withSimple(
48+
new SimpleSpanProcessorModel()
49+
.withExporter(new SpanExporterModel().withConsole(new ConsoleExporterModel())));
50+
tracerProvider.getProcessors().add(processor);
51+
}
52+
53+
private static boolean loggingExporterIsAlreadyConfigured(OpenTelemetryConfigurationModel model) {
54+
TracerProviderModel tracerProvider = model.getTracerProvider();
55+
if (tracerProvider == null) {
56+
return false;
57+
}
58+
for (SpanProcessorModel processor : tracerProvider.getProcessors()) {
59+
SimpleSpanProcessorModel simple = processor.getSimple();
60+
if (simple == null) {
61+
continue;
62+
}
63+
SpanExporterModel exporter = simple.getExporter();
64+
if (exporter == null) {
65+
continue;
66+
}
67+
if (exporter.getConsole() != null) {
68+
return true;
69+
}
70+
}
71+
return false;
72+
}
73+
}

0 commit comments

Comments
 (0)