Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ea62aa1
ref(core): Add MonitorConfigUtils to convert schedules to monitor con…
wedamija Oct 2, 2026
28761ae
fix(core): Only send monitor configs Sentry reads the same way
wedamija Oct 2, 2026
bd47026
ref(core): Add MonitorConfigUtils.fromSpringScheduled
wedamija Oct 2, 2026
fca4216
fix(core): Return no config for stepped single value ranges
wedamija Oct 2, 2026
780c605
fix(core): Read Spring crons per parser and skip fixed delays
wedamija Oct 2, 2026
5d745c7
feat(spring): Send monitor config from @Scheduled with @SentryCheckIn
wedamija Oct 5, 2026
bcd5682
feat(spring): Send @Scheduled monitor config by default
wedamija Oct 5, 2026
6f5ad4e
ref(spring): Shorten upsertMonitorConfig Javadoc
wedamija Oct 5, 2026
d758789
fix(core): Keep Sunday-only day of week ranges
wedamija Oct 6, 2026
d903a79
fix(spring): Only catch runtime exceptions when deriving monitor config
wedamija Oct 6, 2026
5db58a4
ref(spring): Shorten upsertMonitorConfig Javadoc
wedamija Oct 15, 2026
1ca0891
feat(spring-jakarta): Monitor all @Scheduled methods with sentry.moni…
wedamija Oct 17, 2026
358f88a
changelog
wedamija Oct 17, 2026
552de8f
fix(spring-jakarta): Skip reactive @Scheduled methods when monitoring…
wedamija Oct 20, 2026
26054cd
fix(spring): Skip jobs with unsupported schedules when monitoring all…
wedamija Oct 29, 2026
408c89a
Merge remote-tracking branch 'origin/main' into danf/monitor-config-u…
wedamija Oct 29, 2026
4c08ff0
Merge branch 'danf/monitor-config-utils' into danf/spring-checkin-sch…
wedamija Oct 29, 2026
b63d62e
Merge branch 'danf/spring-checkin-scheduled-config' into danf/spring-…
wedamija Oct 29, 2026
1e7aa24
ref(spring): Use pattern-matching instanceof for the @Scheduled time …
wedamija Oct 29, 2026
e22b15e
Merge branch 'danf/spring-checkin-scheduled-config' into danf/spring-…
wedamija Oct 29, 2026
0b9b852
fix(core): Use a 2 minute check-in margin for crons with fixed seconds
wedamija Nov 5, 2026
ae5ae63
Merge branch 'danf/monitor-config-utils' into danf/spring-checkin-sch…
wedamija Nov 5, 2026
129cb73
fix(spring): Default upsertMonitorConfig to false and read @Scheduled…
wedamija Nov 5, 2026
7683e57
Merge branch 'danf/spring-checkin-scheduled-config' into danf/spring-…
wedamija Nov 5, 2026
a496af8
fix(spring): Find @Scheduled tasks to monitor the way Spring does
wedamija Nov 5, 2026
2789b4c
Merge remote-tracking branch 'origin/main' into danf/spring-monitor-s…
wedamija Nov 5, 2026
d9ba607
feat(spring): Warn when two @Scheduled methods share a monitor slug
wedamija Nov 5, 2026
09e7160
ref(spring): Reuse the target class for the slug collision check
wedamija Nov 5, 2026
2a61e06
fix(spring): Skip private and static @Scheduled methods when monitori…
wedamija Nov 5, 2026
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

### Features

