Skip to content

Commit 720378c

Browse files
adinauerclaude
andcommitted
feat(samples): [Cache Tracing 4] Add cache tracing e2e sample
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 84f4889 commit 720378c

File tree

7 files changed

+145
-0
lines changed

7 files changed

+145
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.sentry.samples.spring.boot4;
2+
3+
import org.springframework.web.bind.annotation.DeleteMapping;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.PathVariable;
6+
import org.springframework.web.bind.annotation.PostMapping;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
@RequestMapping("/cache/")
13+
public class CacheController {
14+
private final TodoService todoService;
15+
16+
public CacheController(TodoService todoService) {
17+
this.todoService = todoService;
18+
}
19+
20+
@GetMapping("{id}")
21+
Todo get(@PathVariable Long id) {
22+
return todoService.get(id);
23+
}
24+
25+
@PostMapping
26+
Todo save(@RequestBody Todo todo) {
27+
return todoService.save(todo);
28+
}
29+
30+
@DeleteMapping("{id}")
31+
void delete(@PathVariable Long id) {
32+
todoService.delete(id);
33+
}
34+
}

sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import org.springframework.boot.SpringApplication;
1010
import org.springframework.boot.autoconfigure.SpringBootApplication;
1111
import org.springframework.boot.restclient.RestTemplateBuilder;
12+
import org.springframework.cache.CacheManager;
13+
import org.springframework.cache.annotation.EnableCaching;
14+
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
1215
import org.springframework.context.annotation.Bean;
1316
import org.springframework.scheduling.annotation.EnableScheduling;
1417
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
@@ -19,12 +22,18 @@
1922
import org.springframework.web.reactive.function.client.WebClient;
2023

