Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Keep dropped tombstone and ANR events dropped, instead of reporting the same app exit again at every app start ([#6002](https://github.com/getsentry/sentry-java/pull/6002))
- Keep the `EventListener` wrapped by `SentryOkHttpEventListener` per `Call` ([#6003](https://github.com/getsentry/sentry-java/pull/6003))

## 8.54.0

Expand Down
1 change: 1 addition & 0 deletions sentry-okhttp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
testImplementation(libs.mockito.inline)
testImplementation(libs.okhttp)
testImplementation(libs.okhttp.mockwebserver)
testImplementation(libs.google.truth)
}

buildConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public open class SentryOkHttpEventListener(
private val scopes: IScopes = ScopesAdapter.getInstance(),
private val originalEventListenerCreator: ((call: Call) -> EventListener)? = null,
) : EventListener() {
private var originalEventListener: EventListener? = null
private val originalEventListenerMap: MutableMap<Call, EventListener> = ConcurrentHashMap()

public companion object {
internal const val PROXY_SELECT_EVENT = "http.client.proxy_select_ms"
Expand Down Expand Up @@ -85,27 +85,34 @@ public open class SentryOkHttpEventListener(
) : this(scopes, originalEventListenerCreator = { originalEventListenerFactory.create(it) })

override fun callStart(call: Call) {
originalEventListener = originalEventListenerCreator?.invoke(call)
// The EventListener.Factory contract binds a listener to a single call, so the wrapped
// listener is kept per call instead of in a field shared by all concurrent calls
val originalEventListener = originalEventListenerCreator?.invoke(call)
if (originalEventListener != null) {
originalEventListenerMap[call] = originalEventListener
}
originalEventListener?.callStart(call)
// If the wrapped EventListener is ours, we can just delegate the calls,
// without creating other events that would create duplicates
if (canCreateEventSpan()) {
if (canCreateEventSpan(originalEventListener)) {
eventMap[call] = SentryOkHttpEvent(scopes, call.request())
}
}

override fun proxySelectStart(call: Call, url: HttpUrl) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.proxySelectStart(call, url)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(PROXY_SELECT_EVENT)
}

override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List<Proxy>) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.proxySelectEnd(call, url, proxies)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -117,17 +124,19 @@ public open class SentryOkHttpEventListener(
}

override fun dnsStart(call: Call, domainName: String) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.dnsStart(call, domainName)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(DNS_EVENT)
}

override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.dnsEnd(call, domainName, inetAddressList)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -140,26 +149,29 @@ public open class SentryOkHttpEventListener(
}

override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.connectStart(call, inetSocketAddress, proxy)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(CONNECT_EVENT)
}

override fun secureConnectStart(call: Call) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.secureConnectStart(call)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(SECURE_CONNECT_EVENT)
}

override fun secureConnectEnd(call: Call, handshake: Handshake?) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.secureConnectEnd(call, handshake)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -172,8 +184,9 @@ public open class SentryOkHttpEventListener(
proxy: Proxy,
protocol: Protocol?,
) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.connectEnd(call, inetSocketAddress, proxy, protocol)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -188,8 +201,9 @@ public open class SentryOkHttpEventListener(
protocol: Protocol?,
ioe: IOException,
) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.connectFailed(call, inetSocketAddress, proxy, protocol, ioe)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -202,53 +216,59 @@ public open class SentryOkHttpEventListener(
}

override fun connectionAcquired(call: Call, connection: Connection) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.connectionAcquired(call, connection)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(CONNECTION_EVENT)
}

override fun connectionReleased(call: Call, connection: Connection) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.connectionReleased(call, connection)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventFinish(CONNECTION_EVENT)
}

override fun requestHeadersStart(call: Call) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.requestHeadersStart(call)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(REQUEST_HEADERS_EVENT)
}

override fun requestHeadersEnd(call: Call, request: Request) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.requestHeadersEnd(call, request)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventFinish(REQUEST_HEADERS_EVENT)
}

override fun requestBodyStart(call: Call) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.requestBodyStart(call)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(REQUEST_BODY_EVENT)
}

override fun requestBodyEnd(call: Call, byteCount: Long) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.requestBodyEnd(call, byteCount)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -261,8 +281,9 @@ public open class SentryOkHttpEventListener(
}

override fun requestFailed(call: Call, ioe: IOException) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.requestFailed(call, ioe)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -282,17 +303,19 @@ public open class SentryOkHttpEventListener(
}

override fun responseHeadersStart(call: Call) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.responseHeadersStart(call)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(RESPONSE_HEADERS_EVENT)
}

override fun responseHeadersEnd(call: Call, response: Response) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.responseHeadersEnd(call, response)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -307,17 +330,19 @@ public open class SentryOkHttpEventListener(
}

override fun responseBodyStart(call: Call) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.responseBodyStart(call)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
okHttpEvent.onEventStart(RESPONSE_BODY_EVENT)
}

override fun responseBodyEnd(call: Call, byteCount: Long) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.responseBodyEnd(call, byteCount)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Expand All @@ -330,8 +355,9 @@ public open class SentryOkHttpEventListener(
}

override fun responseFailed(call: Call, ioe: IOException) {
val originalEventListener = originalEventListenerMap[call]
originalEventListener?.responseFailed(call, ioe)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return
Comment thread
markushi marked this conversation as resolved.
Expand All @@ -351,14 +377,15 @@ public open class SentryOkHttpEventListener(
}

override fun callEnd(call: Call) {
originalEventListener?.callEnd(call)
originalEventListenerMap.remove(call)?.callEnd(call)
val okHttpEvent: SentryOkHttpEvent = eventMap.remove(call) ?: return
okHttpEvent.finish()
}

override fun callFailed(call: Call, ioe: IOException) {
val originalEventListener = originalEventListenerMap.remove(call)
originalEventListener?.callFailed(call, ioe)
if (!canCreateEventSpan()) {
if (!canCreateEventSpan(originalEventListener)) {
return
}
val okHttpEvent: SentryOkHttpEvent = eventMap.remove(call) ?: return
Expand All @@ -370,26 +397,26 @@ public open class SentryOkHttpEventListener(
}

override fun canceled(call: Call) {
originalEventListener?.canceled(call)
originalEventListenerMap[call]?.canceled(call)
Comment thread
markushi marked this conversation as resolved.
Outdated
}

override fun satisfactionFailure(call: Call, response: Response) {
originalEventListener?.satisfactionFailure(call, response)
originalEventListenerMap[call]?.satisfactionFailure(call, response)
}

override fun cacheHit(call: Call, response: Response) {
originalEventListener?.cacheHit(call, response)
originalEventListenerMap[call]?.cacheHit(call, response)
}

override fun cacheMiss(call: Call) {
originalEventListener?.cacheMiss(call)
originalEventListenerMap[call]?.cacheMiss(call)
}

override fun cacheConditionalHit(call: Call, cachedResponse: Response) {
originalEventListener?.cacheConditionalHit(call, cachedResponse)
originalEventListenerMap[call]?.cacheConditionalHit(call, cachedResponse)
}

private fun canCreateEventSpan(): Boolean {
private fun canCreateEventSpan(originalEventListener: EventListener?): Boolean {
// If the wrapped EventListener is ours, we shouldn't create spans, as the originalEventListener
// already did it
// In case SentryOkHttpEventListener from sentry-android-okhttp is used, the is check won't work
Expand Down
Loading
Loading