- Monitor every Spring `@Scheduled` method with Sentry Crons by setting `sentry.monitor-scheduled-tasks=true`, without adding `@SentryCheckIn` to each method. Sentry creates each monitor from the method's `@Scheduled` schedule. Exclude methods with `sentry.exclude-scheduled-tasks` ([#6275](https://github.com/getsentry/sentry-java/pull/6275))
- Quartz `SentryJobListener` can send a monitor config derived from the trigger with check-ins, so Sentry creates or updates the monitor from code. Set the `sentry-upsert-monitor-config` job or trigger data key to `"true"` to send the schedule ([#6216](https://github.com/getsentry/sentry-java/pull/6216))
- Send a monitor config derived from Spring `@Scheduled` with `@SentryCheckIn` check-ins, so Sentry creates or updates the monitor from code. Set `@SentryCheckIn(upsertMonitorConfig = true)` to send the schedule ([#6215](https://github.com/getsentry/sentry-java/pull/6215))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,23 @@ public class io/sentry/spring/boot/jakarta/SentryProperties : io/sentry/SentryOp
public fun <init> ()V
public fun getClients ()Lio/sentry/spring/boot/jakarta/SentryProperties$Clients;
public fun getExceptionResolverOrder ()I
public fun getExcludeScheduledTasks ()Ljava/util/List;
public fun getGraphql ()Lio/sentry/spring/boot/jakarta/SentryProperties$Graphql;
public fun getLogging ()Lio/sentry/spring/boot/jakarta/SentryProperties$Logging;
public fun getReactive ()Lio/sentry/spring/boot/jakarta/SentryProperties$Reactive;
public fun getUserFilterOrder ()Ljava/lang/Integer;
public fun isEnableAotCompatibility ()Z
public fun isKeepTransactionsOpenForAsyncResponses ()Z
public fun isMonitorScheduledTasks ()Z
public fun isUseGitCommitIdAsRelease ()Z
public fun setClients (Lio/sentry/spring/boot/jakarta/SentryProperties$Clients;)V
public fun setEnableAotCompatibility (Z)V
public fun setExceptionResolverOrder (I)V
public fun setExcludeScheduledTasks (Ljava/util/List;)V
public fun setGraphql (Lio/sentry/spring/boot/jakarta/SentryProperties$Graphql;)V
public fun setKeepTransactionsOpenForAsyncResponses (Z)V
public fun setLogging (Lio/sentry/spring/boot/jakarta/SentryProperties$Logging;)V
public fun setMonitorScheduledTasks (Z)V
public fun setReactive (Lio/sentry/spring/boot/jakarta/SentryProperties$Reactive;)V
public fun setUseGitCommitIdAsRelease (Z)V
public fun setUserFilterOrder (Ljava/lang/Integer;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import io.sentry.spring.jakarta.SpringProfilesEventProcessor;
import io.sentry.spring.jakarta.SpringSecuritySentryUserProvider;
import io.sentry.spring.jakarta.cache.SentryCacheBeanPostProcessor;
import io.sentry.spring.jakarta.checkin.SentryCheckInAdvice;
import io.sentry.spring.jakarta.checkin.SentryCheckInAdviceConfiguration;
import io.sentry.spring.jakarta.checkin.SentryCheckInPointcutConfiguration;
import io.sentry.spring.jakarta.checkin.SentryQuartzConfiguration;
import io.sentry.spring.jakarta.checkin.SentryScheduledCheckInPointcut;
import io.sentry.spring.jakarta.exception.SentryCaptureExceptionParameterPointcutConfiguration;
import io.sentry.spring.jakarta.exception.SentryExceptionParameterAdviceConfiguration;
import io.sentry.spring.jakarta.kafka.SentryKafkaConsumerBeanPostProcessor;
Expand All @@ -49,11 +51,16 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.aopalliance.aop.Advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.jetbrains.annotations.NotNull;
import org.quartz.core.QuartzScheduler;
import org.slf4j.MDC;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
Expand All @@ -73,6 +80,7 @@
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
Expand Down Expand Up @@ -309,6 +317,27 @@ static class SentryCheckInAspectsConfiguration {
@Import(SentryCheckInPointcutConfiguration.class)
@Open
static class SentryCheckInPointcutAutoConfiguration {}

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "sentry.monitor-scheduled-tasks", havingValue = "true")
@Open
static class SentryScheduledCheckInConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public @NotNull Advice sentryScheduledCheckInAdvice() {
return new SentryCheckInAdvice(ScopesAdapter.getInstance(), true);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public @NotNull Advisor sentryScheduledCheckInAdvisor(
final @NotNull @Qualifier("sentryScheduledCheckInAdvice") Advice advice,
final @NotNull SentryProperties sentryProperties) {
return new DefaultPointcutAdvisor(
new SentryScheduledCheckInPointcut(sentryProperties.getExcludeScheduledTasks()),
advice);
}
}
}

/** Registers beans specific to Spring MVC. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ public class SentryProperties extends SentryOptions {
/** Clients integration properties. */
private @NotNull Clients clients = new Clients();

/** Monitor every {@code @Scheduled} method with Sentry Crons. */
private boolean monitorScheduledTasks = false;

/**
* {@code @Scheduled} methods not to monitor when {@code monitorScheduledTasks} is on, as {@code
* ClassName.methodName} or a regex matching it.
*/
private @NotNull List<String> excludeScheduledTasks = new ArrayList<>();

public boolean isUseGitCommitIdAsRelease() {
return useGitCommitIdAsRelease;
}
Expand Down Expand Up @@ -135,6 +144,22 @@ public void setClients(@NotNull Clients clients) {
this.clients = clients;
}

public boolean isMonitorScheduledTasks() {
return monitorScheduledTasks;
}

public void setMonitorScheduledTasks(boolean monitorScheduledTasks) {
this.monitorScheduledTasks = monitorScheduledTasks;
}

public @NotNull List<String> getExcludeScheduledTasks() {
return excludeScheduledTasks;
}

public void setExcludeScheduledTasks(@NotNull List<String> excludeScheduledTasks) {
this.excludeScheduledTasks = excludeScheduledTasks;
}

@Open
public static class Clients {
/** Enable automatic instrumentation of Spring-managed OkHttp clients. Disabled by default. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.sentry.NoOpTransportFactory
import io.sentry.ProfileLifecycle
import io.sentry.SamplingContext
import io.sentry.Sentry
import io.sentry.SentryEnvelope
import io.sentry.SentryEvent
import io.sentry.SentryIntegrationPackageStorage
import io.sentry.SentryLevel
Expand Down Expand Up @@ -51,16 +52,20 @@ import io.sentry.transport.apache.ApacheHttpClientTransportFactory
import jakarta.servlet.Filter
import java.io.File
import java.lang.RuntimeException
import java.util.concurrent.TimeUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.aspectj.lang.ProceedingJoinPoint
import org.assertj.core.api.Assertions.assertThat
import org.mockito.internal.util.MockUtil.isMock
import org.mockito.kotlin.after
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.timeout
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.quartz.JobExecutionContext
Expand All @@ -71,6 +76,7 @@ import org.quartz.core.QuartzScheduler
import org.slf4j.MDC
import org.springframework.aop.support.NameMatchMethodPointcut
import org.springframework.boot.autoconfigure.AutoConfigurations
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
Expand All @@ -85,6 +91,8 @@ import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.scheduling.quartz.SchedulerFactoryBean
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.client.RestClient
Expand Down Expand Up @@ -1089,6 +1097,44 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `does not monitor @Scheduled methods by default`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj").run {
assertThat(it).doesNotHaveBean("sentryScheduledCheckInAdvisor")
}
}

@Test
fun `monitors @Scheduled methods when monitor-scheduled-tasks is enabled`() {
baseContextRunner
.withPropertyValues(
"sentry.dsn=http://key@localhost/proj",
"sentry.monitor-scheduled-tasks=true",
"sentry.exclude-scheduled-tasks[0]=SentryAutoConfigurationTest.ScheduledJobs.excluded",
)
.withConfiguration(AutoConfigurations.of(AopAutoConfiguration::class.java))
.withUserConfiguration(
MockTransportConfiguration::class.java,
ScheduledJobsConfiguration::class.java,
)
.run {
assertThat(it).hasBean("sentryScheduledCheckInAdvisor")
val transport = it.getBean(ITransport::class.java)
val captor = argumentCaptor<SentryEnvelope>()
verify(transport, timeout(5000).times(2)).send(any(), anyOrNull())
verify(transport, after(500).times(2)).send(captor.capture(), anyOrNull())
val payloads =
captor.allValues.map { envelope -> String(envelope.items.first().data, Charsets.UTF_8) }
assertThat(payloads).allMatch { payload ->
payload.contains(
"\"monitor_slug\":\"sentry-auto-configuration-test-scheduled-jobs-sync\""
)
}
assertThat(payloads[0]).contains("\"value\":1,\"unit\":\"hour\"")
assertThat(payloads[1]).contains("\"status\":\"ok\"")
}
}

@Test
fun `Sentry quartz job listener is added`() {
contextRunner
Expand Down Expand Up @@ -1308,6 +1354,18 @@ class SentryAutoConfigurationTest {
@Bean open fun sentryTransport() = transport
}

@Configuration(proxyBeanMethods = false)
@EnableScheduling
open class ScheduledJobsConfiguration {
@Bean open fun scheduledJobs() = ScheduledJobs()
}

open class ScheduledJobs {
@Scheduled(fixedRate = 1, timeUnit = TimeUnit.HOURS) open fun sync() {}

@Scheduled(fixedRate = 1, timeUnit = TimeUnit.HOURS) open fun excluded() {}
}

@Configuration(proxyBeanMethods = false)
open class NoOpTransportConfiguration {

Expand Down
6 changes: 6 additions & 0 deletions sentry-spring-jakarta/api/sentry-spring-jakarta.api
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/checkin/Sent
public class io/sentry/spring/jakarta/checkin/SentryCheckInAdvice : org/aopalliance/intercept/MethodInterceptor, org/springframework/context/EmbeddedValueResolverAware {
public fun <init> ()V
public fun <init> (Lio/sentry/IScopes;)V
public fun <init> (Lio/sentry/IScopes;Z)V
public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object;
public fun setEmbeddedValueResolver (Lorg/springframework/util/StringValueResolver;)V
}
Expand All @@ -168,6 +169,11 @@ public class io/sentry/spring/jakarta/checkin/SentryQuartzConfiguration {
public fun schedulerFactoryBeanCustomizer ()Lorg/springframework/boot/autoconfigure/quartz/SchedulerFactoryBeanCustomizer;
}

public final class io/sentry/spring/jakarta/checkin/SentryScheduledCheckInPointcut : org/springframework/aop/support/StaticMethodMatcherPointcut {
public fun <init> (Ljava/util/List;)V
public fun matches (Ljava/lang/reflect/Method;Ljava/lang/Class;)Z
}

public final class io/sentry/spring/jakarta/checkin/SentrySchedulerFactoryBeanCustomizer : org/springframework/boot/autoconfigure/quartz/SchedulerFactoryBeanCustomizer {
public fun <init> ()V
public fun customize (Lorg/springframework/scheduling/quartz/SchedulerFactoryBean;)V
Expand Down
Loading
Loading