Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
dd4bdfc
collection: Data Collection
adinauer Jul 14, 2026
e38fdbd
feat(core): Add Data Collection configuration types
adinauer Jul 14, 2026
c4ea3db
test(core): Remove Data Collection identity assertion
adinauer Jul 14, 2026
a337d9e
feat(core): Expose Data Collection options
adinauer Jul 14, 2026
1ad9a19
feat(core): Add Data Collection resolver
adinauer Jul 14, 2026
9c7a845
feat(graphql): Apply Data Collection options
adinauer Jul 17, 2026
be3834c
feat(database): Apply Data Collection query policy
adinauer Jul 17, 2026
d17418d
feat(spring): Apply incoming request body policy
adinauer Jul 17, 2026
fee2df4
feat(apollo): Apply incoming response body policy
adinauer Jul 17, 2026
34dcac9
feat(apollo): Apply outgoing request body policy
adinauer Jul 17, 2026
fa5bc98
feat(graphql): Apply outgoing response body policy
adinauer Jul 17, 2026
5806d4e
feat(http): Apply request header collection policy
adinauer Jul 20, 2026
09e1448
perf(core): Skip redundant header term matching
adinauer Jul 20, 2026
5553391
feat(http): Apply response header collection policy
adinauer Jul 20, 2026
12d5102
ref(core): Remove unused queue collection option
adinauer Jul 21, 2026
bf005fd
merge: Update Data Collection foundation
adinauer Jul 21, 2026
02bb426
test(core): Update Data Collection replacement coverage
adinauer Jul 21, 2026
1ef94b8
merge: Update Data Collection stack
adinauer Jul 21, 2026
5e4888e
merge: Update Data Collection stack
adinauer Jul 21, 2026
4139cb7
merge: Update Data Collection stack
adinauer Jul 21, 2026
2ce1e51
merge: Update Data Collection stack
adinauer Jul 21, 2026
3208fc6
merge: Update Data Collection stack
adinauer Jul 21, 2026
04f91e1
merge: Update Data Collection stack
adinauer Jul 21, 2026
6baa995
merge: Update Data Collection stack
adinauer Jul 21, 2026
ecd3f6e
merge: Update Data Collection stack
adinauer Jul 21, 2026
8bad568
merge: Update Data Collection stack
adinauer Jul 21, 2026
d9a1422
feat(http): Apply query parameter collection policy
adinauer Jul 22, 2026
b13daf8
ref(core): Rename URL query parameter option
adinauer Aug 10, 2026
b64b91b
Merge branch 'feat/data-collection-foundation' into feat/data-collect…
adinauer Aug 10, 2026
9b6ddb7
Merge branch 'feat/data-collection-options' into feat/data-collection…
adinauer Aug 10, 2026
6a66949
ref(core): Rename URL query parameter resolver
adinauer Aug 10, 2026
a904161
Merge branch 'feat/data-collection-resolver' into feat/data-collectio…
adinauer Aug 10, 2026
2f66a94
Merge branch 'feat/data-collection-graphql' into feat/data-collection…
adinauer Aug 10, 2026
78ec5e5
Merge branch 'feat/data-collection-database-query-data' into feat/dat…
adinauer Aug 10, 2026
ddd436d
Merge branch 'feat/data-collection-incoming-request-body' into feat/d…
adinauer Aug 10, 2026
32bb973
Merge branch 'feat/data-collection-incoming-response-body' into feat/…
adinauer Aug 10, 2026
3352d09
Merge branch 'feat/data-collection-outgoing-request-body' into feat/d…
adinauer Aug 10, 2026
29fc629
Merge branch 'feat/data-collection-outgoing-response-body' into feat/…
adinauer Aug 10, 2026
1bf10c4
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 10, 2026
e104fe1
Merge branch 'feat/data-collection-http-response-headers' into feat/d…
adinauer Aug 10, 2026
11e4cc0
ref(http): Use URL query parameter option name
adinauer Aug 10, 2026
a40c2e4
fix(core): Preserve Data Collection on null setter
adinauer Aug 10, 2026
9bce9f3
Merge branch 'feat/data-collection-options' into feat/data-collection…
adinauer Aug 10, 2026
9f37039
Merge branch 'feat/data-collection-resolver' into feat/data-collectio…
adinauer Aug 10, 2026
79f9353
Merge branch 'feat/data-collection-graphql' into feat/data-collection…
adinauer Aug 10, 2026
f6ca83a
Merge branch 'feat/data-collection-database-query-data' into feat/dat…
adinauer Aug 10, 2026
12c91a3
Merge branch 'feat/data-collection-incoming-request-body' into feat/d…
adinauer Aug 10, 2026
835957f
Merge branch 'feat/data-collection-incoming-response-body' into feat/…
adinauer Aug 10, 2026
8dedc6f
Merge branch 'feat/data-collection-outgoing-request-body' into feat/d…
adinauer Aug 10, 2026
2c8ce2d
Merge branch 'feat/data-collection-outgoing-response-body' into feat/…
adinauer Aug 10, 2026
33c5bf4
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 10, 2026
8197fd3
Merge branch 'feat/data-collection-http-response-headers' into feat/d…
adinauer Aug 10, 2026
b5030a5
test(apollo): Cover response header filtering
adinauer Aug 24, 2026
6fbe36c
merge: Update Data Collection stack
adinauer Aug 24, 2026
3d27590
fix(opentelemetry): Preserve completed request headers
adinauer Aug 27, 2026
2e27f7f
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 27, 2026
0f4240b
Merge branch 'feat/data-collection-http-response-headers' into feat/d…
adinauer Aug 27, 2026
9044470
fix(opentelemetry): Preserve completed URL attributes
adinauer Aug 27, 2026
f1f23e1
fix(database): Preserve query descriptions
adinauer Aug 28, 2026
5e6df6a
Merge branch 'feat/data-collection-database-query-data' into feat/dat…
adinauer Aug 28, 2026
e2e780e
Merge branch 'feat/data-collection-incoming-request-body' into feat/d…
adinauer Aug 28, 2026
b574464
Merge branch 'feat/data-collection-incoming-response-body' into feat/…
adinauer Aug 28, 2026
6566174
Merge branch 'feat/data-collection-outgoing-request-body' into feat/d…
adinauer Aug 28, 2026
3d8e910
Merge branch 'feat/data-collection-outgoing-response-body' into feat/…
adinauer Aug 28, 2026
c90ffb3
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 28, 2026
ca96df4
Merge branch 'feat/data-collection-http-response-headers' into feat/d…
adinauer Aug 28, 2026
3a6fa4f
test(graphql): Cover GraphqlUtils request body filtering
adinauer Aug 31, 2026
d0020cf
fix(graphql): Filter batched GraphQL request bodies
adinauer Aug 31, 2026
01fb6b8
test(graphql): Cover malformed batched request body entries
adinauer Aug 31, 2026
301c20f
Merge branch 'feat/data-collection-graphql' into feat/data-collection…
adinauer Aug 31, 2026
89ce079
Merge branch 'feat/data-collection-database-query-data' into feat/dat…
adinauer Aug 31, 2026
1ca5ecb
Merge branch 'feat/data-collection-incoming-request-body' into feat/d…
adinauer Aug 31, 2026
66b0141
Merge branch 'feat/data-collection-incoming-response-body' into feat/…
adinauer Aug 31, 2026
aa9b4ab
Merge branch 'feat/data-collection-outgoing-request-body' into feat/d…
adinauer Aug 31, 2026
3c74442
Merge branch 'feat/data-collection-outgoing-response-body' into feat/…
adinauer Aug 31, 2026
976de46
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 31, 2026
c1f1de2
Merge branch 'feat/data-collection-http-response-headers' into feat/d…
adinauer Aug 31, 2026
8afc27f
ref(core): Clarify forced Data Collection configuration
adinauer Aug 31, 2026
619bee6
Merge branch 'feat/data-collection-foundation' into feat/data-collect…
adinauer Aug 31, 2026
e9df1c2
Merge branch 'feat/data-collection-options' into feat/data-collection…
adinauer Aug 31, 2026
140a666
test(core): Clarify Data Collection resolver scenarios
adinauer Aug 31, 2026
c7a9cbb
Merge branch 'feat/data-collection-resolver' into feat/data-collectio…
adinauer Aug 31, 2026
78e33b6
Merge branch 'feat/data-collection-graphql' into feat/data-collection…
adinauer Aug 31, 2026
6ea6d11
Merge branch 'feat/data-collection-database-query-data' into feat/dat…
adinauer Aug 31, 2026
13f738d
Merge branch 'feat/data-collection-incoming-request-body' into feat/d…
adinauer Aug 31, 2026
7a9630f
Merge branch 'feat/data-collection-incoming-response-body' into feat/…
adinauer Aug 31, 2026
b884d74
Merge branch 'feat/data-collection-outgoing-request-body' into feat/d…
adinauer Aug 31, 2026
f741f4a
Merge branch 'feat/data-collection-outgoing-response-body' into feat/…
adinauer Aug 31, 2026
f6fa18c
test(apollo): Cover request header filtering in Apollo 4
adinauer Aug 31, 2026
7f7ca0e
Merge branch 'feat/data-collection-http-request-headers' into feat/da…
adinauer Aug 31, 2026
a78374a
feat(core): [Data Collection 1] Add configuration types (#5760)
adinauer Sep 2, 2026
c4a43d9
Merge pull request #5761 from getsentry/feat/data-collection-options
adinauer Sep 2, 2026
160ae58
Merge pull request #5799 from getsentry/feat/data-collection-resolver
adinauer Sep 2, 2026
45b2728
Merge pull request #5800 from getsentry/feat/data-collection-graphql
adinauer Sep 2, 2026
77067c9
Merge pull request #5801 from getsentry/feat/data-collection-database…
adinauer Sep 2, 2026
34b69d0
Merge pull request #5802 from getsentry/feat/data-collection-incoming…
adinauer Sep 2, 2026
0d69665
Merge pull request #5803 from getsentry/feat/data-collection-incoming…
adinauer Sep 2, 2026
f665ca5
Merge pull request #5804 from getsentry/feat/data-collection-outgoing…
adinauer Sep 2, 2026
b86cbff
Merge pull request #5805 from getsentry/feat/data-collection-outgoing…
adinauer Sep 2, 2026
6347a0b
Merge pull request #5806 from getsentry/feat/data-collection-http-req…
adinauer Sep 2, 2026
14a445d
Merge pull request #5807 from getsentry/feat/data-collection-http-res…
adinauer Sep 2, 2026
86e38ab
Merge pull request #5810 from getsentry/feat/data-collection-query-pa…
adinauer Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sentry-apollo-3/api/sentry-apollo-3.api
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public final class io/sentry/apollo3/SentryApollo3HttpInterceptor$Companion {

public final class io/sentry/apollo3/SentryApollo3Interceptor : com/apollographql/apollo3/interceptor/ApolloInterceptor {
public fun <init> ()V
public fun <init> (Lio/sentry/IScopes;)V
public synthetic fun <init> (Lio/sentry/IScopes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun intercept (Lcom/apollographql/apollo3/api/ApolloRequest;Lcom/apollographql/apollo3/interceptor/ApolloInterceptorChain;)Lkotlinx/coroutines/flow/Flow;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.sentry.exception.ExceptionMechanismException
import io.sentry.protocol.Mechanism
import io.sentry.protocol.Request
import io.sentry.protocol.Response
import io.sentry.util.GraphqlUtils
import io.sentry.util.HttpUtils
import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion
import io.sentry.util.Platform
Expand Down Expand Up @@ -159,7 +160,7 @@ constructor(
operationType: String?,
operationId: String?,
): ISpan {
val urlDetails = UrlUtils.parse(request.url)
val urlDetails = UrlUtils.parse(request.url, scopes.options.dataCollectionResolver)
val method = request.method.name

val operation = if (operationType != null) "http.graphql.$operationType" else "http.graphql"
Expand All @@ -174,7 +175,9 @@ constructor(

operationId?.let { setData("operationId", it) }

variables?.let { setData("variables", it) }
if (scopes.options.dataCollectionResolver.isGraphqlVariablesWithLegacyAlways) {
variables?.let { setData("variables", it) }
}
setData(HTTP_METHOD_KEY, method.uppercase())
}
}
Expand Down Expand Up @@ -229,7 +232,13 @@ constructor(
span.finish()
}

val breadcrumb = Breadcrumb.http(request.url, request.method.name, statusCode)
val breadcrumb =
Breadcrumb.http(
request.url,
request.method.name,
statusCode,
scopes.options.dataCollectionResolver,
)

request.body?.contentLength.ifHasValidLength { contentLength ->
breadcrumb.setData("request_body_size", contentLength)
Expand Down Expand Up @@ -261,6 +270,36 @@ constructor(
private fun getHeader(key: String, headers: List<HttpHeader>): String? =
headers.firstOrNull { it.name.equals(key, true) }?.value

private fun getRequestHeaders(headers: List<HttpHeader>): MutableMap<String, String>? {
if (scopes.options.dataCollectionResolver.isDataCollectionConfigured) {
val requestHeaders = mutableMapOf<String, String>()
for (header in headers) {
requestHeaders[header.name] = header.value
}
return HttpUtils.filterHeaders(
requestHeaders,
scopes.options.dataCollectionResolver.httpRequestHeaders,
)
.toMutableMap()
}
return getHeaders(headers)
}

private fun getResponseHeaders(headers: List<HttpHeader>): MutableMap<String, String>? {
if (scopes.options.dataCollectionResolver.isDataCollectionConfigured) {
val responseHeaders = mutableMapOf<String, String>()
for (header in headers) {
responseHeaders[header.name] = header.value
}
return HttpUtils.filterHeaders(
responseHeaders,
scopes.options.dataCollectionResolver.httpResponseHeaders,
)
.toMutableMap()
}
return getHeaders(headers)
}

private fun getHeaders(headers: List<HttpHeader>): MutableMap<String, String>? {
// Headers are only sent if isSendDefaultPii is enabled due to PII
if (!scopes.options.isSendDefaultPii) {
Expand Down Expand Up @@ -318,7 +357,7 @@ constructor(
// url will be: https://api.github.com/users/getsentry/repos/
// ideally we'd like a parameterized url: https://api.github.com/users/{user}/repos/
// but that's not possible
val urlDetails = UrlUtils.parse(request.url)
val urlDetails = UrlUtils.parse(request.url, scopes.options.dataCollectionResolver)

// return if its not a target match
if (!PropagationTargetsUtils.contain(failedRequestTargets, urlDetails.urlOrFallback)) {
Expand Down Expand Up @@ -356,22 +395,24 @@ constructor(
cookies =
if (scopes.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null
method = request.method.name
headers = getHeaders(request.headers)
headers = getRequestHeaders(request.headers)
apiTarget = "graphql"

request.body?.let {
bodySize = it.contentLength

val buffer = Buffer()

try {
it.writeTo(buffer)
data = buffer.readUtf8()
} catch (e: Throwable) {
scopes.options.logger.log(SentryLevel.ERROR, "Error reading the request body.", e)
// continue because the response body alone can already give some insights
} finally {
buffer.close()
if (scopes.options.dataCollectionResolver.isOutgoingRequestBody) {
val buffer = Buffer()

try {
it.writeTo(buffer)
data = GraphqlUtils.filterRequestBody(buffer.readUtf8(), scopes.options)
} catch (e: Throwable) {
scopes.options.logger.log(SentryLevel.ERROR, "Error reading the request body.", e)
// continue because the response body alone can already give some insights
} finally {
buffer.close()
}
}
}
}
Expand All @@ -385,13 +426,15 @@ constructor(
} else {
null
}
headers = getHeaders(response.headers)
headers = getResponseHeaders(response.headers)
statusCode = response.statusCode

response.body?.buffer?.size?.ifHasValidLength { contentLength ->
bodySize = contentLength
}
data = body
if (scopes.options.dataCollectionResolver.isIncomingResponseBody) {
data = body
}
}

fingerprints.add(response.statusCode.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import com.apollographql.apollo3.api.Subscription
import com.apollographql.apollo3.api.variables
import com.apollographql.apollo3.interceptor.ApolloInterceptor
import com.apollographql.apollo3.interceptor.ApolloInterceptorChain
import io.sentry.IScopes
import io.sentry.ScopesAdapter
import io.sentry.apollo3.SentryApollo3HttpInterceptor.Companion.SENTRY_APOLLO_3_OPERATION_TYPE
import io.sentry.apollo3.SentryApollo3HttpInterceptor.Companion.SENTRY_APOLLO_3_VARIABLES
import io.sentry.vendor.Base64
import kotlinx.coroutines.flow.Flow

class SentryApollo3Interceptor : ApolloInterceptor {
class SentryApollo3Interceptor
@JvmOverloads
constructor(private val scopes: IScopes = ScopesAdapter.getInstance()) : ApolloInterceptor {
override fun <D : Operation.Data> intercept(
request: ApolloRequest<D>,
chain: ApolloInterceptorChain,
Expand All @@ -28,14 +32,16 @@ class SentryApollo3Interceptor : ApolloInterceptor {
Base64.encodeToString(operationType(request).toByteArray(), Base64.NO_WRAP),
)

request.scalarAdapters?.let {
builder.addHttpHeader(
SENTRY_APOLLO_3_VARIABLES,
Base64.encodeToString(
request.operation.variables(it).valueMap.toString().toByteArray(),
Base64.NO_WRAP,
),
)
if (scopes.options.dataCollectionResolver.isGraphqlVariablesWithLegacyAlways) {
request.scalarAdapters?.let {
builder.addHttpHeader(
SENTRY_APOLLO_3_VARIABLES,
Base64.encodeToString(
request.operation.variables(it).valueMap.toString().toByteArray(),
Base64.NO_WRAP,
),
)
}
}
return chain.proceed(builder.build())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fun ApolloClient.Builder.sentryTracing(
failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS),
beforeSpan: SentryApollo3HttpInterceptor.BeforeSpanCallback? = null,
): ApolloClient.Builder {
addInterceptor(SentryApollo3Interceptor())
addInterceptor(SentryApollo3Interceptor(scopes))
addHttpInterceptor(
SentryApollo3HttpInterceptor(
scopes = scopes,
Expand Down
Loading