Skip to content

Commit acb0d74

Browse files
committed
only auto-configure okhttp when otel is absent, add flag to enable okhttp autoconfig
1 parent 679eaaa commit acb0d74

4 files changed

Lines changed: 132 additions & 7 deletions

File tree

‎sentry-spring-boot-4/api/sentry-spring-boot-4.api‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class io/sentry/spring/boot4/SentryProfilerAutoConfiguration {
3434

3535
public class io/sentry/spring/boot4/SentryProperties : io/sentry/SentryOptions {
3636
public fun <init> ()V
37+
public fun getClients ()Lio/sentry/spring/boot4/SentryProperties$Clients;
3738
public fun getExceptionResolverOrder ()I
3839
public fun getGraphql ()Lio/sentry/spring/boot4/SentryProperties$Graphql;
3940
public fun getLogging ()Lio/sentry/spring/boot4/SentryProperties$Logging;
@@ -42,6 +43,7 @@ public class io/sentry/spring/boot4/SentryProperties : io/sentry/SentryOptions {
4243
public fun isEnableAotCompatibility ()Z
4344
public fun isKeepTransactionsOpenForAsyncResponses ()Z
4445
public fun isUseGitCommitIdAsRelease ()Z
46+
public fun setClients (Lio/sentry/spring/boot4/SentryProperties$Clients;)V
4547
public fun setEnableAotCompatibility (Z)V
4648
public fun setExceptionResolverOrder (I)V
4749
public fun setGraphql (Lio/sentry/spring/boot4/SentryProperties$Graphql;)V
@@ -52,6 +54,12 @@ public class io/sentry/spring/boot4/SentryProperties : io/sentry/SentryOptions {
5254
public fun setUserFilterOrder (Ljava/lang/Integer;)V
5355
}
5456

57+
public class io/sentry/spring/boot4/SentryProperties$Clients {
58+
public fun <init> ()V
59+
public fun isOkHttpEnabled ()Z
60+
public fun setOkHttpEnabled (Z)V
61+
}
62+
5563
public class io/sentry/spring/boot4/SentryProperties$Graphql {
5664
public fun <init> ()V
5765
public fun getIgnoredErrorTypes ()Ljava/util/List;

‎sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryOkHttpAutoConfiguration.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import okhttp3.OkHttpClient;
77
import org.jetbrains.annotations.NotNull;
88
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
9+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
910
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
1011
import org.springframework.context.annotation.Bean;
1112
import org.springframework.context.annotation.Configuration;
@@ -18,10 +19,15 @@
1819
SentryOkHttpInterceptor.class,
1920
SentryOkHttpEventListener.class
2021
})
22+
@ConditionalOnMissingClass({
23+
"io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider",
24+
"io.sentry.opentelemetry.agent.AgentMarker"
25+
})
2126
@ConditionalOnProperty(name = "sentry.dsn")
2227
public class SentryOkHttpAutoConfiguration {
2328

2429
@Bean
30+
@ConditionalOnProperty(name = "sentry.clients.ok-http-enabled", havingValue = "true")
2531
static @NotNull SentryOkHttpClientBeanPostProcessor sentryOkHttpClientBeanPostProcessor() {
2632
return new SentryOkHttpClientBeanPostProcessor();
2733
}

‎sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryProperties.java‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public class SentryProperties extends SentryOptions {
4747
/** Graphql integration properties. */
4848
private @NotNull Graphql graphql = new Graphql();
4949

50+
/** Clients integration properties. */
51+
private @NotNull Clients clients = new Clients();
52+
5053
public boolean isUseGitCommitIdAsRelease() {
5154
return useGitCommitIdAsRelease;
5255
}
@@ -124,6 +127,14 @@ public void setGraphql(@NotNull Graphql graphql) {
124127
this.graphql = graphql;
125128
}
126129

130+
public @NotNull Clients getClients() {
131+
return clients;
132+
}
133+
134+
public void setClients(@NotNull Clients clients) {
135+
this.clients = clients;
136+
}
137+
127138
@Open
128139
public static class Logging {
129140
/** Enable/Disable logging auto-configuration. */
@@ -215,4 +226,18 @@ public void setIgnoredErrorTypes(final @NotNull List<String> ignoredErrorTypes)
215226
this.ignoredErrorTypes = ignoredErrorTypes;
216227
}
217228
}
229+
230+
@Open
231+
public static class Clients {
232+
/** Enable automatic instrumentation of Spring-managed OkHttp clients. Disabled by default. */
233+
private boolean okHttpEnabled = false;
234+
235+
public boolean isOkHttpEnabled() {
236+
return okHttpEnabled;
237+
}
238+
239+
public void setOkHttpEnabled(boolean okHttpEnabled) {
240+
this.okHttpEnabled = okHttpEnabled;
241+
}
242+
}
218243
}

‎sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryOkHttpAutoConfigurationTest.kt‎

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.sentry.spring.boot4
22

33
import com.google.common.truth.Truth.assertThat
4+
import io.opentelemetry.api.OpenTelemetry
45
import io.sentry.ITransportFactory
56
import io.sentry.NoOpTransportFactory
67
import io.sentry.okhttp.SentryOkHttpEventListener
78
import io.sentry.okhttp.SentryOkHttpInterceptor
9+
import io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider
10+
import io.sentry.opentelemetry.agent.AgentMarker
811
import java.util.concurrent.TimeUnit
912
import java.util.concurrent.atomic.AtomicBoolean
1013
import kotlin.test.Test
@@ -23,7 +26,7 @@ import org.springframework.context.annotation.Configuration
2326

2427
class SentryOkHttpAutoConfigurationTest {
2528

26-
private val contextRunner =
29+
private val baseContextRunner =
2730
ApplicationContextRunner()
2831
.withUserConfiguration(TestApplication::class.java, NoOpTransportConfiguration::class.java)
2932
.withPropertyValues(
@@ -35,15 +38,54 @@ class SentryOkHttpAutoConfigurationTest {
3538
"sentry.enable-spotlight=false",
3639
)
3740

41+
private val contextRunner =
42+
baseContextRunner.withPropertyValues("sentry.clients.ok-http-enabled=true")
43+
44+
private val noOtelClassLoader =
45+
FilteredClassLoader(
46+
SentryAutoConfigurationCustomizerProvider::class.java,
47+
AgentMarker::class.java,
48+
)
49+
50+
private val noOtelContextRunner = contextRunner.withClassLoader(noOtelClassLoader)
51+
52+
@Test
53+
fun `does not modify clients unless explicitly enabled`() {
54+
for (properties in
55+
listOf(emptyArray<String>(), arrayOf("sentry.clients.ok-http-enabled=false"))) {
56+
val plainClient = OkHttpClient()
57+
val manualClient =
58+
OkHttpClient.Builder()
59+
.addInterceptor(SentryOkHttpInterceptor())
60+
.eventListener(SentryOkHttpEventListener())
61+
.build()
62+
63+
baseContextRunner
64+
.withClassLoader(noOtelClassLoader)
65+
.withPropertyValues("sentry.dsn=http://key@localhost/proj", *properties)
66+
.withBean("plainClient", OkHttpClient::class.java, { plainClient })
67+
.withBean("manualClient", OkHttpClient::class.java, { manualClient })
68+
.run { context ->
69+
assertThat(context.getBean("plainClient")).isSameInstanceAs(plainClient)
70+
assertThat(context.getBean("manualClient")).isSameInstanceAs(manualClient)
71+
assertThat(context.getBeansOfType(SentryOkHttpClientBeanPostProcessor::class.java))
72+
.isEmpty()
73+
assertThat(context.getBean(SentryProperties::class.java).clients.isOkHttpEnabled)
74+
.isFalse()
75+
}
76+
}
77+
}
78+
3879
@Test
3980
fun `instruments a Spring managed OkHttpClient`() {
40-
contextRunner
81+
noOtelContextRunner
4182
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
4283
.withUserConfiguration(OkHttpClientConfiguration::class.java)
4384
.run { context ->
4485
val client = context.getBean(OkHttpClient::class.java)
4586
val existingInterceptor = context.getBean("existingInterceptor", Interceptor::class.java)
4687

88+
assertThat(context.getBean(SentryProperties::class.java).clients.isOkHttpEnabled).isTrue()
4789
assertThat(client.connectTimeoutMillis).isEqualTo(1234)
4890
assertThat(client.interceptors).contains(existingInterceptor)
4991
assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor>()).hasSize(1)
@@ -62,7 +104,7 @@ class SentryOkHttpAutoConfigurationTest {
62104

63105
@Test
64106
fun `instruments every Spring managed OkHttpClient`() {
65-
contextRunner
107+
noOtelContextRunner
66108
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
67109
.withUserConfiguration(MultipleOkHttpClientsConfiguration::class.java)
68110
.run { context ->
@@ -78,7 +120,7 @@ class SentryOkHttpAutoConfigurationTest {
78120

79121
@Test
80122
fun `does not duplicate an existing Sentry interceptor`() {
81-
contextRunner
123+
noOtelContextRunner
82124
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
83125
.withUserConfiguration(ManuallyInstrumentedOkHttpClientConfiguration::class.java)
84126
.run { context ->
@@ -120,9 +162,42 @@ class SentryOkHttpAutoConfigurationTest {
120162
assertThat(client.interceptors).isEmpty()
121163
}
122164

165+
@Test
166+
fun `does not instrument when OpenTelemetry agent is present`() {
167+
contextRunner
168+
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
169+
.withUserConfiguration(OkHttpClientConfiguration::class.java)
170+
.run { context ->
171+
val client = context.getBean(OkHttpClient::class.java)
172+
173+
assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor>()).isEmpty()
174+
assertThat(client.eventListenerFactory.create(mock()))
175+
.isNotInstanceOf(SentryOkHttpEventListener::class.java)
176+
}
177+
}
178+
179+
@Test
180+
fun `does not instrument when Sentry OpenTelemetry integration is present`() {
181+
contextRunner
182+
.withClassLoader(FilteredClassLoader(AgentMarker::class.java))
183+
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
184+
.withUserConfiguration(
185+
OkHttpClientConfiguration::class.java,
186+
OpenTelemetryConfiguration::class.java,
187+
)
188+
.run { context ->
189+
val client = context.getBean(OkHttpClient::class.java)
190+
191+
assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor>()).isEmpty()
192+
assertThat(client.eventListenerFactory.create(mock()))
193+
.isNotInstanceOf(SentryOkHttpEventListener::class.java)
194+
}
195+
}
196+
123197
@Test
124198
fun `does not instrument OkHttpClient without a dsn`() {
125-
contextRunner.withUserConfiguration(OkHttpClientConfiguration::class.java).run { context ->
199+
noOtelContextRunner.withUserConfiguration(OkHttpClientConfiguration::class.java).run { context
200+
->
126201
val client = context.getBean(OkHttpClient::class.java)
127202

128203
assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor>()).isEmpty()
@@ -133,15 +208,21 @@ class SentryOkHttpAutoConfigurationTest {
133208

134209
@Test
135210
fun `does not create a default OkHttpClient`() {
136-
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj").run { context ->
211+
noOtelContextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj").run { context ->
137212
assertThat(context.getBeansOfType(OkHttpClient::class.java)).isEmpty()
138213
}
139214
}
140215

141216
@Test
142217
fun `does not instrument when sentry-okhttp is not on the classpath`() {
143218
contextRunner
144-
.withClassLoader(FilteredClassLoader(SentryOkHttpInterceptor::class.java))
219+
.withClassLoader(
220+
FilteredClassLoader(
221+
SentryOkHttpInterceptor::class.java,
222+
SentryAutoConfigurationCustomizerProvider::class.java,
223+
AgentMarker::class.java,
224+
)
225+
)
145226
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
146227
.withUserConfiguration(OkHttpClientConfiguration::class.java)
147228
.run { context ->
@@ -154,6 +235,11 @@ class SentryOkHttpAutoConfigurationTest {
154235

155236
@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration open class TestApplication
156237

238+
@Configuration(proxyBeanMethods = false)
239+
open class OpenTelemetryConfiguration {
240+
@Bean open fun openTelemetry(): OpenTelemetry = OpenTelemetry.noop()
241+
}
242+
157243
@Configuration(proxyBeanMethods = false)
158244
open class NoOpTransportConfiguration {
159245
@Bean open fun noOpTransportFactory(): ITransportFactory = NoOpTransportFactory.getInstance()

0 commit comments

Comments
 (0)