From 7426ac713e6042ea577f248c1d48b80ae06bba93 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 18 Mar 2026 11:30:49 +0100 Subject: [PATCH] feat(cache): Add cache.write boolean span attribute Set cache.write on spans across all four cache wrapper implementations to indicate whether an operation actually modified the cache. This complements the existing cache.hit attribute for read operations. Co-Authored-By: Claude --- .../java/io/sentry/jcache/SentryJCacheWrapper.java | 13 +++++++++++++ .../io/sentry/jcache/SentryJCacheWrapperTest.kt | 14 ++++++++++++++ .../sentry/spring7/cache/SentryCacheWrapper.java | 8 ++++++++ .../sentry/spring7/cache/SentryCacheWrapperTest.kt | 12 ++++++++++++ .../spring/jakarta/cache/SentryCacheWrapper.java | 8 ++++++++ .../spring/jakarta/cache/SentryCacheWrapperTest.kt | 12 ++++++++++++ .../io/sentry/spring/cache/SentryCacheWrapper.java | 7 +++++++ .../sentry/spring/cache/SentryCacheWrapperTest.kt | 9 +++++++++ sentry/api/sentry.api | 1 + .../main/java/io/sentry/SpanDataConvention.java | 1 + 10 files changed, 85 insertions(+) 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 e8377a2aae..6e97c2baa8 100644 --- a/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java +++ b/sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java @@ -105,6 +105,7 @@ public void put(final K key, final V value) { } try { delegate.put(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -123,6 +124,7 @@ public V getAndPut(final K key, final V value) { } try { final V result = delegate.getAndPut(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -143,6 +145,7 @@ public void putAll(final Map map) { } try { delegate.putAll(map); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -161,6 +164,7 @@ public boolean putIfAbsent(final K key, final V value) { } try { final boolean result = delegate.putIfAbsent(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -180,6 +184,7 @@ public boolean replace(final K key, final V oldValue, final V newValue) { } try { final boolean result = delegate.replace(key, oldValue, newValue); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -199,6 +204,7 @@ public boolean replace(final K key, final V value) { } try { final boolean result = delegate.replace(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -218,6 +224,7 @@ public V getAndReplace(final K key, final V value) { } try { final V result = delegate.getAndReplace(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result != null); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -239,6 +246,7 @@ public boolean remove(final K key) { } try { final boolean result = delegate.remove(key); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -258,6 +266,7 @@ public boolean remove(final K key, final V oldValue) { } try { final boolean result = delegate.remove(key, oldValue); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -277,6 +286,7 @@ public V getAndRemove(final K key) { } try { final V result = delegate.getAndRemove(key); + span.setData(SpanDataConvention.CACHE_WRITE, result != null); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -297,6 +307,7 @@ public void removeAll(final Set keys) { } try { delegate.removeAll(keys); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -316,6 +327,7 @@ public void removeAll() { } try { delegate.removeAll(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -337,6 +349,7 @@ public void clear() { } try { delegate.clear(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); diff --git a/sentry-jcache/src/test/kotlin/io/sentry/jcache/SentryJCacheWrapperTest.kt b/sentry-jcache/src/test/kotlin/io/sentry/jcache/SentryJCacheWrapperTest.kt index 3021b04af0..dd3f9f59bf 100644 --- a/sentry-jcache/src/test/kotlin/io/sentry/jcache/SentryJCacheWrapperTest.kt +++ b/sentry-jcache/src/test/kotlin/io/sentry/jcache/SentryJCacheWrapperTest.kt @@ -62,6 +62,7 @@ class SentryJCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("auto.cache.jcache", span.spanContext.origin) assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) @@ -128,6 +129,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.put", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -145,6 +147,7 @@ class SentryJCacheWrapperTest { assertEquals("oldValue", result) assertEquals(1, tx.spans.size) assertEquals("cache.getAndPut", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("getAndPut", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -163,6 +166,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.putAll", span.operation) assertEquals("testCache", span.description) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) val cacheKeys = span.getData(SpanDataConvention.CACHE_KEY_KEY) as List<*> assertTrue(cacheKeys.containsAll(listOf("k1", "k2"))) assertEquals("putAll", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) @@ -184,6 +188,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.putIfAbsent", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -204,6 +209,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.replace", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("replace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -221,6 +227,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.replace", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("replace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -240,6 +247,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.getAndReplace", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("getAndReplace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -258,6 +266,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.remove", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("remove", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -274,6 +283,7 @@ class SentryJCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.remove", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("remove", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -290,6 +300,7 @@ class SentryJCacheWrapperTest { assertEquals("value", result) assertEquals(1, tx.spans.size) assertEquals("cache.getAndRemove", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("getAndRemove", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -308,6 +319,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.removeAll", span.operation) assertEquals("testCache", span.description) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("removeAll", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -323,6 +335,7 @@ class SentryJCacheWrapperTest { verify(delegate).removeAll() assertEquals(1, tx.spans.size) assertEquals("cache.removeAll", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("removeAll", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -340,6 +353,7 @@ class SentryJCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.clear", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheWrapper.java b/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheWrapper.java index 646e8a09cf..38ff9b7d4d 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheWrapper.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheWrapper.java @@ -96,6 +96,7 @@ public SentryCacheWrapper(final @NotNull Cache delegate, final @NotNull IScopes return valueLoader.call(); }); span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get()); + span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get()); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -171,6 +172,7 @@ public CompletableFuture retrieve( span.setThrowable(throwable); } else { span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get()); + span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get()); span.setStatus(SpanStatus.OK); } span.finish(); @@ -186,6 +188,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { delegate.put(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -205,6 +208,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { final ValueWrapper result = delegate.putIfAbsent(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result == null); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -225,6 +229,7 @@ public void evict(final @NotNull Object key) { } try { delegate.evict(key); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -243,6 +248,7 @@ public boolean evictIfPresent(final @NotNull Object key) { } try { final boolean result = delegate.evictIfPresent(key); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -263,6 +269,7 @@ public void clear() { } try { delegate.clear(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -281,6 +288,7 @@ public boolean invalidate() { } try { final boolean result = delegate.invalidate(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { diff --git a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/cache/SentryCacheWrapperTest.kt b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/cache/SentryCacheWrapperTest.kt index c42abfcb7a..8f3867dd50 100644 --- a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/cache/SentryCacheWrapperTest.kt +++ b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/cache/SentryCacheWrapperTest.kt @@ -61,6 +61,7 @@ class SentryCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("auto.cache.spring", span.spanContext.origin) assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) @@ -155,6 +156,7 @@ class SentryCacheWrapperTest { assertEquals("cached", result) assertEquals(1, tx.spans.size) assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } @Test @@ -172,6 +174,7 @@ class SentryCacheWrapperTest { assertEquals("loaded", result) assertEquals(1, tx.spans.size) assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } // -- retrieve(Object key) -- @@ -191,6 +194,7 @@ class SentryCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("retrieve", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) assertTrue(span.isFinished) } @@ -287,6 +291,7 @@ class SentryCacheWrapperTest { assertEquals("cached", result.get()) assertEquals(1, tx.spans.size) assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertTrue(tx.spans.first().isFinished) } @@ -306,6 +311,7 @@ class SentryCacheWrapperTest { assertEquals("loaded", result.get()) assertEquals(1, tx.spans.size) assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertTrue(tx.spans.first().isFinished) } @@ -355,6 +361,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.put", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -375,6 +382,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.putIfAbsent", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -393,6 +401,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.evict", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evict", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -409,6 +418,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.evictIfPresent", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evictIfPresent", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -426,6 +436,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.clear", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -443,6 +454,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.invalidate", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("invalidate", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheWrapper.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheWrapper.java index a994bce0b6..61405c0276 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheWrapper.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheWrapper.java @@ -96,6 +96,7 @@ public SentryCacheWrapper(final @NotNull Cache delegate, final @NotNull IScopes return valueLoader.call(); }); span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get()); + span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get()); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -171,6 +172,7 @@ public CompletableFuture retrieve( span.setThrowable(throwable); } else { span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get()); + span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get()); span.setStatus(SpanStatus.OK); } span.finish(); @@ -186,6 +188,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { delegate.put(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -205,6 +208,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { final ValueWrapper result = delegate.putIfAbsent(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result == null); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -225,6 +229,7 @@ public void evict(final @NotNull Object key) { } try { delegate.evict(key); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -243,6 +248,7 @@ public boolean evictIfPresent(final @NotNull Object key) { } try { final boolean result = delegate.evictIfPresent(key); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -263,6 +269,7 @@ public void clear() { } try { delegate.clear(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -281,6 +288,7 @@ public boolean invalidate() { } try { final boolean result = delegate.invalidate(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/cache/SentryCacheWrapperTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/cache/SentryCacheWrapperTest.kt index 1739003fa3..d366e7574f 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/cache/SentryCacheWrapperTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/cache/SentryCacheWrapperTest.kt @@ -61,6 +61,7 @@ class SentryCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("auto.cache.spring", span.spanContext.origin) assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) @@ -155,6 +156,7 @@ class SentryCacheWrapperTest { assertEquals("cached", result) assertEquals(1, tx.spans.size) assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } @Test @@ -172,6 +174,7 @@ class SentryCacheWrapperTest { assertEquals("loaded", result) assertEquals(1, tx.spans.size) assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } // -- retrieve(Object key) -- @@ -191,6 +194,7 @@ class SentryCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("retrieve", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) assertTrue(span.isFinished) } @@ -287,6 +291,7 @@ class SentryCacheWrapperTest { assertEquals("cached", result.get()) assertEquals(1, tx.spans.size) assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertTrue(tx.spans.first().isFinished) } @@ -306,6 +311,7 @@ class SentryCacheWrapperTest { assertEquals("loaded", result.get()) assertEquals(1, tx.spans.size) assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertTrue(tx.spans.first().isFinished) } @@ -355,6 +361,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.put", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -375,6 +382,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.putIfAbsent", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -393,6 +401,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.evict", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evict", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -409,6 +418,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.evictIfPresent", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evictIfPresent", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -426,6 +436,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.clear", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -443,6 +454,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.invalidate", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("invalidate", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } diff --git a/sentry-spring/src/main/java/io/sentry/spring/cache/SentryCacheWrapper.java b/sentry-spring/src/main/java/io/sentry/spring/cache/SentryCacheWrapper.java index eaa9028b47..e4deceae29 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/cache/SentryCacheWrapper.java +++ b/sentry-spring/src/main/java/io/sentry/spring/cache/SentryCacheWrapper.java @@ -94,6 +94,7 @@ public SentryCacheWrapper(final @NotNull Cache delegate, final @NotNull IScopes return valueLoader.call(); }); span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get()); + span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get()); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -114,6 +115,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { delegate.put(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -133,6 +135,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) { } try { final ValueWrapper result = delegate.putIfAbsent(key, value); + span.setData(SpanDataConvention.CACHE_WRITE, result == null); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -153,6 +156,7 @@ public void evict(final @NotNull Object key) { } try { delegate.evict(key); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -171,6 +175,7 @@ public boolean evictIfPresent(final @NotNull Object key) { } try { final boolean result = delegate.evictIfPresent(key); + span.setData(SpanDataConvention.CACHE_WRITE, result); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { @@ -191,6 +196,7 @@ public void clear() { } try { delegate.clear(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); } catch (Throwable e) { span.setStatus(SpanStatus.INTERNAL_ERROR); @@ -209,6 +215,7 @@ public boolean invalidate() { } try { final boolean result = delegate.invalidate(); + span.setData(SpanDataConvention.CACHE_WRITE, true); span.setStatus(SpanStatus.OK); return result; } catch (Throwable e) { diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/cache/SentryCacheWrapperTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/cache/SentryCacheWrapperTest.kt index 7eed20b0d4..11e078e86f 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/cache/SentryCacheWrapperTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/cache/SentryCacheWrapperTest.kt @@ -59,6 +59,7 @@ class SentryCacheWrapperTest { assertEquals("myKey", span.description) assertEquals(SpanStatus.OK, span.status) assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY)) + assertNull(span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("auto.cache.spring", span.spanContext.origin) assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) @@ -153,6 +154,7 @@ class SentryCacheWrapperTest { assertEquals("cached", result) assertEquals(1, tx.spans.size) assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } @Test @@ -170,6 +172,7 @@ class SentryCacheWrapperTest { assertEquals("loaded", result) assertEquals(1, tx.spans.size) assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY)) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) } // -- put -- @@ -186,6 +189,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.put", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -206,6 +210,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.putIfAbsent", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -224,6 +229,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.evict", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evict", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -240,6 +246,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.evictIfPresent", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("evictIfPresent", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -257,6 +264,7 @@ class SentryCacheWrapperTest { val span = tx.spans.first() assertEquals("cache.clear", span.operation) assertEquals(SpanStatus.OK, span.status) + assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE)) assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY)) assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY)) } @@ -274,6 +282,7 @@ class SentryCacheWrapperTest { assertTrue(result) assertEquals(1, tx.spans.size) assertEquals("cache.invalidate", tx.spans.first().operation) + assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE)) assertEquals("invalidate", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY)) } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index c66106ca47..c9d3da2f44 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -4345,6 +4345,7 @@ public abstract interface class io/sentry/SpanDataConvention { public static final field CACHE_HIT_KEY Ljava/lang/String; public static final field CACHE_KEY_KEY Ljava/lang/String; public static final field CACHE_OPERATION_KEY Ljava/lang/String; + public static final field CACHE_WRITE Ljava/lang/String; public static final field CALL_STACK_KEY Ljava/lang/String; public static final field CONTRIBUTES_TTFD Ljava/lang/String; public static final field CONTRIBUTES_TTID Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/SpanDataConvention.java b/sentry/src/main/java/io/sentry/SpanDataConvention.java index 75d688e2dd..cbb6d0ceb7 100644 --- a/sentry/src/main/java/io/sentry/SpanDataConvention.java +++ b/sentry/src/main/java/io/sentry/SpanDataConvention.java @@ -29,4 +29,5 @@ public interface SpanDataConvention { String CACHE_HIT_KEY = "cache.hit"; String CACHE_KEY_KEY = "cache.key"; String CACHE_OPERATION_KEY = "cache.operation"; + String CACHE_WRITE = "cache.write"; }