Skip to content

Commit b795cb4

Browse files
authored
POTEL 53 - Automatically set span factory based on presence of OpenTelemetry (#3858)
* Auto config for Spring Boot combined with OTel but without agent * try to cleanup otel classloader * make agent, no agent and agent without auto init work for spring boot * Fix ignored instrumentation for OTel without agent; separate sample for no agent * fix test result upload on CI * automatically detect otel and use OtelSpanFactory * changelog
1 parent ca3da4b commit b795cb4

File tree

16 files changed

+113
-71
lines changed

16 files changed

+113
-71
lines changed

.github/workflows/system-tests-backend.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ jobs:
9292
9393
- name: Start server and run integration test for sentry-cli commands
9494
run: |
95-
test/system-test-sentry-server-start.sh > sentry-mock-server.txt 2>&1 & test/system-test-spring-server-start.sh "${{ matrix.sample }}" "${{ matrix.agent }}" "${{ matrix.agent-auto-init }}" > spring-server.txt 2>&1 & test/wait-for-spring.sh && ./gradlew :sentry-samples:${{ matrix.sample }}:systemTest
95+
test/system-test-run.sh "${{ matrix.sample }}" "${{ matrix.agent }}" "${{ matrix.agent-auto-init }}"
9696
9797
- name: Upload test results
9898
if: always()

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
- The `sentry-opentelemetry-extra` module has been removed again, most classes have been moved to `sentry-opentelemetry-bootstrap` which is loaded into the bootstrap classloader (i.e. `null`) when our Java agent is used. The rest has been moved into `sentry-opentelemetry-agentcustomization` and is loaded into the agent classloader when our Java agent is used.
2323
- The `sentry-opentelemetry-bootstrap` and `sentry-opentelemetry-agentcustomization` modules can be used without the agent as well, in which case all classes are loaded into the application classloader. Check out our `sentry-samples-spring-boot-jakarta-opentelemetry-noagent` sample.
2424
- In this mode the SDK makes use of `GlobalOpenTelemetry`
25+
- Automatically set span factory based on presence of OpenTelemetry ([#3858](https://github.com/getsentry/sentry-java/pull/3858))
26+
- `SentrySpanFactoryHolder` has been removed as it is no longer required.
2527
- Add a sample for showcasing Sentry with OpenTelemetry for Spring Boot 3 with our Java agent (`sentry-samples-spring-boot-jakarta-opentelemetry`) ([#3856](https://github.com/getsentry/sentry-java/pull/3828))
2628
- Add a sample for showcasing Sentry with OpenTelemetry for Spring Boot 3 without our Java agent (`sentry-samples-spring-boot-jakarta-opentelemetry-noagent`) ([#3856](https://github.com/getsentry/sentry-java/pull/3856))
2729
- Add `globalHubMode` to options ([#3805](https://github.com/getsentry/sentry-java/pull/3805))

sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import io.sentry.Sentry;
1010
import io.sentry.SentryIntegrationPackageStorage;
1111
import io.sentry.SentryOptions;
12-
import io.sentry.SentrySpanFactoryHolder;
1312
import io.sentry.protocol.SdkVersion;
1413
import io.sentry.protocol.SentryPackage;
1514
import java.io.IOException;
@@ -34,9 +33,6 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
3433
ensureSentryOtelStorageIsInitialized();
3534
final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo();
3635

37-
final @NotNull OtelSpanFactory spanFactory = new OtelSpanFactory();
38-
SentrySpanFactoryHolder.setSpanFactory(spanFactory);
39-
4036
if (isSentryAutoInitEnabled()) {
4137
Sentry.init(
4238
options -> {

sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.sentry.opentelemetry;
22

33
import io.sentry.SentryOptions;
4-
import io.sentry.SentrySpanFactoryHolder;
54
import io.sentry.util.SpanUtils;
65
import org.jetbrains.annotations.ApiStatus;
76
import org.jetbrains.annotations.Nullable;
@@ -12,7 +11,6 @@ public final class OpenTelemetryUtil {
1211
public static void applyOpenTelemetryOptions(
1312
final @Nullable SentryOptions options, final boolean isAgent) {
1413
if (options != null) {
15-
options.setSpanFactory(SentrySpanFactoryHolder.getSpanFactory());
1614
options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry(isAgent));
1715
}
1816
}

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package io.sentry.spring.boot.jakarta.it
22

3+
import io.sentry.DefaultSpanFactory
34
import io.sentry.IScopes
45
import io.sentry.ITransportFactory
56
import io.sentry.Sentry
7+
import io.sentry.SentryOptions
68
import io.sentry.checkEvent
79
import io.sentry.checkTransaction
810
import io.sentry.spring.jakarta.tracing.SentrySpan
@@ -223,6 +225,12 @@ open class App {
223225

224226
@Bean
225227
open fun mockTransport() = transport
228+
229+
@Bean
230+
open fun optionsCallback() = Sentry.OptionsConfiguration<SentryOptions> { options ->
231+
// due to OTel being on the classpath we need to set the default again
232+
options.spanFactory = DefaultSpanFactory()
233+
}
226234
}
227235

228236
@RestController

sentry-spring-jakarta/api/sentry-spring-jakarta.api

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ public final class io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHand
204204

205205
public class io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryAgentWithoutAutoInitConfiguration {
206206
public fun <init> ()V
207-
public static fun openTelemetrySpanFactory ()Lio/sentry/ISpanFactory;
208207
public fun sentryOpenTelemetryOptionsConfiguration ()Lio/sentry/Sentry$OptionsConfiguration;
209208
}
210209

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryAgentWithoutAutoInitConfiguration.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package io.sentry.spring.jakarta.opentelemetry;
22

33
import com.jakewharton.nopen.annotation.Open;
4-
import io.sentry.ISpanFactory;
54
import io.sentry.Sentry;
65
import io.sentry.SentryIntegrationPackageStorage;
76
import io.sentry.SentryOptions;
87
import io.sentry.opentelemetry.OpenTelemetryUtil;
9-
import io.sentry.opentelemetry.OtelSpanFactory;
108
import org.jetbrains.annotations.NotNull;
119
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1210
import org.springframework.context.annotation.Bean;
@@ -16,12 +14,6 @@
1614
@Open
1715
public class SentryOpenTelemetryAgentWithoutAutoInitConfiguration {
1816

19-
@Bean
20-
@ConditionalOnMissingBean
21-
public static ISpanFactory openTelemetrySpanFactory() {
22-
return new OtelSpanFactory();
23-
}
24-
2517
@Bean
2618
@ConditionalOnMissingBean(name = "sentryOpenTelemetryOptionsConfiguration")
2719
public @NotNull Sentry.OptionsConfiguration<SentryOptions>

sentry/api/sentry.api

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3175,12 +3175,6 @@ public final class io/sentry/SentryReplayOptions$SentryReplayQuality : java/lang
31753175
public static fun values ()[Lio/sentry/SentryReplayOptions$SentryReplayQuality;
31763176
}
31773177

3178-
public final class io/sentry/SentrySpanFactoryHolder {
3179-
public fun <init> ()V
3180-
public static fun getSpanFactory ()Lio/sentry/ISpanFactory;
3181-
public static fun setSpanFactory (Lio/sentry/ISpanFactory;)V
3182-
}
3183-
31843178
public final class io/sentry/SentrySpanStorage {
31853179
public fun get (Ljava/lang/String;)Lio/sentry/ISpan;
31863180
public static fun getInstance ()Lio/sentry/SentrySpanStorage;
@@ -3486,6 +3480,11 @@ public abstract interface class io/sentry/SpanDataConvention {
34863480
public static final field THREAD_NAME Ljava/lang/String;
34873481
}
34883482

3483+
public final class io/sentry/SpanFactoryFactory {
3484+
public fun <init> ()V
3485+
public static fun create (Lio/sentry/util/LoadClass;Lio/sentry/ILogger;)Lio/sentry/ISpanFactory;
3486+
}
3487+
34893488
public abstract interface class io/sentry/SpanFinishedCallback {
34903489
public abstract fun execute (Lio/sentry/Span;)V
34913490
}

sentry/src/main/java/io/sentry/ScopesStorageFactory.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.sentry;
22

33
import io.sentry.util.LoadClass;
4+
import io.sentry.util.Platform;
45
import java.lang.reflect.InvocationTargetException;
56
import org.jetbrains.annotations.ApiStatus;
67
import org.jetbrains.annotations.NotNull;
@@ -21,23 +22,25 @@ public final class ScopesStorageFactory {
2122

2223
private static @NotNull IScopesStorage createInternal(
2324
final @NotNull LoadClass loadClass, final @NotNull ILogger logger) {
24-
if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) {
25-
Class<?> otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger);
26-
if (otelScopesStorageClazz != null) {
27-
try {
28-
final @Nullable Object otelScopesStorage =
29-
otelScopesStorageClazz.getDeclaredConstructor().newInstance();
30-
if (otelScopesStorage != null && otelScopesStorage instanceof IScopesStorage) {
31-
return (IScopesStorage) otelScopesStorage;
25+
if (Platform.isJvm()) {
26+
if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) {
27+
Class<?> otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger);
28+
if (otelScopesStorageClazz != null) {
29+
try {
30+
final @Nullable Object otelScopesStorage =
31+
otelScopesStorageClazz.getDeclaredConstructor().newInstance();
32+
if (otelScopesStorage != null && otelScopesStorage instanceof IScopesStorage) {
33+
return (IScopesStorage) otelScopesStorage;
34+
}
35+
} catch (InstantiationException e) {
36+
// TODO log
37+
} catch (IllegalAccessException e) {
38+
// TODO log
39+
} catch (InvocationTargetException e) {
40+
// TODO log
41+
} catch (NoSuchMethodException e) {
42+
// TODO log
3243
}
33-
} catch (InstantiationException e) {
34-
// TODO log
35-
} catch (IllegalAccessException e) {
36-
// TODO log
37-
} catch (InvocationTargetException e) {
38-
// TODO log
39-
} catch (NoSuchMethodException e) {
40-
// TODO log
4144
}
4245
}
4346
}

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.sentry.transport.NoOpTransportGate;
2222
import io.sentry.util.AutoClosableReentrantLock;
2323
import io.sentry.util.LazyEvaluator;
24+
import io.sentry.util.LoadClass;
2425
import io.sentry.util.Platform;
2526
import io.sentry.util.SampleRateUtils;
2627
import io.sentry.util.StringUtils;
@@ -2543,7 +2544,7 @@ public SentryOptions() {
25432544
private SentryOptions(final boolean empty) {
25442545
experimental = new ExperimentalOptions(empty);
25452546
if (!empty) {
2546-
setSpanFactory(new DefaultSpanFactory());
2547+
setSpanFactory(SpanFactoryFactory.create(new LoadClass(), NoOpLogger.getInstance()));
25472548
// SentryExecutorService should be initialized before any
25482549
// SendCachedEventFireAndForgetIntegration
25492550
executorService = new SentryExecutorService();

0 commit comments

Comments
 (0)