Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelDisabled;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelEnabled;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelMapConverter;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread.ThreadDetailsCustomizerProvider;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelSpringProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties;
Expand Down Expand Up @@ -196,6 +197,11 @@ public DeclarativeConfigurationCustomizerProvider distroConfigurationCustomizerP
return new ResourceCustomizerProvider();
}

@Bean
public DeclarativeConfigurationCustomizerProvider threadDetailsCustomizerProvider() {
return new ThreadDetailsCustomizerProvider();
}

@Bean
public ComponentProvider distroComponentProvider() {
return new DistroComponentProvider();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread;

import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor;
import io.opentelemetry.instrumentation.thread.internal.ThreadDetailsSpanProcessor;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -24,7 +24,7 @@ public AutoConfigurationCustomizerProvider threadDetailOtelCustomizer() {
return p ->
p.addTracerProviderCustomizer(
(builder, config) -> {
builder.addSpanProcessor(new AddThreadDetailsSpanProcessor());
builder.addSpanProcessor(new ThreadDetailsSpanProcessor());
return builder;
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread;

import io.opentelemetry.instrumentation.thread.internal.AbstractThreadDetailsCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionPropertyModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel;

/**
* Adds thread details span attributes when enabled via the {@code distribution.spring_starter}
* node.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class ThreadDetailsCustomizerProvider extends AbstractThreadDetailsCustomizerProvider {

@Override
protected boolean isEnabled(OpenTelemetryConfigurationModel model) {
DistributionModel distribution = model.getDistribution();
if (distribution == null) {
return false;
}
DistributionPropertyModel springStarter =
distribution.getAdditionalProperties().get("spring_starter");
Comment thread
trask marked this conversation as resolved.
if (springStarter == null) {
return false;
}
Object enabled = springStarter.getAdditionalProperties().get("thread_details_enabled");
if (enabled instanceof Boolean) {
return (Boolean) enabled;
}
// a String when set via environment variable substitution (thread_details_enabled: ${OTEL_...})
return enabled instanceof String && Boolean.parseBoolean((String) enabled);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel;
import java.io.ByteArrayInputStream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class ThreadDetailsCustomizerProviderTest {

@ParameterizedTest
@CsvSource(
nullValues = "NULL",
value = {
"true, true",
"false, false",
"NULL, false", // distribution node not set
", false", // thread_details_enabled key present with no value
"invalid, false",
})
@SuppressWarnings("StringConcatToTextBlock") // latest dep allows text blocks
void isEnabled(String propertyValue, boolean expected) {
String enabled =
propertyValue == null
? ""
: "distribution:\n"
+ " spring_starter:\n"
+ " thread_details_enabled: "
+ propertyValue
+ "\n";

String yaml = "file_format: \"1.0\"\n" + enabled;

OpenTelemetryConfigurationModel model =
DeclarativeConfiguration.parse(new ByteArrayInputStream(yaml.getBytes(UTF_8)));

assertThat(new ThreadDetailsCustomizerProvider().isEnabled(model)).isEqualTo(expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class AgentDistributionConfig {

private final List<String> excludeClassLoaders;

private final boolean threadDetailsEnabled;

private final InstrumentationConfig instrumentation;

public static AgentDistributionConfig get() {
Expand Down Expand Up @@ -86,6 +88,7 @@ public static void set(AgentDistributionConfig distributionConfig) {
Boolean forceSynchronousAgentListeners,
@Nullable @JsonProperty("exclude_classes") List<String> excludeClasses,
@Nullable @JsonProperty("exclude_class_loaders") List<String> excludeClassLoaders,
@Nullable @JsonProperty("thread_details_enabled") Boolean threadDetailsEnabled,
@Nullable @JsonProperty("instrumentation") InstrumentationConfig instrumentation) {
this.indyEnabled = indyEnabled != null ? indyEnabled : false;
this.forceSynchronousAgentListeners =
Expand All @@ -94,12 +97,13 @@ public static void set(AgentDistributionConfig distributionConfig) {
excludeClasses != null ? new ArrayList<>(excludeClasses) : new ArrayList<>();
this.excludeClassLoaders =
excludeClassLoaders != null ? new ArrayList<>(excludeClassLoaders) : new ArrayList<>();
this.threadDetailsEnabled = threadDetailsEnabled != null ? threadDetailsEnabled : false;
this.instrumentation = instrumentation != null ? instrumentation : new InstrumentationConfig();
}

// Default constructor for testing
AgentDistributionConfig() {
this(null, null, null, null, null);
this(null, null, null, null, null, null);
}

/**
Expand Down Expand Up @@ -162,6 +166,10 @@ public boolean isIndyEnabled() {
return indyEnabled;
}

public boolean isThreadDetailsEnabled() {
return threadDetailsEnabled;
}

public boolean isForceSynchronousAgentListeners() {
return forceSynchronousAgentListeners;
}
Expand Down Expand Up @@ -227,6 +235,9 @@ private static final class ConfigPropertiesAgentDistributionConfig
"otel.javaagent.experimental.force-synchronous-agent-listeners", false),
configProperties.getList("otel.javaagent.exclude-classes"),
configProperties.getList("otel.javaagent.exclude-class-loaders"),
configProperties.getBoolean(
"otel.javaagent.add-thread-details",
!configProperties.getBoolean("otel.instrumentation.common.v3-preview", false)),
null);
this.configProperties = configProperties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
import com.google.auto.service.AutoService;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor;
import io.opentelemetry.instrumentation.thread.internal.ThreadDetailsSpanProcessor;
import io.opentelemetry.javaagent.extension.instrumentation.internal.AgentDistributionConfig;
import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
Expand All @@ -20,8 +21,6 @@

@AutoService(AutoConfigurationCustomizerProvider.class)
public class AgentTracerProviderConfigurer implements AutoConfigurationCustomizerProvider {
private static final String ADD_THREAD_DETAILS = "otel.javaagent.add-thread-details";

@Override
public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) {
autoConfigurationCustomizer.addTracerProviderCustomizer(
Expand All @@ -32,25 +31,19 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) {
private static SdkTracerProviderBuilder configure(
SdkTracerProviderBuilder sdkTracerProviderBuilder, ConfigProperties config) {

// Register additional thread details logging span processor
boolean v3Preview = config.getBoolean("otel.instrumentation.common.v3-preview", false);
if (config.getBoolean(ADD_THREAD_DETAILS, !v3Preview)) {
sdkTracerProviderBuilder.addSpanProcessor(new AddThreadDetailsSpanProcessor());
if (AgentDistributionConfig.fromConfigProperties(config).isThreadDetailsEnabled()) {
sdkTracerProviderBuilder.addSpanProcessor(new ThreadDetailsSpanProcessor());
}

maybeEnableLoggingExporter(sdkTracerProviderBuilder, config);

return sdkTracerProviderBuilder;
}

private static void maybeEnableLoggingExporter(
SdkTracerProviderBuilder builder, ConfigProperties config) {
if (EarlyInitAgentConfig.get().isDebug()) {
// don't install another instance if the user has already explicitly requested it.
if (loggingExporterIsNotAlreadyConfigured(config)) {
builder.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()));
sdkTracerProviderBuilder.addSpanProcessor(
SimpleSpanProcessor.create(LoggingSpanExporter.create()));
}
}

return sdkTracerProviderBuilder;
}

private static boolean loggingExporterIsNotAlreadyConfigured(ConfigProperties config) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.tooling.config;

import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.thread.internal.AbstractThreadDetailsCustomizerProvider;
import io.opentelemetry.javaagent.extension.instrumentation.internal.AgentDistributionConfig;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel;

/** Adds thread details span attributes when enabled via the {@code distribution.javaagent} node. */
@AutoService(DeclarativeConfigurationCustomizerProvider.class)
public final class ThreadDetailsCustomizerProvider extends AbstractThreadDetailsCustomizerProvider {

@Override
public int order() {
// run after JavaagentDistributionAccessCustomizerProvider (default order) has populated
// AgentDistributionConfig from the distribution.javaagent node
return 1;
}

@Override
protected boolean isEnabled(OpenTelemetryConfigurationModel model) {
return AgentDistributionConfig.get().isThreadDetailsEnabled();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ void testForceSynchronousAgentListeners() {
assertThat(AgentDistributionConfig.get().isForceSynchronousAgentListeners()).isFalse();
}

@Test
void testThreadDetailsEnabled() {
assertThat(AgentDistributionConfig.get().isThreadDetailsEnabled()).isTrue();
}

@Test
void testExcludeClasses() {
assertThat(AgentDistributionConfig.get().getExcludeClasses())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ distribution:
javaagent:
indy/development: true
force_synchronous_agent_listeners/development: false
thread_details_enabled: true
exclude_classes:
- com.example.excluded.Class1
- com.example.excluded.Class2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.thread.internal;

import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorPropertyModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel;
import java.util.ArrayList;
import java.util.List;

/**
* Adds the {@link ThreadDetailsSpanProcessor} (via the {@link ThreadDetailsComponentProvider}) to
* the declarative configuration model when {@link #isEnabled(OpenTelemetryConfigurationModel)}
* returns {@code true}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public abstract class AbstractThreadDetailsCustomizerProvider
implements DeclarativeConfigurationCustomizerProvider {

protected abstract boolean isEnabled(OpenTelemetryConfigurationModel model);

@Override
public void customize(DeclarativeConfigurationCustomizer customizer) {
customizer.addModelCustomizer(
model -> {
maybeAddThreadDetailsProcessor(model);
return model;
});
}

private void maybeAddThreadDetailsProcessor(OpenTelemetryConfigurationModel model) {
if (!isEnabled(model)) {
return;
}
TracerProviderModel tracerProvider = model.getTracerProvider();
if (tracerProvider == null) {
tracerProvider = new TracerProviderModel();
model.withTracerProvider(tracerProvider);
}
List<SpanProcessorModel> processors = tracerProvider.getProcessors();
if (processors == null) {
processors = new ArrayList<>();
tracerProvider.withProcessors(processors);
}
processors.add(
new SpanProcessorModel()
.withAdditionalProperty(
ThreadDetailsComponentProvider.NAME, new SpanProcessorPropertyModel()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.thread.internal;

import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;

/**
* Declarative configuration component provider that exposes {@link ThreadDetailsSpanProcessor}
* under the name {@value #NAME}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class ThreadDetailsComponentProvider implements ComponentProvider {

public static final String NAME = "thread_details";

@Override
public Class<SpanProcessor> getType() {
return SpanProcessor.class;
}

@Override
public String getName() {
return NAME;
}

@Override
public SpanProcessor create(DeclarativeConfigProperties config) {
return new ThreadDetailsSpanProcessor();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class AddThreadDetailsSpanProcessor implements SpanProcessor {
public class ThreadDetailsSpanProcessor implements SpanProcessor {

// attributes are not stable yet
public static final AttributeKey<Long> THREAD_ID = longKey("thread.id");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.opentelemetry.instrumentation.thread.internal.ThreadDetailsComponentProvider
Loading
Loading