Skip to content
Open
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
1 change: 1 addition & 0 deletions sentry-jcache/api/sentry-jcache.api
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public final class io/sentry/jcache/BuildConfig {
}

public final class io/sentry/jcache/SentryJCacheWrapper : javax/cache/Cache {
public fun <init> (Ljavax/cache/Cache;)V
public fun <init> (Ljavax/cache/Cache;Lio/sentry/IScopes;)V
public fun clear ()V
public fun close ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -36,6 +37,10 @@ public final class SentryJCacheWrapper<K, V> implements Cache<K, V> {
private final @NotNull Cache<K, V> delegate;
private final @NotNull IScopes scopes;

public SentryJCacheWrapper(final @NotNull Cache<K, V> delegate) {
this(delegate, ScopesAdapter.getInstance());
}

public SentryJCacheWrapper(final @NotNull Cache<K, V> delegate, final @NotNull IScopes scopes) {
this.delegate = delegate;
this.scopes = scopes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private static void demonstrateCacheTracing() {
Cache<String, String> rawCache = cacheManager.createCache("myCache", config);

// Wrap with SentryJCacheWrapper to enable cache tracing
Cache<String, String> cache = new SentryJCacheWrapper<>(rawCache, Sentry.getCurrentScopes());
Cache<String, String> cache = new SentryJCacheWrapper<>(rawCache);

// All cache operations inside a transaction produce child spans
ITransaction transaction = Sentry.startTransaction("cache-demo", "demo");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -21,6 +22,7 @@
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
@EnableCaching
@EnableScheduling
public class SentryDemoApplication {
public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Long, Todo> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,6 +23,7 @@
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
@EnableCaching
@EnableScheduling
public class SentryDemoApplication {
public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Long, Todo> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading
Loading