Skip to content

Commit db75a56

Browse files
authored
Merge 737c147 into ab8a72d
2 parents ab8a72d + 737c147 commit db75a56

File tree

31 files changed

+562
-49
lines changed

31 files changed

+562
-49
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Fixes
6+
7+
- Fix profiling init for Spring and Spring Boot w Agent auto-init ([#4815](https://github.com/getsentry/sentry-java/pull/4815))
8+
59
### Improvements
610

711
- Fallback to distinct-id as user.id logging attribute when user is not set ([#4847](https://github.com/getsentry/sentry-java/pull/4847))

sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/provider/AsyncProfilerProfileConverterProvider.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import io.sentry.profiling.JavaProfileConverterProvider;
66
import org.jetbrains.annotations.ApiStatus;
77
import org.jetbrains.annotations.NotNull;
8-
import org.jetbrains.annotations.Nullable;
98

109
/**
1110
* AsyncProfiler implementation of {@link JavaProfileConverterProvider}. This provider integrates
@@ -15,7 +14,7 @@
1514
public final class AsyncProfilerProfileConverterProvider implements JavaProfileConverterProvider {
1615

1716
@Override
18-
public @Nullable IProfileConverter getProfileConverter() {
17+
public @NotNull IProfileConverter getProfileConverter() {
1918
return new AsyncProfilerProfileConverter();
2019
}
2120

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package io.sentry.asyncprofiler.init
2+
3+
import io.sentry.ILogger
4+
import io.sentry.ISentryExecutorService
5+
import io.sentry.NoOpContinuousProfiler
6+
import io.sentry.NoOpProfileConverter
7+
import io.sentry.SentryOptions
8+
import io.sentry.asyncprofiler.profiling.JavaContinuousProfiler
9+
import io.sentry.asyncprofiler.provider.AsyncProfilerProfileConverterProvider
10+
import io.sentry.util.InitUtil
11+
import kotlin.test.Test
12+
import org.mockito.kotlin.mock
13+
14+
class AsyncProfilerInitUtilTest {
15+
16+
@Test
17+
fun `initialize Profiler returns no-op profiler if profiling disabled`() {
18+
val options = SentryOptions()
19+
val profiler = InitUtil.initializeProfiler(options)
20+
assert(profiler is NoOpContinuousProfiler)
21+
}
22+
23+
@Test
24+
fun `initialize Converter returns no-op profiler if profiling disabled`() {
25+
val options = SentryOptions()
26+
val converter = InitUtil.initializeProfileConverter(options)
27+
assert(converter is NoOpProfileConverter)
28+
}
29+
30+
@Test
31+
fun `initialize Profiler returns no-op profiler if profiler already initialized`() {
32+
val options =
33+
SentryOptions().also {
34+
it.setProfileSessionSampleRate(1.0)
35+
it.tracesSampleRate = 1.0
36+
it.setContinuousProfiler(
37+
JavaContinuousProfiler(mock<ILogger>(), "", 10, mock<ISentryExecutorService>())
38+
)
39+
}
40+
41+
val profiler = InitUtil.initializeProfiler(options)
42+
assert(profiler is NoOpContinuousProfiler)
43+
}
44+
45+
@Test
46+
fun `initialize converter returns no-op converter if converter already initialized`() {
47+
val options =
48+
SentryOptions().also {
49+
it.setProfileSessionSampleRate(1.0)
50+
it.tracesSampleRate = 1.0
51+
it.profilerConverter = AsyncProfilerProfileConverterProvider.AsyncProfilerProfileConverter()
52+
}
53+
54+
val converter = InitUtil.initializeProfileConverter(options)
55+
assert(converter is NoOpProfileConverter)
56+
}
57+
58+
@Test
59+
fun `initialize Profiler returns JavaContinuousProfiler if profiling enabled but profiler not yet initialized`() {
60+
val options =
61+
SentryOptions().also {
62+
it.setProfileSessionSampleRate(1.0)
63+
it.tracesSampleRate = 1.0
64+
}
65+
val profiler = InitUtil.initializeProfiler(options)
66+
assert(profiler is JavaContinuousProfiler)
67+
}
68+
69+
@Test
70+
fun `initialize Profiler returns AsyncProfilerProfileConverterProvider if profiling enabled but profiler not yet initialized`() {
71+
val options =
72+
SentryOptions().also {
73+
it.setProfileSessionSampleRate(1.0)
74+
it.tracesSampleRate = 1.0
75+
}
76+
val converter = InitUtil.initializeProfileConverter(options)
77+
assert(converter is AsyncProfilerProfileConverterProvider.AsyncProfilerProfileConverter)
78+
}
79+
}

sentry-spring-7/api/sentry-spring-7.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ public class io/sentry/spring7/SentryInitBeanPostProcessor : org/springframework
4242
public fun setApplicationContext (Lorg/springframework/context/ApplicationContext;)V
4343
}
4444

45+
public class io/sentry/spring7/SentryProfilerConfiguration {
46+
public fun <init> ()V
47+
public fun sentryOpenTelemetryProfilerConfiguration ()Lio/sentry/IContinuousProfiler;
48+
public fun sentryOpenTelemetryProfilerConverterConfiguration ()Lio/sentry/IProfileConverter;
49+
}
50+
4551
public class io/sentry/spring7/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor {
4652
public fun <init> (Lio/sentry/spring7/tracing/TransactionNameProvider;Ljakarta/servlet/http/HttpServletRequest;)V
4753
public fun getOrder ()Ljava/lang/Long;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io.sentry.spring7;
2+
3+
import com.jakewharton.nopen.annotation.Open;
4+
import io.sentry.IContinuousProfiler;
5+
import io.sentry.IProfileConverter;
6+
import io.sentry.NoOpContinuousProfiler;
7+
import io.sentry.NoOpProfileConverter;
8+
import io.sentry.Sentry;
9+
import io.sentry.SentryOptions;
10+
import io.sentry.util.InitUtil;
11+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.context.annotation.Configuration;
14+
15+
/**
16+
* Handles late initialization of the profiler if the application is run with the OTEL Agent in
17+
* auto-init mode. In that case the agent cannot initialize the profiler yet and falls back to No-Op
18+
* implementations. This Configuration sets the profiler and converter on the options if that was
19+
* the case.
20+
*/
21+
@Configuration(proxyBeanMethods = false)
22+
@Open
23+
public class SentryProfilerConfiguration {
24+
25+
@Bean
26+
@ConditionalOnMissingBean(name = "sentryOpenTelemetryProfilerConfiguration")
27+
public IContinuousProfiler sentryOpenTelemetryProfilerConfiguration() {
28+
SentryOptions options = Sentry.getGlobalScope().getOptions();
29+
IContinuousProfiler profiler = NoOpContinuousProfiler.getInstance();
30+
31+
if (Sentry.isEnabled()) {
32+
return InitUtil.initializeProfiler(options);
33+
} else {
34+
return profiler;
35+
}
36+
}
37+
38+
@Bean
39+
@ConditionalOnMissingBean(name = "sentryOpenTelemetryProfilerConverterConfiguration")
40+
public IProfileConverter sentryOpenTelemetryProfilerConverterConfiguration() {
41+
SentryOptions options = Sentry.getGlobalScope().getOptions();
42+
IProfileConverter converter = NoOpProfileConverter.getInstance();
43+
44+
if (Sentry.isEnabled()) {
45+
return InitUtil.initializeProfileConverter(options);
46+
} else {
47+
return converter;
48+
}
49+
}
50+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public class io/sentry/spring/boot4/SentryLogbackInitializer : org/springframewo
2424
public fun supportsEventType (Lorg/springframework/core/ResolvableType;)Z
2525
}
2626

27+
public class io/sentry/spring/boot4/SentryProfilerAutoConfiguration {
28+
public fun <init> ()V
29+
}
30+
2731
public class io/sentry/spring/boot4/SentryProperties : io/sentry/SentryOptions {
2832
public fun <init> ()V
2933
public fun getExceptionResolverOrder ()I
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.sentry.spring.boot4;
2+
3+
import com.jakewharton.nopen.annotation.Open;
4+
import io.sentry.spring7.SentryProfilerConfiguration;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Import;
8+
9+
@Configuration(proxyBeanMethods = false)
10+
@ConditionalOnClass(name = {"io.sentry.opentelemetry.agent.AgentMarker"})
11+
@Open
12+
@Import(SentryProfilerConfiguration.class)
13+
public class SentryProfilerAutoConfiguration {}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
io.sentry.spring.boot4.SentryAutoConfiguration
2+
io.sentry.spring.boot4.SentryProfilerAutoConfiguration
23
io.sentry.spring.boot4.SentryLogbackAppenderAutoConfiguration
34
io.sentry.spring.boot4.SentryWebfluxAutoConfiguration

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import io.sentry.Breadcrumb
77
import io.sentry.EventProcessor
88
import io.sentry.FilterString
99
import io.sentry.Hint
10+
import io.sentry.IContinuousProfiler
11+
import io.sentry.IProfileConverter
1012
import io.sentry.IScopes
1113
import io.sentry.ITransportFactory
1214
import io.sentry.Integration
@@ -87,6 +89,7 @@ class SentryAutoConfigurationTest {
8789
AutoConfigurations.of(
8890
SentryAutoConfiguration::class.java,
8991
WebMvcAutoConfiguration::class.java,
92+
SentryProfilerAutoConfiguration::class.java,
9093
)
9194
)
9295

@@ -1037,6 +1040,39 @@ class SentryAutoConfigurationTest {
10371040
}
10381041
}
10391042

1043+
@Test
1044+
fun `when AgentMarker is on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are created and set on options`() {
1045+
SentryIntegrationPackageStorage.getInstance().clearStorage()
1046+
contextRunner
1047+
.withPropertyValues(
1048+
"sentry.dsn=http://key@localhost/proj",
1049+
"sentry.traces-sample-rate=1.0",
1050+
"sentry.auto-init=false",
1051+
"debug=true",
1052+
)
1053+
.run {
1054+
assertThat(it).hasSingleBean(IContinuousProfiler::class.java)
1055+
assertThat(it).hasSingleBean(IProfileConverter::class.java)
1056+
}
1057+
}
1058+
1059+
@Test
1060+
fun `when AgentMarker is not on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are not created`() {
1061+
SentryIntegrationPackageStorage.getInstance().clearStorage()
1062+
contextRunner
1063+
.withPropertyValues(
1064+
"sentry.dsn=http://key@localhost/proj",
1065+
"sentry.traces-sample-rate=1.0",
1066+
"sentry.profile-session-sample-rate=1.0",
1067+
"debug=true",
1068+
)
1069+
.withClassLoader(FilteredClassLoader(AgentMarker::class.java, OpenTelemetry::class.java))
1070+
.run {
1071+
assertThat(it).doesNotHaveBean(IContinuousProfiler::class.java)
1072+
assertThat(it).doesNotHaveBean(IProfileConverter::class.java)
1073+
}
1074+
}
1075+
10401076
@Configuration(proxyBeanMethods = false)
10411077
open class CustomSchedulerFactoryBeanCustomizerConfiguration {
10421078
class MyJobListener : JobListener {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public class io/sentry/spring/boot/jakarta/SentryLogbackInitializer : org/spring
2424
public fun supportsEventType (Lorg/springframework/core/ResolvableType;)Z
2525
}
2626

27+
public class io/sentry/spring/boot/jakarta/SentryProfilerAutoConfiguration {
28+
public fun <init> ()V
29+
}
30+
2731
public class io/sentry/spring/boot/jakarta/SentryProperties : io/sentry/SentryOptions {
2832
public fun <init> ()V
2933
public fun getExceptionResolverOrder ()I

0 commit comments

Comments
 (0)