diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/MetricController.java b/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/MetricController.java index 2a969ec884..be75f5e300 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/MetricController.java +++ b/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/MetricController.java @@ -16,6 +16,8 @@ public class MetricController { @GetMapping("count") String count() { + Sentry.setAttribute("user.type", "admin"); + Sentry.setAttribute("feature.version", 2); Sentry.metrics().count("countMetric"); return "count metric increased"; } diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/PersonController.java b/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/PersonController.java index c65c9040d5..489dc629d2 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/PersonController.java +++ b/sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/PersonController.java @@ -27,6 +27,10 @@ Person person(@PathVariable Long id) { ISpan currentSpan = Sentry.getSpan(); ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi"); try { + Sentry.setAttribute("user.type", "admin"); + Sentry.setAttribute("feature.version", 2); + Sentry.setAttribute("debug.enabled", true); + Sentry.logger().warn("warn Sentry logging"); Sentry.logger().error("error Sentry logging"); Sentry.logger().info("hello %s %s", "there", "world!"); diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt index dc2ca2a10a..039d9d640c 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt @@ -21,7 +21,9 @@ class MetricsSystemTest { assertEquals(200, restClient.lastKnownStatusCode) testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "countMetric", "counter", 1.0) + testHelper.doesContainMetric(event, "countMetric", "counter", 1.0) && + testHelper.doesMetricHaveAttribute(event, "countMetric", "user.type", "admin") && + testHelper.doesMetricHaveAttribute(event, "countMetric", "feature.version", 2) } } diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 362a857714..2389734a8b 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -50,7 +50,20 @@ class PersonSystemTest { testHelper.ensureLogsReceived { logs, envelopeHeader -> testHelper.doesContainLogWithBody(logs, "warn Sentry logging") && testHelper.doesContainLogWithBody(logs, "error Sentry logging") && - testHelper.doesContainLogWithBody(logs, "hello there world!") + testHelper.doesContainLogWithBody(logs, "hello there world!") && + testHelper.doesLogWithBodyHaveAttribute( + logs, + "warn Sentry logging", + "user.type", + "admin", + ) && + testHelper.doesLogWithBodyHaveAttribute( + logs, + "warn Sentry logging", + "feature.version", + 2, + ) && + testHelper.doesLogWithBodyHaveAttribute(logs, "warn Sentry logging", "debug.enabled", true) } } diff --git a/sentry-system-test-support/api/sentry-system-test-support.api b/sentry-system-test-support/api/sentry-system-test-support.api index ff620c7d80..51ef7da55d 100644 --- a/sentry-system-test-support/api/sentry-system-test-support.api +++ b/sentry-system-test-support/api/sentry-system-test-support.api @@ -574,6 +574,8 @@ public final class io/sentry/systemtest/util/TestHelper { public static synthetic fun doesContainMetric$default (Lio/sentry/systemtest/util/TestHelper;Lio/sentry/SentryMetricsEvents;Ljava/lang/String;Ljava/lang/String;DLjava/lang/String;ILjava/lang/Object;)Z public final fun doesEventHaveExceptionMessage (Lio/sentry/SentryEvent;Ljava/lang/String;)Z public final fun doesEventHaveFlag (Lio/sentry/SentryEvent;Ljava/lang/String;Z)Z + public final fun doesLogWithBodyHaveAttribute (Lio/sentry/SentryLogEvents;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)Z + public final fun doesMetricHaveAttribute (Lio/sentry/SentryMetricsEvents;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)Z public final fun doesTransactionContainSpanWithDescription (Lio/sentry/protocol/SentryTransaction;Ljava/lang/String;)Z public final fun doesTransactionContainSpanWithOp (Lio/sentry/protocol/SentryTransaction;Ljava/lang/String;)Z public final fun doesTransactionContainSpanWithOpAndDescription (Lio/sentry/protocol/SentryTransaction;Ljava/lang/String;Ljava/lang/String;)Z diff --git a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt index 2881460a2d..19817c34ac 100644 --- a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt +++ b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt @@ -190,6 +190,68 @@ class TestHelper(backendUrl: String) { return true } + fun doesLogWithBodyHaveAttribute( + logs: SentryLogEvents, + body: String, + attributeKey: String, + attributeValue: Any?, + ): Boolean { + val logItem = logs.items.firstOrNull { logItem -> logItem.body == body } + if (logItem == null) { + println("Unable to find log item with body $body in logs:") + logObject(logs) + return false + } + + val attr = logItem.attributes?.get(attributeKey) + if (attr == null) { + println("Unable to find attribute $attributeKey on log with body $body:") + logObject(logItem) + return false + } + + if (attr.value != attributeValue) { + println( + "Attribute $attributeKey has value ${attr.value} but expected $attributeValue on log with body $body:" + ) + logObject(logItem) + return false + } + + return true + } + + fun doesMetricHaveAttribute( + metrics: SentryMetricsEvents, + metricName: String, + attributeKey: String, + attributeValue: Any?, + ): Boolean { + val metricItem = metrics.items.firstOrNull { it.name == metricName } + if (metricItem == null) { + println("Unable to find metric with name $metricName in metrics:") + logObject(metrics) + return false + } + + val attr = metricItem.attributes?.get(attributeKey) + if (attr == null) { + println("Unable to find attribute $attributeKey on metric $metricName:") + logObject(metricItem) + return false + } + + if (attr.value != attributeValue) { + println( + "Attribute $attributeKey has value ${attr.value} but expected $attributeValue on metric $metricName:" + ) + logObject(metricItem) + return false + } + + return true + } + private fun checkIfTransactionMatches( envelopeString: String, callback: ((SentryTransaction, SentryEnvelopeHeader) -> Boolean),