Skip to content
Back to archipelago.banuacoder.com

Feature SDKs (Pro)

app_debugger_analytics Pro ​

Tier
Proversion 1.3.4
Command
archipelago generate app_debugger_analytics
Generates
  • features/app_debugger_analytics/app_debugger_analytics
  • features/app_debugger_analytics/app_debugger_analytics_api
  • features/app_debugger_analytics/app_debugger_analytics_noop
Generates first
app_debuggerwhen needed and missing, so there is nothing to install first
Depends on
shared_consent, shared_identifiers, analytics_sdkgenerate these first

Analytics event inspector sub-brick for app_debugger. Adds an Analytics tool to the debugger that captures every TrackData event flowing through the AnalyticInterceptor chain, plus a consent-status banner so a build's consent state is never mistaken for "nothing is being tracked". Automatically swapped to a no-op stub in production-flavor builds via build_prepare.yaml.

Variables ​

This brick has no generation-time variables.

Prerequisites ​

  • app_debugger is generated first when the project lacks it. It provides the archipelago:debug_panel_pages sentinel; see Wiring below for where it lives).
  • analytics_sdk must already be generated (provides analytics_api, which this brick's hook adds to app_debugger — not analytics_impl, since a debug-only package must not pull in a release impl).

Usage ​

Interactive ​

bash
archipelago generate app_debugger_analytics

Non-interactive (CI) ​

bash
archipelago generate app_debugger_analytics --config my_config.json

Public API ​

AnalyticsInspectorSDK (in app_debugger_analytics_api) is the contract both the debug impl and the noop stub implement:

dart
abstract interface class AnalyticsInspectorSDK() implements FeatureSDK {
  bool get isActive;
  Stream<AnalyticsCaptureEntry> get captureStream;
  List<AnalyticsCaptureEntry> get currentEntries;
  void clearCapture();
  AnalyticInterceptor? get interceptor;
}

interceptor returns null in the noop stub, so callers must null-guard before registering it (see wiring below).

Generated Structure ​

text
features/
└── app_debugger_analytics/
    ├── app_debugger_analytics_api/
    │   └── lib/src/
    │       ├── analytics_inspector_sdk.dart      # AnalyticsInspectorSDK contract
    │       └── models/
    │           ├── analytics_capture_entry.dart
    │           └── analytics_inspector_config.dart
    ├── app_debugger_analytics/
    │   └── lib/src/
    │       ├── analytics_debugger_interceptor.dart
    │       ├── analytics_inspector_page.dart
    │       ├── analytics_inspector_sdk_impl.dart
    │       ├── consent_status_banner.dart        # Explains why the tool may look empty
    │       └── di/
    └── app_debugger_analytics_noop/
        └── lib/src/
            └── di/

Wiring (Auto-Patched by post_gen.dart) ​

On a current project both patches below land in features/app_debugger/app_debugger/lib/src/di/debug_tools.dart. The marker moved between app_debugger versions, so the hook uses the first file that has it: app_debugger's lib/src/di/debug_tools.dart (2.0.0 and later), its lib/src/app_debugger_sdk_impl.dart (1.3.0 to 1.15.x), then the app's lib/bootstrap.dart (older), and adds the imports and path dependencies to that package. A file that already resolves the SDK is left as it is. If no file has the marker, pre_gen prints the lines to add and stops before it writes any file.

di/debug_tools.dart — eager resolution ​

dart
// archipelago:debug_panel_pages:start
GetIt.I<AnalyticsInspectorSDK>();
// archipelago:debug_panel_pages:end

di/debug_tools.dart — interceptor registration ​

dart
// archipelago:analytics_inspector_interceptor:start
final analyticsInterceptor = GetIt.I<AnalyticsInspectorSDK>().interceptor;
if (analyticsInterceptor != null) {
  AnalyticInterceptorRegistry.instance.register(analyticsInterceptor);
}
// archipelago:analytics_inspector_interceptor:end

app_debugger only depends on analytics_api for this (not analytics_impl) — the registry lives in analytics_api, and analytics_impl reads it on every tracked call, so the interceptor applies whenever it registers.

post_launch_initializer.dart — AnalyticsInitializer.initialize() ​

The hook also injects a _AnalyticsPostLaunchInitializer class plus an archipelago:analytics_initializer sentinel into the app's post_launch_initializer.dart, so AnalyticsInitializer.initialize(getIt) actually runs (starting vendors behind whatever interceptors are already queued) — it is idempotent and skipped if that sentinel is already present. If the app doesn't have lib/host/initializers/post_launch_initializer.dart yet, this step is skipped and a warning is logged.

If any of these patches can't find their target (e.g. a hand-edited app_debugger without the marker), add them manually — the hook logs a warning naming exactly which sentinel is missing.

ConsentStatusBanner (from consent_api) is rendered above the captured-events list. A consent interceptor sits ahead of the debugger's own interceptor in the chain, so when a purpose is denied, events never reach the inspector — the tool would otherwise look identical to "nothing is being tracked". The banner reads the ambient ConsentGateRegistry.instance.gate and shows one of three states: granted, denied, or forced (when a decision carries ConsentSource.debugOverride, e.g. via the CONSENT_DEBUG_GRANT_ALL dart-define) — a forced grant is always visually distinct from real user consent.

Interceptor Chain Semantics ​

The AnalyticsDebuggerInterceptor is purely observational — it never drops events. intercept() always calls chain.proceed(data) and returns the result. Adding this interceptor has no side-effects on your analytics pipeline.

The onSetUserId and onReset hooks track the current user ID so captured entries carry the user context at the time of the event.

build_prepare Swap ​

yaml
- debug: app_debugger_analytics
  release: app_debugger_analytics_noop

Changelog ​

Run archipelago brick info app_debugger_analytics to see the latest published version. Enterprise customers can read the full CHANGELOG.md in the brick source repository.