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_prepareto 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
archipelago generate monitoring_sdkYou will be prompted for:
- appName —
MyApp(must match your monorepo app name) - isForMonorepo —
true
Or use a config file:
{
"appName": "MyApp",
"isForMonorepo": true
}archipelago generate monitoring_sdk --config monitoring_config.jsonStep 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.dartStep 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:
// 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:
# 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 productionBefore building, run:
dart run devtools/build_prepare.dart --flavor productionThis 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:
// 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:
// 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
| Approach | Downside |
|---|---|
if (kReleaseMode) | Dead code still in the binary, vendor SDK still linked |
build_prepare + noop | Vendor 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
- Set up authentication with crash tracking on auth failures
- Configure networking to log HTTP errors to monitoring