diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3942983f4..2ee5e782434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Support multiple debug-metadata.properties ([#3024](https://github.com/getsentry/sentry-java/pull/3024)) +### Fixes + +- Send breadcrumbs and client error even without transactions ([#3087](https://github.com/getsentry/sentry-java/pull/3087)) + ### Dependencies - Bump Gradle from v8.4.0 to v8.5.0 ([#3070](https://github.com/getsentry/sentry-java/pull/3070)) diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt index 4870c8bb64b..8be7c4b8cec 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt @@ -139,7 +139,22 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req /** Finishes the call root span, and runs [beforeFinish] on it. Then a breadcrumb is sent. */ fun finishEvent(finishDate: SentryDate? = null, beforeFinish: ((span: ISpan) -> Unit)? = null) { - callRootSpan ?: return + // We put data in the hint and send a breadcrumb + val hint = Hint() + hint.set(TypeCheckHint.OKHTTP_REQUEST, request) + response?.let { hint.set(TypeCheckHint.OKHTTP_RESPONSE, it) } + + // We send the breadcrumb even without spans. + hub.addBreadcrumb(breadcrumb, hint) + // No span is created (e.g. no transaction is running) + if (callRootSpan == null) { + + // We report the client error even without spans. + clientErrorResponse?.let { + SentryOkHttpUtils.captureClientError(hub, it.request, it) + } + return + } // We forcefully finish all spans, even if they should already have been finished through finishSpan() eventSpans.values.filter { !it.isFinished }.forEach { @@ -159,13 +174,6 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req } else { callRootSpan.finish() } - - // We put data in the hint and send a breadcrumb - val hint = Hint() - hint.set(TypeCheckHint.OKHTTP_REQUEST, request) - response?.let { hint.set(TypeCheckHint.OKHTTP_RESPONSE, it) } - - hub.addBreadcrumb(breadcrumb, hint) return } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt index 1363c237850..308a7bbf26e 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt @@ -123,11 +123,11 @@ class SentryOkHttpEventTest { } @Test - fun `when root span is null, no breadcrumb is created`() { + fun `when root span is null, breadcrumb is created anyway`() { val sut = fixture.getSut(currentSpan = null) assertNull(sut.callRootSpan) sut.finishEvent() - verify(fixture.hub, never()).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test @@ -572,6 +572,28 @@ class SentryOkHttpEventTest { sut.setClientErrorResponse(clientErrorResponse) verify(fixture.hub, never()).captureEvent(any(), any()) sut.finishEvent() + assertNotNull(sut.callRootSpan) + verify(fixture.hub).captureEvent( + argThat { + throwable is SentryHttpClientException && + throwable!!.message!!.startsWith("HTTP Client Error with status code: ") + }, + argThat { + get(TypeCheckHint.OKHTTP_REQUEST) != null && + get(TypeCheckHint.OKHTTP_RESPONSE) != null + } + ) + } + + @Test + fun `setClientErrorResponse will capture the client error on finishEvent even when no span is running`() { + val sut = fixture.getSut(currentSpan = null) + val clientErrorResponse = mock() + whenever(clientErrorResponse.request).thenReturn(fixture.mockRequest) + sut.setClientErrorResponse(clientErrorResponse) + verify(fixture.hub, never()).captureEvent(any(), any()) + sut.finishEvent() + assertNull(sut.callRootSpan) verify(fixture.hub).captureEvent( argThat { throwable is SentryHttpClientException &&