2124
@SpringBootApplication
25+
@EnableCaching
2226
@EnableScheduling
2327
public class SentryDemoApplication {
2428
public static void main(String[] args) {
2529
SpringApplication.run(SentryDemoApplication.class, args);
2630
}
2731

32+
@Bean
33+
CacheManager cacheManager() {
34+
return new ConcurrentMapCacheManager("todos");
35+
}
36+
2837
@Bean
2938
RestTemplate restTemplate(RestTemplateBuilder builder) {
3039
return builder.build();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.sentry.samples.spring.boot4;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
import org.springframework.cache.annotation.CacheEvict;
6+
import org.springframework.cache.annotation.CachePut;
7+
import org.springframework.cache.annotation.Cacheable;
8+
import org.springframework.stereotype.Service;
9+
10+
@Service
11+
public class TodoService {
12+
private final Map<Long, Todo> store = new ConcurrentHashMap<>();
13+
14+
@Cacheable(value = "todos", key = "#id")
15+
public Todo get(Long id) {
16+
return store.get(id);
17+
}
18+
19+
@CachePut(value = "todos", key = "#todo.id")
20+
public Todo save(Todo todo) {
21+
store.put(todo.getId(), todo);
22+
return todo;
23+
}
24+
25+
@CacheEvict(value = "todos", key = "#id")
26+
public void delete(Long id) {
27+
store.remove(id);
28+
}
29+
}

sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ sentry.logs.enabled=true
2020
sentry.profile-session-sample-rate=1.0
2121
sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces
2222
sentry.profile-lifecycle=TRACE
23+
sentry.enable-cache-tracing=true
2324

2425
# Uncomment and set to true to enable aot compatibility
2526
# This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.sentry.systemtest
2+
3+
import io.sentry.systemtest.util.TestHelper
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
import org.junit.Before
7+
8+
class CacheSystemTest {
9+
lateinit var testHelper: TestHelper
10+
11+
@Before
12+
fun setup() {
13+
testHelper = TestHelper("http://localhost:8080")
14+
testHelper.reset()
15+
}
16+
17+
@Test
18+
fun `cache put and get produce spans`() {
19+
val restClient = testHelper.restClient
20+
21+
// Save a todo (triggers @CachePut -> cache.put span)
22+
val todo = Todo(1L, "test-todo", false)
23+
restClient.saveCachedTodo(todo)
24+
assertEquals(200, restClient.lastKnownStatusCode)
25+
26+
testHelper.ensureTransactionReceived { transaction, _ ->
27+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put")
28+
}
29+
30+
testHelper.reset()
31+
32+
// Get the todo (triggers @Cacheable -> cache.get span, should be a hit)
33+
restClient.getCachedTodo(1L)
34+
assertEquals(200, restClient.lastKnownStatusCode)
35+
36+
testHelper.ensureTransactionReceived { transaction, _ ->
37+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get")
38+
}
39+
}
40+
41+
@Test
42+
fun `cache evict produces span`() {
43+
val restClient = testHelper.restClient
44+
45+
restClient.deleteCachedTodo(1L)
46+
47+
testHelper.ensureTransactionReceived { transaction, _ ->
48+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.remove")
49+
}
50+
}
51+
}

sentry-system-test-support/api/sentry-system-test-support.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,9 @@ public final class io/sentry/systemtest/util/RestTestClient : io/sentry/systemte
548548
public static synthetic fun createPerson$default (Lio/sentry/systemtest/util/RestTestClient;Lio/sentry/systemtest/Person;Ljava/util/Map;ILjava/lang/Object;)Lio/sentry/systemtest/Person;
549549
public final fun createPersonDistributedTracing (Lio/sentry/systemtest/Person;Ljava/util/Map;)Lio/sentry/systemtest/Person;
550550
public static synthetic fun createPersonDistributedTracing$default (Lio/sentry/systemtest/util/RestTestClient;Lio/sentry/systemtest/Person;Ljava/util/Map;ILjava/lang/Object;)Lio/sentry/systemtest/Person;
551+
public final fun deleteCachedTodo (J)V
551552
public final fun errorWithFeatureFlag (Ljava/lang/String;)Ljava/lang/String;
553+
public final fun getCachedTodo (J)Lio/sentry/systemtest/Todo;
552554
public final fun getCountMetric ()Ljava/lang/String;
553555
public final fun getDistributionMetric (J)Ljava/lang/String;
554556
public final fun getGaugeMetric (J)Ljava/lang/String;
@@ -558,6 +560,7 @@ public final class io/sentry/systemtest/util/RestTestClient : io/sentry/systemte
558560
public final fun getTodo (J)Lio/sentry/systemtest/Todo;
559561
public final fun getTodoRestClient (J)Lio/sentry/systemtest/Todo;
560562
public final fun getTodoWebclient (J)Lio/sentry/systemtest/Todo;
563+
public final fun saveCachedTodo (Lio/sentry/systemtest/Todo;)Lio/sentry/systemtest/Todo;
561564
}
562565

563566
public final class io/sentry/systemtest/util/SentryMockServerClient : io/sentry/systemtest/util/LoggingInsecureRestClient {

sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/RestTestClient.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ class RestTestClient(private val backendBaseUrl: String) : LoggingInsecureRestCl
5050
return callTyped(request, true)
5151
}
5252

53+
fun getCachedTodo(id: Long): Todo? {
54+
val request = Request.Builder().url("$backendBaseUrl/cache/$id")
55+
56+
return callTyped(request, true)
57+
}
58+
59+
fun saveCachedTodo(todo: Todo): Todo? {
60+
val request = Request.Builder().url("$backendBaseUrl/cache/").post(toRequestBody(todo))
61+
62+
return callTyped(request, true)
63+
}
64+
65+
fun deleteCachedTodo(id: Long) {
66+
val request = Request.Builder().url("$backendBaseUrl/cache/$id").delete()
67+
68+
call(request, true)
69+
}
70+
5371
fun checkFeatureFlag(flagKey: String): FeatureFlagResponse? {
5472
val request = Request.Builder().url("$backendBaseUrl/feature-flag/check/$flagKey")
5573

0 commit comments

Comments
 (0)