Skip to content

Support Cronet HTTP instrumentation and network timings #6228

Description

@sentry-junior

Direct Cronet usage currently gets no out-of-the-box network metrics from Sentry. In OkHttp-over-Cronet setups where Sentry's interceptor runs, the HTTP request is captured, but DNS/connect/TLS timings are missing. Some OkHttp bridges also prevent the interceptor from running, which means trace propagation and failed-request capture are missing too. Cronet's RequestFinishedInfo.Metrics is the source for transport timings, so OkHttp-side instrumentation alone can't fill that gap.

Raised by an Android SDK user who runs OkHttp over Cronet and currently uses reflection on SentryOkHttpEventListener internals (eventMap, internal attribute keys) to attach Cronet timings to the Sentry span.

Current behavior

  • Direct Cronet: there is no out-of-the-box Sentry network instrumentation, so no request-level network metrics are captured.
  • OkHttp over Cronet, with Sentry's interceptor reached: the HTTP request is captured, but Cronet-owned DNS/connect/TLS details are not.
  • Google's cronet-transport-for-okhttp (and forks of it): CronetInterceptor is the last application interceptor and never calls chain.proceed. SAGP appends SentryOkHttpInterceptor after app interceptors (ResponseWithInterceptorChainMethodVisitor.kt), so it never runs: no trace headers, no failed-request capture. SentryOkHttpEventListener only sees callStart/callEnd.
  • Bridges that swap transport at ConnectInterceptor: our interceptor runs and header/body events may be replayed, but dnsStart, connectStart, secureConnectStart and connectionAcquired never fire because Cronet owns the socket.
  • Other Cronet wrappers: no Sentry Cronet integration exists today.

Why OkHttp-side changes aren't enough

  • EventListener callbacks carry no timestamps and SentryOkHttpEvent stamps them with now(), so a bridge replaying DNS/connect/TLS events after the fact would record ~0ms durations.
  • Moving SentryOkHttpInterceptor to the front of the chain globally isn't viable: it would record the request before other interceptors modify it (auth headers, URL rewrites) and change span semantics for every OkHttp app.

Cronet findings (source reading only, not validated on device)

  • UrlRequest.Builder.addRequestAnnotation, UrlRequest.Builder.setRequestFinishedListener and CronetEngine.addRequestFinishedListener are in the base API since cronet-api 113; older versions need the Experimental* classes.
  • These base methods are silent no-ops on providers that don't implement them (e.g. the fallback Java provider), so missing support shows up as a callback that never arrives.
  • Embedded Cronet: engine-level and per-request listeners, annotations and metrics all work (CronetUrlRequest.maybeReportMetrics).
  • Platform HttpEngine (Android 14+): listeners and annotations work, but getMetrics() returns CronetMetrics.empty() (AndroidRequestFinishedInfoWrapper), so phase timings are unavailable. Default CronetProvider selection can pick HttpEngine before Play Services.
  • Play Services Cronet: closed source, unverified.
  • RequestFinishedInfo is delivered asynchronously and can arrive after the OkHttp span has finished (Google's bridge doesn't wait for it).

Proposed direction (from discussion, not final)

  • A public API to record transport timings on an http.client span using absolute timestamps (DNS, connect, TLS, send, TTFB) plus protocol, socketReused, QUIC error codes and wire byte counts, with Sentry-owned attribute names.
  • A sentry-cronet integration covering two cases:
    • Direct Cronet: full HTTP client instrumentation, on par with the other HTTP integrations — create the http.client span, add trace headers, record request/response data (method, URL, status, sizes), breadcrumbs and failed-request capture, plus transport timings via the API above.
    • Cronet as OkHttp transport: don't create a second span; enrich the existing OkHttp span with transport timings only.
    • Correlation via a request annotation carrying the Sentry span, plus an engine-level RequestFinishedInfo.Listener (so it doesn't replace per-request listeners set by bridges) that reads it.
  • Manual wiring first (bridge mapping hooks, wrapper listener hooks); SAGP call-site instrumentation of UrlRequest.Builder.build() / CronetEngine.Builder.build() later.

Open questions

  • How to correlate a UrlRequest with the originating OkHttp Call (e.g. a thread-local set around getResponseWithInterceptorChain), given it relies on the bridge building the request on the call's thread.
  • Play Services provider support for annotations, listeners and metrics.
  • How long to hold a span open for late RequestFinishedInfo, and what to do when metrics are empty.

via roman.

--

View Junior Session [Sentry]

Activity

  1. linear-code commented on Oct 6, 2026

    @linear-code
  2. changed the title [-]Support Cronet network timings in HTTP client spans[/-] [+]Support Cronet HTTP instrumentation and network timings[/+] on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions