Skip to content
Draft
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 @@ -5,7 +5,6 @@
import io.sentry.profiling.JavaProfileConverterProvider;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

@Override
public @Nullable IProfileConverter getProfileConverter() {
public @NotNull IProfileConverter getProfileConverter() {
return new AsyncProfilerProfileConverter();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import io.sentry.ILogger
import io.sentry.ISentryExecutorService
import io.sentry.NoOpContinuousProfiler
import io.sentry.NoOpProfileConverter
import io.sentry.SentryOptions
import io.sentry.asyncprofiler.profiling.JavaContinuousProfiler
import io.sentry.asyncprofiler.provider.AsyncProfilerProfileConverterProvider
import io.sentry.util.InitUtil
import kotlin.test.Test
import org.mockito.kotlin.mock

class AsyncProfilerInitUtilTest {

@Test
fun `initialize Profiler returns no-op profiler if profiling disabled`() {
val options = SentryOptions()
val profiler = InitUtil.initializeProfiler(options)
assert(profiler is NoOpContinuousProfiler)
}

@Test
fun `initialize Converter returns no-op profiler if profiling disabled`() {
val options = SentryOptions()
val converter = InitUtil.initializeProfileConverter(options)
assert(converter is NoOpProfileConverter)
}

@Test
fun `initialize Profiler returns no-op profiler if profiler already initialized`() {
val options =
SentryOptions().also {
it.setProfileSessionSampleRate(1.0)
it.tracesSampleRate = 1.0
it.setContinuousProfiler(
JavaContinuousProfiler(mock<ILogger>(), "", 10, mock<ISentryExecutorService>())
)
}

val profiler = InitUtil.initializeProfiler(options)
assert(profiler is NoOpContinuousProfiler)
}

@Test
fun `initialize converter returns no-op converter if converter already initialized`() {
val options =
SentryOptions().also {
it.setProfileSessionSampleRate(1.0)
it.tracesSampleRate = 1.0
it.profilerConverter = AsyncProfilerProfileConverterProvider.AsyncProfilerProfileConverter()
}

val converter = InitUtil.initializeProfileConverter(options)
assert(converter is NoOpProfileConverter)
}

@Test
fun `initialize Profiler returns JavaContinuousProfiler if profiling enabled but profiler not yet initialized`() {
val options =
SentryOptions().also {
it.setProfileSessionSampleRate(1.0)
it.tracesSampleRate = 1.0
}
val profiler = InitUtil.initializeProfiler(options)
assert(profiler is JavaContinuousProfiler)
}

@Test
fun `initialize Profiler returns AsyncProfilerProfileConverterProvider if profiling enabled but profiler not yet initialized`() {
val options =
SentryOptions().also {
it.setProfileSessionSampleRate(1.0)
it.tracesSampleRate = 1.0
}
val converter = InitUtil.initializeProfileConverter(options)
assert(converter is AsyncProfilerProfileConverterProvider.AsyncProfilerProfileConverter)
}
}
6 changes: 6 additions & 0 deletions sentry-spring-7/api/sentry-spring-7.api
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class io/sentry/spring7/SentryInitBeanPostProcessor : org/springframework
public fun setApplicationContext (Lorg/springframework/context/ApplicationContext;)V
}

public class io/sentry/spring7/SentryProfilerConfiguration {
public fun <init> ()V
public fun sentryOpenTelemetryProfilerConfiguration ()Lio/sentry/IContinuousProfiler;
public fun sentryOpenTelemetryProfilerConverterConfiguration ()Lio/sentry/IProfileConverter;
}

public class io/sentry/spring7/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor {
public fun <init> (Lio/sentry/spring7/tracing/TransactionNameProvider;Ljakarta/servlet/http/HttpServletRequest;)V
public fun getOrder ()Ljava/lang/Long;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.sentry.spring7;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.IContinuousProfiler;
import io.sentry.IProfileConverter;
import io.sentry.NoOpContinuousProfiler;
import io.sentry.NoOpProfileConverter;
import io.sentry.Sentry;
import io.sentry.SentryOptions;
import io.sentry.util.InitUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Handles late initialization of the profiler if the application is run with the OTEL Agent in
* auto-init mode. In that case the agent cannot initialize the profiler yet and falls back to No-Op
* implementations. This Configuration sets the profiler and converter on the options if that was
* the case.
*/
@Configuration(proxyBeanMethods = false)
@Open
public class SentryProfilerConfiguration {

@Bean
@ConditionalOnMissingBean(name = "sentryOpenTelemetryProfilerConfiguration")
public IContinuousProfiler sentryOpenTelemetryProfilerConfiguration() {
SentryOptions options = Sentry.getGlobalScope().getOptions();
IContinuousProfiler profiler = NoOpContinuousProfiler.getInstance();

if (Sentry.isEnabled()) {
return InitUtil.initializeProfiler(options);
} else {
return profiler;
}
}

@Bean
@ConditionalOnMissingBean(name = "sentryOpenTelemetryProfilerConverterConfiguration")
public IProfileConverter sentryOpenTelemetryProfilerConverterConfiguration() {
SentryOptions options = Sentry.getGlobalScope().getOptions();
IProfileConverter converter = NoOpProfileConverter.getInstance();

if (Sentry.isEnabled()) {
return InitUtil.initializeProfileConverter(options);
} else {
return converter;
}
}
}
4 changes: 4 additions & 0 deletions sentry-spring-boot-4/api/sentry-spring-boot-4.api
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public class io/sentry/spring/boot4/SentryLogbackInitializer : org/springframewo
public fun supportsEventType (Lorg/springframework/core/ResolvableType;)Z
}

public class io/sentry/spring/boot4/SentryProfilerAutoConfiguration {
public fun <init> ()V
}

public class io/sentry/spring/boot4/SentryProperties : io/sentry/SentryOptions {
public fun <init> ()V
public fun getExceptionResolverOrder ()I
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.sentry.spring.boot4;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.spring7.SentryProfilerConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {"io.sentry.opentelemetry.agent.AgentMarker"})
@Open
@Import(SentryProfilerConfiguration.class)
public class SentryProfilerAutoConfiguration {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
io.sentry.spring.boot4.SentryAutoConfiguration
io.sentry.spring.boot4.SentryProfilerAutoConfiguration
io.sentry.spring.boot4.SentryLogbackAppenderAutoConfiguration
io.sentry.spring.boot4.SentryWebfluxAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import io.sentry.Breadcrumb
import io.sentry.EventProcessor
import io.sentry.FilterString
import io.sentry.Hint
import io.sentry.IContinuousProfiler
import io.sentry.IProfileConverter
import io.sentry.IScopes
import io.sentry.ITransportFactory
import io.sentry.Integration
Expand Down Expand Up @@ -87,6 +89,7 @@ class SentryAutoConfigurationTest {
AutoConfigurations.of(
SentryAutoConfiguration::class.java,
WebMvcAutoConfiguration::class.java,
SentryProfilerAutoConfiguration::class.java,
)
)

Expand Down Expand Up @@ -1037,6 +1040,39 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `when AgentMarker is on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are created and set on options`() {
SentryIntegrationPackageStorage.getInstance().clearStorage()
contextRunner
.withPropertyValues(
"sentry.dsn=http://key@localhost/proj",
"sentry.traces-sample-rate=1.0",
"sentry.auto-init=false",
"debug=true",
)
.run {
assertThat(it).hasSingleBean(IContinuousProfiler::class.java)
assertThat(it).hasSingleBean(IProfileConverter::class.java)
}
}

@Test
fun `when AgentMarker is not on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are not created`() {
SentryIntegrationPackageStorage.getInstance().clearStorage()
contextRunner
.withPropertyValues(
"sentry.dsn=http://key@localhost/proj",
"sentry.traces-sample-rate=1.0",
"sentry.profile-session-sample-rate=1.0",
"debug=true",
)
.withClassLoader(FilteredClassLoader(AgentMarker::class.java, OpenTelemetry::class.java))
.run {
assertThat(it).doesNotHaveBean(IContinuousProfiler::class.java)
assertThat(it).doesNotHaveBean(IProfileConverter::class.java)
}
}

@Configuration(proxyBeanMethods = false)
open class CustomSchedulerFactoryBeanCustomizerConfiguration {
class MyJobListener : JobListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public class io/sentry/spring/boot/jakarta/SentryLogbackInitializer : org/spring
public fun supportsEventType (Lorg/springframework/core/ResolvableType;)Z
}

public class io/sentry/spring/boot/jakarta/SentryProfilerAutoConfiguration {
public fun <init> ()V
}

public class io/sentry/spring/boot/jakarta/SentryProperties : io/sentry/SentryOptions {
public fun <init> ()V
public fun getExceptionResolverOrder ()I
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.sentry.spring.boot.jakarta;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.spring.jakarta.SentryProfilerConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {"io.sentry.opentelemetry.agent.AgentMarker"})
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it conditional on the async profiler class too

@Open
@Import(SentryProfilerConfiguration.class)
public class SentryProfilerAutoConfiguration {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
io.sentry.spring.boot.jakarta.SentryAutoConfiguration
io.sentry.spring.boot.jakarta.SentryProfilerAutoConfiguration
io.sentry.spring.boot.jakarta.SentryLogbackAppenderAutoConfiguration
io.sentry.spring.boot.jakarta.SentryWebfluxAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import io.sentry.DataCategory
import io.sentry.EventProcessor
import io.sentry.FilterString
import io.sentry.Hint
import io.sentry.IContinuousProfiler
import io.sentry.IProfileConverter
import io.sentry.IScopes
import io.sentry.ITransportFactory
import io.sentry.Integration
Expand Down Expand Up @@ -91,6 +93,7 @@ class SentryAutoConfigurationTest {
AutoConfigurations.of(
SentryAutoConfiguration::class.java,
WebMvcAutoConfiguration::class.java,
SentryProfilerAutoConfiguration::class.java,
)
)

Expand Down Expand Up @@ -1059,6 +1062,39 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `when AgentMarker is on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are created and set on options`() {
SentryIntegrationPackageStorage.getInstance().clearStorage()
contextRunner
.withPropertyValues(
"sentry.dsn=http://key@localhost/proj",
"sentry.traces-sample-rate=1.0",
"sentry.auto-init=false",
"debug=true",
)
.run {
assertThat(it).hasSingleBean(IContinuousProfiler::class.java)
assertThat(it).hasSingleBean(IProfileConverter::class.java)
}
}

@Test
fun `when AgentMarker is not on the classpath and ContinuousProfiling is enabled IContinuousProfiler and IProfileConverter beans are not created`() {
SentryIntegrationPackageStorage.getInstance().clearStorage()
contextRunner
.withPropertyValues(
"sentry.dsn=http://key@localhost/proj",
"sentry.traces-sample-rate=1.0",
"sentry.profile-session-sample-rate=1.0",
"debug=true",
)
.withClassLoader(FilteredClassLoader(AgentMarker::class.java, OpenTelemetry::class.java))
.run {
assertThat(it).doesNotHaveBean(IContinuousProfiler::class.java)
assertThat(it).doesNotHaveBean(IProfileConverter::class.java)
}
}

@Configuration(proxyBeanMethods = false)
open class CustomSchedulerFactoryBeanCustomizerConfiguration {
class MyJobListener : JobListener {
Expand Down
4 changes: 4 additions & 0 deletions sentry-spring-boot/api/sentry-spring-boot.api
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public class io/sentry/spring/boot/SentryLogbackInitializer : org/springframewor
public fun supportsEventType (Lorg/springframework/core/ResolvableType;)Z
}

public class io/sentry/spring/boot/SentryProfilerAutoConfiguration {
public fun <init> ()V
}

public class io/sentry/spring/boot/SentryProperties : io/sentry/SentryOptions {
public fun <init> ()V
public fun getExceptionResolverOrder ()I
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.sentry.spring.boot;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.spring.SentryProfilerConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {"io.sentry.opentelemetry.agent.AgentMarker"})
@Open
@Import(SentryProfilerConfiguration.class)
public class SentryProfilerAutoConfiguration {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.sentry.spring.boot.SentryAutoConfiguration,\
io.sentry.spring.boot.SentryProfilerAutoConfiguration,\
io.sentry.spring.boot.SentryLogbackAppenderAutoConfiguration,\
io.sentry.spring.boot.SentryWebfluxAutoConfiguration

Expand Down
Loading
Loading