Skip to content

Setting Up the Monitoring SDK

What you'll learn

  • Generating the Monitoring SDK with API, Impl, and Noop packages
  • Understanding the Impl + Noop pattern for build-time stripping
  • Configuring crash reporting and performance monitoring
  • Using build_prepare to swap implementations per build flavor

Prerequisites

  • An existing Archipelago monorepo (see Monorepo Scaffolding)
  • Familiarity with crash reporting concepts (Sentry, Crashlytics)

Step 1: Generate the Monitoring SDK

bash
archipelago generate monitoring_sdk

You will be prompted for:

  • appNameMyApp (must match your monorepo app name)
  • isForMonorepotrue

Or use a config file:

json
{
  "appName": "MyApp",
  "isForMonorepo": true
}
bash
archipelago generate monitoring_sdk --config monitoring_config.json

Step 2: Understand the Three-Package Structure

The Monitoring SDK generates three packages, not two:

infrastructure/
├── monitoring_sdk_api/           # Contracts
│   └── lib/src/
│       ├── monitoring_client.dart     # Abstract interface
│       ├── crash_reporter.dart        # Crash reporting contract
│       └── performance_monitor.dart   # Performance tracing contract
├── monitoring_sdk_impl/          # Real implementation
│   └── lib/src/
│       ├── sentry_monitoring_client.dart
│       ├── di/monitoring_module.dart
│       └── ...
└── monitoring_sdk_noop/          # No-op implementation
    └── lib/src/
        ├── noop_monitoring_client.dart
        └── di/monitoring_module.dart

Step 3: The Noop Pattern Explained

The noop package implements every method in monitoring_sdk_api as a no-op (does nothing). This is the key to build-time stripping:

dart
// monitoring_sdk_noop/lib/src/noop_monitoring_client.dart
class NoopMonitoringClient implements MonitoringClient {
  @override
  Future<void> init() async {}

  @override
  void captureException(Object error, {StackTrace? stackTrace}) {}

  @override
  void startPerformanceTrace(String name) {}

  @override
  void stopPerformanceTrace(String name) {}
}

In debug and staging builds, the noop replaces the real implementation, keeping your dev builds clean and free from third-party SDK noise.

Step 4: Configure build_prepare

The build_prepare.yaml at your monorepo root controls which implementation is used per flavor:

yaml
# build_prepare.yaml
flavors:
  development:
    monitoring_sdk:
      dependency: monitoring_sdk_noop    # No-op in dev
  staging:
    monitoring_sdk:
      dependency: monitoring_sdk_noop    # No-op in staging
  production:
    monitoring_sdk:
      dependency: monitoring_sdk_impl    # Real in production

Before building, run:

bash
dart run devtools/build_prepare.dart --flavor production

This rewrites pubspec.yaml path dependencies so the correct implementation is resolved at compile time. No runtime if/else, no unused code in the binary.

Step 5: Wire the Real Implementation

Edit the impl package to connect your monitoring vendor:

dart
// monitoring_sdk_impl/lib/src/sentry_monitoring_client.dart
class SentryMonitoringClient implements MonitoringClient {
  @override
  Future<void> init() async {
    await SentryFlutter.init((options) {
      options.dsn = const String.fromEnvironment('SENTRY_DSN');
      options.tracesSampleRate = 0.2;
      options.environment = const String.fromEnvironment('FLAVOR');
    });
  }

  @override
  void captureException(Object error, {StackTrace? stackTrace}) {
    Sentry.captureException(error, stackTrace: stackTrace);
  }

  @override
  void startPerformanceTrace(String name) {
    // Start a Sentry transaction
  }
}

Step 6: Use Monitoring in Features

Features depend on monitoring_sdk_api and never import the impl or noop:

dart
// In any feature's error handling
class PaymentRepository {
  final MonitoringClient _monitoring;

  Future<Payment> processPayment(PaymentRequest request) async {
    try {
      _monitoring.startPerformanceTrace('process_payment');
      final result = await _remoteDataSource.process(request);
      _monitoring.stopPerformanceTrace('process_payment');
      return result;
    } catch (e, st) {
      _monitoring.captureException(e, stackTrace: st);
      rethrow;
    }
  }
}

Why Noop Instead of Runtime Checks

ApproachDownside
if (kReleaseMode)Dead code still in the binary, vendor SDK still linked
build_prepare + noopVendor SDK not compiled at all in debug builds

The noop pattern keeps your debug APK smaller and avoids initializing SDKs that send data during development.

When to Use This Pattern

The Impl + Noop pattern is used for infrastructure SDKs that:

  • Have third-party vendor dependencies (Sentry, Firebase, Datadog)
  • Should not run in debug or test environments
  • Need compile-time stripping, not just runtime toggling

Other SDKs using this same pattern: analytics_sdk, feature_flag_sdk.

Next Steps

Built by Banua Coder