Skip to content

Conversation

@TabooSun
Copy link

@TabooSun TabooSun commented Nov 23, 2025

💡 Motivation and Context

Close #169

Didn't manage to test the code because when I check the Posthog dashboard, none of the events are recorded in the Activity pane, not sure if it is filtered, might need help here.

💚 How did you test it?

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

Copilot AI review requested due to automatic review settings November 23, 2025 14:24
@TabooSun TabooSun requested a review from a team as a code owner November 23, 2025 14:24
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Dio interceptor to enable automatic capturing of network events for session replay and network performance monitoring with PostHog. The interceptor integrates with Dio's request/response lifecycle to track HTTP requests and responses.

  • Adds PosthogDioInterceptor class that extends Dio's Interceptor
  • Captures network events on both successful responses and errors
  • Transforms request/response data into PostHog-compatible snapshot events
  • Adds Dio as a direct dependency

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
pubspec.yaml Adds Dio 5.9.0 as a new dependency to support the network interceptor
lib/src/replay/network_performance/dio_interceptor.dart Implements the PosthogDioInterceptor class that captures network events from Dio requests/responses and sends them to PostHog as snapshot events

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pubspec.yaml Outdated
# plugin_platform_interface depends on meta anyway
meta: ^1.3.0
stack_trace: ^1.12.0
dio: ^5.9.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has to be a new package within the posthog-flutter repo so users that dont use dio dont need to have this as a dependency

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

import 'package:dio/dio.dart';
import 'package:posthog_flutter/posthog_flutter.dart';

