[Flutter] Add network request/response logging (manual + automatic) - #1187
Open
iainfinlayson wants to merge 4 commits into
Open
iainfinlayson wants to merge 4 commits into
iainfinlayson wants to merge 4 commits into
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
platform/capture_flutter/android/build.gradle.kts and
examples/flutter/android/app/build.gradle.kts each declare a
`kotlin { compilerOptions { jvmTarget = ... } }` block but never apply
org.jetbrains.kotlin.android in their own plugins{} block. Gradle only
generates the typed `kotlin {}` accessor for plugins applied within that
same script's plugins{} block, not for ones applied transitively (e.g. by
the Flutter Gradle plugin), so both modules failed to compile:
Unresolved reference 'compilerOptions' / 'jvmTarget'
Since capture_flutter's own Kotlin plugin module never compiled, any
Android app depending on it failed with:
cannot find symbol: class CaptureFlutterPlugin
Also pins a Kotlin Gradle plugin version (2.3.20, matching platform/jvm)
in the example app's settings.gradle.kts, since neither module declared
one and Flutter's minimum supported Kotlin version (1.8.10) is newer than
Gradle's own default.
Verified: `flutter build apk --release` succeeds in examples/flutter
against this branch (previously failed on main).
Adds Capture.logNetworkRequest / Capture.logNetworkResponse to the alpha Flutter plugin, bridging to the native HttpRequestInfo/HttpResponseInfo APIs on both Android and iOS. Fills one of the "Not Supported Yet" gaps called out for the alpha (no HttpRequestInfo/HttpResponseInfo equivalent was exposed). - lib/src/network.dart: HttpRequestInfo, HttpResponse, HttpRequestMetrics, HttpUrlPath, HttpResult, with a dependency-free UUID v4 generator for spanId (no new pub package needed). - Android/iOS bridges reconstruct the request/response as native value types per call (stateless — no live-object map to leak), matching field shapes 1:1 against the native source. Durations/metrics are milliseconds throughout the Dart API; the iOS bridge converts to TimeInterval seconds. - Also fixes a drift bug: the SPM copy of the iOS plugin (ios/capture_flutter/Sources/...) was missing the setEntityId/ clearEntityId handlers that a prior PR (#1178) only added to the CocoaPods copy (ios/Classes/...). Both copies are now identical again. - examples/flutter: adds a real "Network Request (ping google)" button (no other example app in this repo — Android, iOS, or any bitdrift-shop variant — demonstrates the manual network API interactively; all rely on automatic OkHttp/URLSession instrumentation instead, which Flutter has no equivalent of). Also fixes a separate pre-existing bug: the release build type had no signingConfig, so `flutter build apk --release` produced a genuinely unsigned, uninstallable APK. Verified end-to-end on both platforms: real GET to google.com logged via the new API, confirmed in the bitdrift session timeline with correct _status_code, _result, _duration_ms, and _request.* correlation fields. Stacked on the pending Kotlin-plugin build fix (this branch's parent).
Adds Capture.enableNetworkInstrumentation(), which installs a dart:io
HttpOverrides that automatically logs every request/response made
through the default HTTP stack -- no code changes needed at each call
site, unlike the manual logNetworkRequest/logNetworkResponse API added
earlier in this branch.
- lib/src/network_instrumentation.dart: wraps HttpClient/
HttpClientRequest/HttpClientResponse, reconstructed against the exact
dart:io interfaces (read directly from the local Dart SDK source, not
from memory) to avoid subtly wrong overrides. Chains any HttpOverrides
the app already set (e.g. cert pinning) rather than clobbering it.
Reads the x-capture-path-template header (the same convention the shop
demos already use) for path-template collapsing, since automatic
instrumentation has no other way to infer one.
- Calls the exact same Capture.logNetworkRequest/logNetworkResponse Dart
API added earlier in this stack -- no native Kotlin/Swift changes at
all. Automatic and manual paths converge on the identical
handleLogNetworkRequest/handleLogNetworkResponse native handlers.
- README.md: documents both APIs plus an explicit "when automatic works
vs. when manual is required" table, mirroring the native SDKs' own
automatic-vs-manual split (OkHttp EventListener/URLSession swizzling
vs. the manual HttpRequestInfo/HttpResponseInfo API):
- Covered automatically: raw dart:io HttpClient, package:http's
default client, dio's default adapter on mobile.
- Manual required: HTTP clients that bypass dart:io (dio configured
with CronetHttpAdapter/CupertinoHttpAdapter), native SDKs making
their own requests, WebViews (a separate, not-yet-bridged
integration point), HttpClient instances created before
enableNetworkInstrumentation() runs, and networking done in an
isolate that hasn't called it.
- examples/flutter: adds a second button ("Network Request — auto") that
fires a plain HttpClient request with zero manual logging calls around
it, to demonstrate/prove the automatic path.
Verified on both platforms: real GET to google.com captured with correct
status code and byte count, with zero manual logging calls and zero
runtime exceptions on either the request-time or the (unawaited,
async) response-time logging call.
iainfinlayson
force-pushed
the
iain/fix-capture-flutter-android-kotlin-plugin
branch
from
September 4, 2026 12:13
f4a156c to
13f7860
Compare
iainfinlayson
force-pushed
the
iain/flutter-network-logging
branch
from
September 4, 2026 12:13
55ea79a to
33a7ee2
Compare
Contributor
Author
|
I have read the CLA Document and I hereby sign the CLA |
Base automatically changed from
iain/fix-capture-flutter-android-kotlin-plugin
to
main
September 4, 2026 13:12
3 tasks
…nifest (#1199) Keeps this branch on the pre-Built-in-Kotlin toolchain (Flutter 3.10+/Dart 3.0+) while still tracking the latest native Android/iOS Capture SDK releases, so customers who can't yet adopt Flutter 3.44+ still get current SDK fixes. Also fixes the Package.swift swift-tools-version ordering bug (SPM rejects the manifest otherwise) independently of the Kotlin migration. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
Fills one of the alpha's documented gaps: no
HttpRequestInfo/HttpResponseInfoequivalent was exposed in the Flutter plugin. Adds two complementary APIs:Capture.logNetworkRequest(...)/Capture.logNetworkResponse(...), bridged to the nativeHttpRequestInfo/HttpResponseInfoAPIs on both platforms.Capture.enableNetworkInstrumentation(), adart:ioHttpOverridesthat captures requests/responses through the default HTTP stack with zero code changes at each call site — calls the exact same manual API under the hood, no new native code.Depends on #1186 (stacked on it — the example app's Android build doesn't work without that fix). Merge #1186 first; this diff will shrink once that lands.
Manual API
platform/capture_flutter/lib/src/network.dart—HttpRequestInfo,HttpResponse,HttpRequestMetrics,HttpUrlPath,HttpResult. Includes a small dependency-free UUID v4 generator forspanId.CaptureFlutterPlugin.kt) and both iOS plugin copies bridge these to native. Stateless design — each call reconstructs the request/response as a native value type rather than holding a live object across calls, since these are value types compared structurally, not by reference (no leak-prone map likeactiveSpans). Durations/metrics are milliseconds throughout the Dart API; the iOS bridge converts toTimeIntervalseconds for the native call.Automatic instrumentation
platform/capture_flutter/lib/src/network_instrumentation.dart—Capture.enableNetworkInstrumentation()installs anHttpOverrideswrappingHttpClient/HttpClientRequest/HttpClientResponse. Implemented against the exactdart:iointerfaces (read directly from the local Dart SDK source, not from memory, to avoid subtly wrong overrides). Chains anyHttpOverridesthe app already set (e.g. cert pinning) rather than clobbering it. Reads thex-capture-path-templateheader — the same convention the shop demos already use — for path-template collapsing, since automatic instrumentation has no other way to infer one.Calls the exact same
Capture.logNetworkRequest/logNetworkResponseDart API from the manual path above — no additional native Kotlin/Swift code. Both paths converge on the identicalhandleLogNetworkRequest/handleLogNetworkResponsenative handlers.README documents an explicit "when automatic works vs. when manual is required" table, mirroring the native SDKs' own split (OkHttp
EventListener/URLSessionswizzling vs. their own manualHttpRequestInfo/HttpResponseInfoAPIs):dart:io HttpClient,package:http's default client,dio's default adapter (mobile)diowithCronetHttpAdapter/CupertinoHttpAdapter, or any client bypassingdart:ioHttpOverridesHttpClientcreated beforeenableNetworkInstrumentation()x-capture-path-templateheaderHttpUrlPath(value, template:)directlyBugs found and fixed along the way
ios/capture_flutter/Sources/capture_flutter/CaptureFlutterPlugin.swift) was missingsetEntityId/clearEntityId, which [Flutter] Add setEntity/clearEntity + bump version #1178 only added to the CocoaPods copy (ios/Classes/CaptureFlutterPlugin.swift). Re-synced for now; worth consolidating the two copies eventually so this can't drift again.examples/flutter/android/app/build.gradle.kts's release build type had nosigningConfig—flutter build apk --releasesilently produced a genuinely unsigned APK (INSTALL_PARSE_FAILED_NO_CERTIFICATESon install). Fixed by signing with the debug key, matching thesa-publicshop demo.Example app
Added two buttons to
examples/flutter: "Network Request (ping google)" (manual) and "Network Request — auto (ping google)" (zero manual logging calls, relies entirely onenableNetworkInstrumentation()). Worth noting: no existing example app in this repo — Android, iOS, or anybitdrift-shopvariant — demonstrates the manual network API interactively; they all rely on automatic OkHttp/URLSession instrumentation. This seemed like the only way to actually exercise both new APIs by hand.Verification
Built, installed, and ran on both platforms (Android emulator + iOS Simulator):
HTTPRequest/HTTPResponseentries present, correct_status_code,_result,_duration_ms,_request.*correlation fields, built-in network matchers picked it up.HttpClientGET with zero manual logging calls) on both platforms — real200response with correct byte count, zero Dart-level exceptions, zero platform-channel errors, on both the synchronous request-time log call and the unawaited async response-time log call.CHANGELOG.md's "Unreleased" section has been updated, if applicable. (Flutter-specific changes go inplatform/capture_flutter/ALPHA_RELEASES.mdinstead, per its own convention — updated there for both APIs.)