Skip to content

Commit

Permalink
Merge b1d9eab into 4d04310
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanosiano committed Apr 21, 2023
2 parents 4d04310 + b1d9eab commit f08b219
Show file tree
Hide file tree
Showing 10 changed files with 1,101 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- Add OkHttp event spans ([#2659](https://github.com/getsentry/sentry-java/pull/2659))
- Attach Trace Context when an ANR is detected (ANRv1) ([#2583](https://github.com/getsentry/sentry-java/pull/2583))
- Make log4j2 integration compatible with log4j 3.0 ([#2634](https://github.com/getsentry/sentry-java/pull/2634))
- Instead of relying on package scanning, we now use an annotation processor to generate `Log4j2Plugins.dat`
Expand Down
34 changes: 34 additions & 0 deletions sentry-android-okhttp/api/sentry-android-okhttp.api
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@ public final class io/sentry/android/okhttp/BuildConfig {
public fun <init> ()V
}

public final class io/sentry/android/okhttp/SentryOkHttpEventListener : okhttp3/EventListener {
public static final field Companion Lio/sentry/android/okhttp/SentryOkHttpEventListener$Companion;
public fun <init> ()V
public fun <init> (Lio/sentry/IHub;)V
public synthetic fun <init> (Lio/sentry/IHub;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun callEnd (Lokhttp3/Call;)V
public fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V
public fun callStart (Lokhttp3/Call;)V
public fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V
public fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V
public fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V
public fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V
public fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V
public fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V
public fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V
public fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V
public fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V
public fun requestBodyEnd (Lokhttp3/Call;J)V
public fun requestBodyStart (Lokhttp3/Call;)V
public fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V
public fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V
public fun requestHeadersStart (Lokhttp3/Call;)V
public fun responseBodyEnd (Lokhttp3/Call;J)V
public fun responseBodyStart (Lokhttp3/Call;)V
public fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V
public fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V
public fun responseHeadersStart (Lokhttp3/Call;)V
public fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V
public fun secureConnectStart (Lokhttp3/Call;)V
}

public final class io/sentry/android/okhttp/SentryOkHttpEventListener$Companion {
}

public final class io/sentry/android/okhttp/SentryOkHttpInterceptor : io/sentry/IntegrationName, okhttp3/Interceptor {
public fun <init> ()V
public fun <init> (Lio/sentry/IHub;)V
Expand Down
1 change: 1 addition & 0 deletions sentry-android-okhttp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ dependencies {
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))

// tests
testImplementation(projects.sentryTestSupport)
testImplementation(Config.Libs.okhttp)
testImplementation(Config.TestLibs.kotlinTestJunit)
testImplementation(Config.TestLibs.androidxJunit)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package io.sentry.android.okhttp

import io.sentry.Breadcrumb
import io.sentry.Hint
import io.sentry.IHub
import io.sentry.ISpan
import io.sentry.SpanStatus
import io.sentry.TypeCheckHint
import io.sentry.util.UrlUtils
import okhttp3.Call
import okhttp3.Response
import java.net.URL

private val uuidRegex by lazy { Regex("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}") }

internal class SentryOkHttpEvent(private val hub: IHub, private val call: Call) {
private val eventSpans: MutableMap<String, ISpan> = HashMap()
private val breadcrumb: Breadcrumb
internal val callRootSpan: ISpan?
private var response: Response? = null

init {
val urlDetails = UrlUtils.parse(call.request().url.toString())
val url = urlDetails.urlOrFallback
val trimmedUrl: String = trimUrl(url)
val host: String = call.request().url.host
val encodedPath: String = call.request().url.encodedPath
val method: String = call.request().method

// We start the call span that will contain all the others
callRootSpan = hub.span?.startChild("http.client", "$method $url")

// We setup a breadcrumb with all meaningful data
breadcrumb = Breadcrumb.http(url, method)
breadcrumb.setData("url", url)
breadcrumb.setData("filtered_url", trimmedUrl)
breadcrumb.setData("host", host)
breadcrumb.setData("path", encodedPath)
breadcrumb.setData("method", method)
breadcrumb.setData("success", true)

// We add the same data to the root call span
callRootSpan?.setData("url", url)
callRootSpan?.setData("filtered_url", trimmedUrl)
callRootSpan?.setData("host", host)
callRootSpan?.setData("path", encodedPath)
callRootSpan?.setData("method", method)
callRootSpan?.setData("success", true)
}

private fun trimUrl(url: String): String {
// Remove any uuid from the url and replace it with a "*"
val trimmedUrl = url.replace(uuidRegex, "*")
if (URL(trimmedUrl).query == null) {
return trimmedUrl
}
// Remove any parameter from the url
return trimmedUrl.replace(URL(trimmedUrl).query, "").replace("?", "")
}

/**
* Sets the [Response] that will be sent in the breadcrumb [Hint].
* Also, it sets the protocol and status code in the breadcrumb and the call root span.
*/
fun setResponse(response: Response) {
this.response = response
breadcrumb.setData("protocol", response.protocol.name)
breadcrumb.setData("status_code", response.code)
callRootSpan?.setData("protocol", response.protocol.name)
callRootSpan?.setData("status_code", response.code)
callRootSpan?.status = SpanStatus.fromHttpStatusCode(response.code)
}

fun setProtocol(protocolName: String?) {
if (protocolName != null) {
breadcrumb.setData("protocol", protocolName)
callRootSpan?.setData("protocol", protocolName)
}
}

fun setRequestBodySize(byteCount: Long) {
if (byteCount > -1) {
breadcrumb.setData("request_body_size", byteCount)
callRootSpan?.setData("request_body_size", byteCount)
}
}

fun setResponseBodySize(byteCount: Long) {
if (byteCount > -1) {
breadcrumb.setData("response_body_size", byteCount)
callRootSpan?.setData("response_body_size", byteCount)
}
}

/**
* Sets the success flag in the breadcrumb and the call root span to false.
* Also sets the [errorMessage] if not null.
*/
fun setError(errorMessage: String?) {
breadcrumb.setData("success", false)
callRootSpan?.setData("success", false)
if (errorMessage != null) {
breadcrumb.setData("error_message", errorMessage)
callRootSpan?.setData("error_message", errorMessage)
}
}

/** Starts a span, if the callRootSpan is not null. */
fun startSpan(event: String) {
val span = callRootSpan?.startChild("http.client", event) ?: return
eventSpans[event] = span
}

/** Finishes a previously started span, and runs [beforeFinish] on it and on the call root span. */
fun finishSpan(event: String, beforeFinish: ((span: ISpan) -> Unit)? = null) {
val span = eventSpans[event] ?: return
beforeFinish?.invoke(span)
callRootSpan?.let { beforeFinish?.invoke(it) }
span.finish()
}

/** Finishes the call root span, and runs [beforeFinish] on it. Then a breadcrumb is sent. */
fun finishEvent(beforeFinish: ((span: ISpan) -> Unit)? = null) {
callRootSpan ?: return

// We forcefully finish all spans, even if they should already have been finished through finishSpan()
eventSpans.values.filter { !it.isFinished }.forEach { it.finish(SpanStatus.DEADLINE_EXCEEDED) }
beforeFinish?.invoke(callRootSpan)
callRootSpan.finish()

// We put data in the hint and send a breadcrumb
val hint = Hint()
hint.set(TypeCheckHint.OKHTTP_REQUEST, call.request())
response?.let { hint.set(TypeCheckHint.OKHTTP_RESPONSE, it) }

hub.addBreadcrumb(breadcrumb, hint)
return
}
}
Loading

0 comments on commit f08b219

Please sign in to comment.