class PosthogDioInterceptor extends Interceptor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class PosthogDioInterceptor extends Interceptor {
class PostHogDioInterceptor extends Interceptor {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment on lines 48 to 49
if (publishableRequest != null) 'request': publishableRequest,
if (publishableResponse != null) 'response': publishableResponse,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should skip this for now, or we should check its body size and type before appending to the properties.
events that are large will be dropped, and it has to be json serializable, so better to skip for a 1st version

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Size concern: May I know what's the size limit here? If you let me know, I can drop it.
  • json serializable ensurance: actually _tryTransformDataToPublishableObject has already transformed them to publishable object that is ensured to be json serializable

Kinda need this for my usage, so if possible I plan to have it in the 1st version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinda need this for my usage, so if possible I plan to have it in the 1st version.

Expose a ctor param when creating a PosthogDioInterceptor instance, so users can optin/out of attaching payloads.
like attachBodies or attachPayloads with default false due to PII risk

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size concern: May I know what's the size limit here? If you let me know, I can drop it.

lets dow hat the JS SDK does
https://github.com/PostHog/posthog-js/blob/962649dcc87ff67da2e80e7dfb776020b903bc7b/packages/browser/src/extensions/replay/external/config.ts#L130-L170
you can inspire from this implementation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

},
'timestamp': DateTime.now().millisecondsSinceEpoch,
};
await Posthog().capture(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you dont need to await this, so you can remove the Future<void> from this method

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need it after the latest change.

Response<dynamic> response,
ResponseInterceptorHandler handler,
) async {
await _captureNetworkEvent(
Copy link
Member

@marandaneto marandaneto Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should only call this method if (check ScreenshotCapturer):

final isSessionReplayActive =
            await _nativeCommunicator.isSessionReplayActive();

so we dont try to capture snapshot network requests if replay is deactivated, this method is private, we'd need to expose internally at least

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@marandaneto
Copy link
Member

Didn't manage to test the code because when I check the Posthog dashboard, none of the events are recorded in the Activity pane, not sure if it is filtered, might need help here.

they are not analytics events, they are session replay metata, it wont appear in the Activity page, they will be part of a recording in the session replay recordings

@marandaneto marandaneto requested a review from ioannisj November 24, 2025 08:18
) async {
final Response<dynamic>? response = err.response;
if (response != null) {
await _captureNetworkEvent(response: response);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are checking response here, but I think this will miss network connection errors, connection timeouts etc which may not have a response object returned

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, isn't this supposed to be captured by the Error Tracking automatically (unless user catch the error)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but not with the same level of details as you have here (you have more context in this method).
what you could do is: try catch, read what you need and rethrow the error.

Copy link
Author

@TabooSun TabooSun Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think we should rethrow the error here, the interceptor is supposed to just send whatever we want here.

If we throw here, the next interceptor that user configures will not be called.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if _captureNetworkEvent throws, we wont catch/log anything.
if we try catch and rethrow, this is the behaviour that would happen anyway.
same approach i've done before https://github.com/getsentry/sentry-dart/blob/20faa47cb637900f4a6112a89bd71419fd7bc16d/packages/dio/lib/src/tracing_client_adapter.dart#L68-L80

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused about the comment after a few replies. Let me try and restructure first.

So, initially the ask is to handle the errors from the overriding method: onError, in which it contains network connection errors, connection timeouts etc. I didn't want to handle it because I feel that this is supposed to be handled by the Error Tracking.

Then I was asked to catch the error here:

yes but not with the same level of details as you have here (you have more context in this method).
what you could do is: try catch, read what you need and rethrow the error.

which is weird because technically this is different ask compared to the original handling of the DioException from the onError method.

If we throw here, the next interceptor that user configures will not be called.

This will no longer be true after my latest changes of moving the call of _captureNetworkEvent after super.onError.


All in all, I don't mind handling the err from onError but I need a sample of implementing it, do I just send the stack trace along or just the error message? If we want stack trace then I still think it's better to use Posthog.captureException as this is the easiest way for me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a misunderstanding here.

We're not talking about onError
We're talking about being able to create a network request snapshot even if there's an error.
Not talking about network errors, but rather API errors eg 4xx or 5xx, you still want network request snapshots.
If dio throws an exception in this use case, we have to try-catch-rethrow so you are able to do so.

@TabooSun
Copy link
Author

Didn't manage to test the code because when I check the Posthog dashboard, none of the events are recorded in the Activity pane, not sure if it is filtered, might need help here.

they are not analytics events, they are session replay metata, it wont appear in the Activity page, they will be part of a recording in the session replay recordings

Still don't see it, this is the sample data I observe from Proxyman:

{
  "api_key": "",
  "uuid": "",
  "properties": {
    "$window_id": "",
    "$snapshot_data": {
      "type": 6,
      "data": {
        "payload": {
          "method": "POST",
          "status_code": 200,
          "url": "",
          "request": {
          },
          "response": {
          }
        },
        "plugin": "rrweb/network@1"
      },
      "timestamp": 1763992397179
    },
    "$snapshot_source": "mobile",
    "$lib": "posthog-flutter",
    "distinct_id": "",
    "$lib_version": "5.9.0",
    "$screen_name": "",
    "$session_id": "019AB623-E12C-7C70-A9DD-53B345EE89BF"
  },
  "event": "$snapshot",
  "timestamp": "2025-11-24T13:53:17.221Z",
  "distinct_id": ""
}
CleanShot 2025-11-24 at 22 15 07@2x


# Add regular dependencies here.
dependencies:
dio: ^5.9.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dio: ^5.9.0
dio: ^5.0.0

if we dont use anything new, we can target the major instead of the latest

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

# Add regular dependencies here.
dependencies:
dio: ^5.9.0
posthog_flutter: ^5.9.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
posthog_flutter: ^5.9.0
posthog_flutter: ^5.0.0

same here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Comment on lines 66 to 81
final hasExceededLimit = await _hasExceededSizeLimit(
data: response.requestOptions.data,
header: response.requestOptions.headers,
);
return (
value,
hasExceededLimit,
);
},
),
if (attachPayloads)
_tryTransformDataToPublishableObject(
data: response.data,
).then(
(value) async {
final hasExceededLimit = await _hasExceededSizeLimit(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have to sum the request payload + response payload to tell if hasExceededLimit or not since what matters is the size of the final posthog event built here snapshotData

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


/// A Dio interceptor that captures network events and sends them to PostHog.
class PostHogDioInterceptor extends Interceptor {
final NativeCommunicator _nativeCommunicator = NativeCommunicator();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of creating a new instance for this, we should expose and reuse this class

final _nativeCommunicator = NativeCommunicator();

example: https://github.com/PostHog/posthog-android/blob/743a341365f5d9c1cf254a7b01882b59c3089e30/posthog/src/main/java/com/posthog/PostHog.kt#L1096-L1102
we expose a public method in the posthog class so we can call it directly from here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@marandaneto
Copy link
Member

now i think our example should depend on the new package and add a button in the sample for testing
also we should fix the release process and CI now https://github.com/PostHog/posthog-flutter/tree/main/.github/workflows to consider the new package
maybe the current package should move under the packages folder as well since its a mono repo with multiple packages

@TabooSun
Copy link
Author

now i think our example should depend on the new package and add a button in the sample for testing also we should fix the release process and CI now https://github.com/PostHog/posthog-flutter/tree/main/.github/workflows to consider the new package maybe the current package should move under the packages folder as well since its a mono repo with multiple packages

Done moving to package and adding button for testing. But can you help with the workflow? It seems that it requires approval, not sure if this is a one-time thing or the approval has to be granted every time, if it's latter, I think it's better that the maintainer handles this.

@marandaneto
Copy link
Member

@TabooSun I'm handling an incident now, but will follow up on comments/maybe open a PR on top of yours with suggestions later on (later this week or early next week), so we avoid the back and forth since I am focusing on other tasks that have higher priority for now, sorry about that.

@ioannisj
Copy link
Collaborator

@marandaneto ping on this one

@marandaneto
Copy link
Member

@TabooSun mind fixing conflicts after your changes? so i can do a final review/release if all good?

@TabooSun TabooSun force-pushed the add-dio-interceptor branch from 167a0d9 to 9616f39 Compare December 19, 2025 13:24
@TabooSun TabooSun force-pushed the add-dio-interceptor branch from ad638e3 to 18cb2f7 Compare December 19, 2025 13:37
@TabooSun
Copy link
Author

@TabooSun mind fixing conflicts after your changes? so i can do a final review/release if all good?

Hi, updated~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session replay Network performance recording

3 participants