-
Notifications
You must be signed in to change notification settings - Fork 7
/
EmbraceOkHttp3ApplicationInterceptor.kt
101 lines (94 loc) · 4.69 KB
/
EmbraceOkHttp3ApplicationInterceptor.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package io.embrace.android.embracesdk.okhttp3
import io.embrace.android.embracesdk.Embrace
import io.embrace.android.embracesdk.annotation.InternalApi
import io.embrace.android.embracesdk.internal.network.http.EmbraceHttpPathOverride
import io.embrace.android.embracesdk.network.EmbraceNetworkRequest
import io.embrace.android.embracesdk.network.http.HttpMethod
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
/**
* This interceptor will only intercept errors that client app experiences.
*
* We used OkHttp application interceptor in this case because this interceptor
* will be added first in the OkHttp3 interceptors stack. This allows us to catch network errors.
* OkHttp network interceptors are added almost at the end of stack, they are closer to "the wire"
* so they are not able to see network errors.
*
* We used the [EmbraceCustomPathException] to capture the custom path added in the interceptor
* chain process for client errors on requests to a generic URL like a GraphQL endpoint.
*/
@InternalApi
public class EmbraceOkHttp3ApplicationInterceptor internal constructor(
private val embrace: Embrace
) : Interceptor {
public constructor() : this(Embrace.getInstance())
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val startTime = embrace.internalInterface.getSdkCurrentTime()
val request: Request = chain.request()
return try {
// we are not interested in response, just proceed
chain.proceed(request)
} catch (e: EmbraceCustomPathException) {
if (embrace.isStarted && !embrace.internalInterface.isInternalNetworkCaptureDisabled()) {
val urlString = EmbraceHttpPathOverride.getURLString(EmbraceOkHttp3PathOverrideRequest(request), e.customPath)
embrace.recordNetworkRequest(
EmbraceNetworkRequest.fromIncompleteRequest(
urlString,
HttpMethod.fromString(request.method),
startTime,
embrace.internalInterface.getSdkCurrentTime(),
causeName(e, UNKNOWN_EXCEPTION),
causeMessage(e, UNKNOWN_MESSAGE),
request.header(embrace.traceIdHeader),
if (embrace.internalInterface.isNetworkSpanForwardingEnabled()) request.header(TRACEPARENT_HEADER_NAME) else null,
null
)
)
}
throw e
} catch (e: Exception) {
// we are interested in errors.
if (embrace.isStarted && !embrace.internalInterface.isInternalNetworkCaptureDisabled()) {
val urlString = EmbraceHttpPathOverride.getURLString(EmbraceOkHttp3PathOverrideRequest(request))
val errorType = e.javaClass.canonicalName
val errorMessage = e.message
embrace.recordNetworkRequest(
EmbraceNetworkRequest.fromIncompleteRequest(
urlString,
HttpMethod.fromString(request.method),
startTime,
embrace.internalInterface.getSdkCurrentTime(),
errorType ?: UNKNOWN_EXCEPTION,
errorMessage ?: UNKNOWN_MESSAGE,
request.header(embrace.traceIdHeader),
if (embrace.internalInterface.isNetworkSpanForwardingEnabled()) request.header(TRACEPARENT_HEADER_NAME) else null,
null
)
)
}
throw e
}
}
internal companion object {
internal const val TRACEPARENT_HEADER_NAME = "traceparent"
internal const val UNKNOWN_EXCEPTION = "Unknown"
internal const val UNKNOWN_MESSAGE = "An error occurred during the execution of this network request"
/**
* Return the canonical name of the cause of a [Throwable]. Handles null elements throughout,
* including the throwable and its cause, in which case [defaultName] is returned
*/
internal fun causeName(throwable: Throwable?, defaultName: String = ""): String {
return throwable?.cause?.javaClass?.canonicalName ?: defaultName
}
/**
* Return the message of the cause of a [Throwable]. Handles null elements throughout,
* including the throwable and its cause, in which case [defaultMessage] is returned
*/
internal fun causeMessage(throwable: Throwable?, defaultMessage: String = ""): String {
return throwable?.cause?.message ?: defaultMessage
}
}
}