diff --git a/sentry-jcache/api/sentry-jcache.api b/sentry-jcache/api/sentry-jcache.api index 61f1031267f..b834ba41064 100644 --- a/sentry-jcache/api/sentry-jcache.api +++ b/sentry-jcache/api/sentry-jcache.api @@ -4,6 +4,7 @@ public final class io/sentry/jcache/BuildConfig { } public final class io/sentry/jcache/SentryJCacheWrapper : javax/cache/Cache { + public fun (Ljavax/cache/Cache;)V public fun (Ljavax/cache/Cache;Lio/sentry/IScopes;)V public fun clear ()V public fun close ()V diff --git a/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java b/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java index 61c829726f6..d5022a5455f 100644 --- a/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java +++ b/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java @@ -2,6 +2,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import io.sentry.SpanDataConvention; import io.sentry.SpanOptions; import io.sentry.SpanStatus; @@ -36,6 +37,10 @@ public final class SentryJCacheWrapper implements Cache { private final @NotNull Cache delegate; private final @NotNull IScopes scopes; + public SentryJCacheWrapper(final @NotNull Cache delegate) { + this(delegate, ScopesAdapter.getInstance()); + } + public SentryJCacheWrapper(final @NotNull Cache delegate, final @NotNull IScopes scopes) { this.delegate = delegate; this.scopes = scopes; diff --git a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java index 15888cb5702..6f0a554821e 100644 --- a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java +++ b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java @@ -211,7 +211,7 @@ private static void demonstrateCacheTracing() { Cache rawCache = cacheManager.createCache("myCache", config); // Wrap with SentryJCacheWrapper to enable cache tracing - Cache cache = new SentryJCacheWrapper<>(rawCache, Sentry.getCurrentScopes()); + Cache cache = new SentryJCacheWrapper<>(rawCache); // All cache operations inside a transaction produce child spans ITransaction transaction = Sentry.startTransaction("cache-demo", "demo"); diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts index ef38162d6bf..c3e8ba06fae 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts @@ -59,6 +59,10 @@ dependencies { implementation(projects.sentryAsyncProfiler) implementation(libs.otel) + // cache tracing + implementation(libs.springboot4.starter.cache) + implementation(libs.caffeine) + // database query tracing implementation(projects.sentryJdbc) runtimeOnly(libs.hsqldb) diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/CacheController.java b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/CacheController.java new file mode 100644 index 00000000000..3c2e66442de --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/CacheController.java @@ -0,0 +1,34 @@ +package io.sentry.samples.spring.boot4; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/cache/") +public class CacheController { + private final TodoService todoService; + + public CacheController(TodoService todoService) { + this.todoService = todoService; + } + + @GetMapping("{id}") + Todo get(@PathVariable Long id) { + return todoService.get(id); + } + + @PostMapping + Todo save(@RequestBody Todo todo) { + return todoService.save(todo); + } + + @DeleteMapping("{id}") + void delete(@PathVariable Long id) { + todoService.delete(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java index aa5ebce68cd..b00609ad9ac 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java @@ -11,6 +11,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.restclient.RestTemplateBuilder; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; @@ -21,6 +22,7 @@ import org.springframework.web.reactive.function.client.WebClient; @SpringBootApplication +@EnableCaching @EnableScheduling public class SentryDemoApplication { public static void main(String[] args) { diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/TodoService.java b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/TodoService.java new file mode 100644 index 00000000000..c837ab8398a --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/java/io/sentry/samples/spring/boot4/TodoService.java @@ -0,0 +1,29 @@ +package io.sentry.samples.spring.boot4; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +public class TodoService { + private final Map store = new ConcurrentHashMap<>(); + + @Cacheable(value = "todos", key = "#id") + public Todo get(Long id) { + return store.get(id); + } + + @CachePut(value = "todos", key = "#todo.id") + public Todo save(Todo todo) { + store.put(todo.getId(), todo); + return todo; + } + + @CacheEvict(value = "todos", key = "#id") + public void delete(Long id) { + store.remove(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties index 6b57706019b..a0808e04fde 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties @@ -20,6 +20,9 @@ sentry.in-app-includes="io.sentry.samples" sentry.profile-session-sample-rate=1.0 sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces sentry.profile-lifecycle=TRACE +sentry.enable-cache-tracing=true +spring.cache.cache-names=todos +spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt new file mode 100644 index 00000000000..7853750a8fb --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt @@ -0,0 +1,51 @@ +package io.sentry.systemtest + +import io.sentry.systemtest.util.TestHelper +import kotlin.test.Test +import kotlin.test.assertEquals +import org.junit.Before + +class CacheSystemTest { + lateinit var testHelper: TestHelper + + @Before + fun setup() { + testHelper = TestHelper("http://localhost:8080") + testHelper.reset() + } + + @Test + fun `cache put and get produce spans`() { + val restClient = testHelper.restClient + + // Save a todo (triggers @CachePut -> cache.put span) + val todo = Todo(1L, "test-todo", false) + restClient.saveCachedTodo(todo) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put") + } + + testHelper.reset() + + // Get the todo (triggers @Cacheable -> cache.get span, should be a hit) + restClient.getCachedTodo(1L) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get") + } + } + + @Test + fun `cache evict produces span`() { + val restClient = testHelper.restClient + + restClient.deleteCachedTodo(1L) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.remove") + } + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts index 4f3d64524fd..01e07fc2526 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts @@ -58,6 +58,10 @@ dependencies { implementation(projects.sentryAsyncProfiler) implementation(projects.sentryOpentelemetry.sentryOpentelemetryOtlpSpring) + // cache tracing + implementation(libs.springboot4.starter.cache) + implementation(libs.caffeine) + // database query tracing implementation(projects.sentryJdbc) runtimeOnly(libs.hsqldb) diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/CacheController.java b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/CacheController.java new file mode 100644 index 00000000000..f453f81187c --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/CacheController.java @@ -0,0 +1,34 @@ +package io.sentry.samples.spring.boot4.otlp; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/cache/") +public class CacheController { + private final TodoService todoService; + + public CacheController(TodoService todoService) { + this.todoService = todoService; + } + + @GetMapping("{id}") + Todo get(@PathVariable Long id) { + return todoService.get(id); + } + + @PostMapping + Todo save(@RequestBody Todo todo) { + return todoService.save(todo); + } + + @DeleteMapping("{id}") + void delete(@PathVariable Long id) { + todoService.delete(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/SentryDemoApplication.java index b4c58c4882e..1bf622154d0 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/SentryDemoApplication.java +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/SentryDemoApplication.java @@ -12,6 +12,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.restclient.RestTemplateBuilder; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; @@ -22,6 +23,7 @@ import org.springframework.web.reactive.function.client.WebClient; @SpringBootApplication +@EnableCaching @EnableScheduling public class SentryDemoApplication { public static void main(String[] args) { diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/TodoService.java b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/TodoService.java new file mode 100644 index 00000000000..1a748a165a4 --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/java/io/sentry/samples/spring/boot4/otlp/TodoService.java @@ -0,0 +1,29 @@ +package io.sentry.samples.spring.boot4.otlp; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +public class TodoService { + private final Map store = new ConcurrentHashMap<>(); + + @Cacheable(value = "todos", key = "#id") + public Todo get(Long id) { + return store.get(id); + } + + @CachePut(value = "todos", key = "#todo.id") + public Todo save(Todo todo) { + store.put(todo.getId(), todo); + return todo; + } + + @CacheEvict(value = "todos", key = "#id") + public void delete(Long id) { + store.remove(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/resources/application.properties index 43c0bd18c08..f9b35099062 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/main/resources/application.properties @@ -20,6 +20,9 @@ sentry.logs.enabled=true sentry.profile-session-sample-rate=1.0 sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces sentry.profile-lifecycle=TRACE +sentry.enable-cache-tracing=true +spring.cache.cache-names=todos +spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-4-otlp/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt new file mode 100644 index 00000000000..7853750a8fb --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt @@ -0,0 +1,51 @@ +package io.sentry.systemtest + +import io.sentry.systemtest.util.TestHelper +import kotlin.test.Test +import kotlin.test.assertEquals +import org.junit.Before + +class CacheSystemTest { + lateinit var testHelper: TestHelper + + @Before + fun setup() { + testHelper = TestHelper("http://localhost:8080") + testHelper.reset() + } + + @Test + fun `cache put and get produce spans`() { + val restClient = testHelper.restClient + + // Save a todo (triggers @CachePut -> cache.put span) + val todo = Todo(1L, "test-todo", false) + restClient.saveCachedTodo(todo) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put") + } + + testHelper.reset() + + // Get the todo (triggers @Cacheable -> cache.get span, should be a hit) + restClient.getCachedTodo(1L) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get") + } + } + + @Test + fun `cache evict produces span`() { + val restClient = testHelper.restClient + + restClient.deleteCachedTodo(1L) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.remove") + } + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts index b9986a31d02..cdcf65711a8 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts @@ -32,6 +32,10 @@ dependencies { implementation(libs.springboot4.starter.webflux) implementation(libs.springboot4.starter.webclient) + // cache tracing + implementation(libs.springboot4.starter.cache) + implementation(libs.caffeine) + testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(projects.sentrySystemTestSupport) testImplementation(libs.apollo3.kotlin) diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/CacheController.java b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/CacheController.java new file mode 100644 index 00000000000..3c2e66442de --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/CacheController.java @@ -0,0 +1,34 @@ +package io.sentry.samples.spring.boot4; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/cache/") +public class CacheController { + private final TodoService todoService; + + public CacheController(TodoService todoService) { + this.todoService = todoService; + } + + @GetMapping("{id}") + Todo get(@PathVariable Long id) { + return todoService.get(id); + } + + @PostMapping + Todo save(@RequestBody Todo todo) { + return todoService.save(todo); + } + + @DeleteMapping("{id}") + void delete(@PathVariable Long id) { + todoService.delete(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java index 72980871730..0d37be7634c 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.web.reactive.function.client.WebClient; @SpringBootApplication +@EnableCaching public class SentryDemoApplication { public static void main(String[] args) { SpringApplication.run(SentryDemoApplication.class, args); diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/TodoService.java b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/TodoService.java new file mode 100644 index 00000000000..c837ab8398a --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/java/io/sentry/samples/spring/boot4/TodoService.java @@ -0,0 +1,29 @@ +package io.sentry.samples.spring.boot4; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +public class TodoService { + private final Map store = new ConcurrentHashMap<>(); + + @Cacheable(value = "todos", key = "#id") + public Todo get(Long id) { + return store.get(id); + } + + @CachePut(value = "todos", key = "#todo.id") + public Todo save(Todo todo) { + store.put(todo.getId(), todo); + return todo; + } + + @CacheEvict(value = "todos", key = "#id") + public void delete(Long id) { + store.remove(id); + } +} diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties index 9e9d6596e08..9fc969efd28 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties @@ -15,3 +15,6 @@ sentry.enable-spotlight=true sentry.profile-session-sample-rate=1.0 sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces sentry.profile-lifecycle=TRACE +sentry.enable-cache-tracing=true +spring.cache.cache-names=todos +spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt new file mode 100644 index 00000000000..7853750a8fb --- /dev/null +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt @@ -0,0 +1,51 @@ +package io.sentry.systemtest + +import io.sentry.systemtest.util.TestHelper +import kotlin.test.Test +import kotlin.test.assertEquals +import org.junit.Before + +class CacheSystemTest { + lateinit var testHelper: TestHelper + + @Before + fun setup() { + testHelper = TestHelper("http://localhost:8080") + testHelper.reset() + } + + @Test + fun `cache put and get produce spans`() { + val restClient = testHelper.restClient + + // Save a todo (triggers @CachePut -> cache.put span) + val todo = Todo(1L, "test-todo", false) + restClient.saveCachedTodo(todo) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put") + } + + testHelper.reset() + + // Get the todo (triggers @Cacheable -> cache.get span, should be a hit) + restClient.getCachedTodo(1L) + assertEquals(200, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get") + } + } + + @Test + fun `cache evict produces span`() { + val restClient = testHelper.restClient + + restClient.deleteCachedTodo(1L) + + testHelper.ensureTransactionReceived { transaction, _ -> + testHelper.doesTransactionContainSpanWithOp(transaction, "cache.remove") + } + } +} diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 55a1136fbe0..70489c580a5 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -61,7 +61,8 @@ "OTEL_TRACES_EXPORTER": "none", "OTEL_METRICS_EXPORTER": "none", "OTEL_LOGS_EXPORTER": "none", - "SENTRY_LOGS_ENABLED": "true" + "SENTRY_LOGS_ENABLED": "true", + "SENTRY_ENABLE_CACHE_TRACING": "true" } class ServerType(Enum):