### Clone Repository and Setup Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Clone your forked repository and add the upstream repository for development. ```bash git clone https://github.com/YOUR_USERNAME/flutterrific_opentelemetry.git cd flutterrific_opentelemetry ``` ```bash git remote add upstream https://github.com/MindfulSoftwareLLC/flutterrific_opentelemetry.git ``` -------------------------------- ### Development Setup Bash Script Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Commands to clone the repository, navigate into the directory, fetch dependencies, and run tests and analysis. ```bash git clone https://github.com/MindfulSoftwareLLC/flutterrific_opentelemetry.git cd flutterrific_opentelemetry flutter pub get dart test dart analyze ``` -------------------------------- ### Install Dependencies Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Install project dependencies using Flutter's package manager. ```bash flutter pub get ``` -------------------------------- ### Run All Flutter Tests Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/test/README.md Execute all tests within the Flutter project. Ensure all dependencies are installed with `flutter pub get` before running. ```bash flutter test ``` -------------------------------- ### Make Scripts Executable Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/DEVELOPMENT.md Before using the Makefile, ensure that the necessary shell scripts have execute permissions. This is a one-time setup step after cloning the repository. ```bash chmod +x run_coverage.sh ``` -------------------------------- ### Initialize OpenTelemetry for Widget Tests Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Configure OpenTelemetry for widget tests using `SimpleSpanProcessor` and `ConsoleExporter` to avoid conflicts with `FakeAsync`. Ensure proper setup and cleanup within test fixtures. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; Future initializeForTest() async { await FlutterOTel.initialize( endpoint: 'http://localhost:4317', serviceName: 'test-service', spanProcessor: SimpleSpanProcessor(ConsoleExporter()), enableMetrics: false, enableLogs: false, flushTracesInterval: null, // No periodic timer in tests detectPlatformResources: false, ); } // In tests: setUp(() async { await FlutterOTel.reset(); await initializeForTest(); }); tearDown(() async { await FlutterOTel.reset(); }); ``` -------------------------------- ### Auto-Collected OTel Metrics Examples Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt All metrics are auto-populated after initialization. Examples show how to query these metrics in Grafana. No additional code is needed for auto-collection. ```dart // All metrics are auto-populated — no code needed after initialize(). // To use them in Grafana, query by metric name, e.g.: // histogram_quantile(0.95, rate(flutter_frame_duration_bucket[5m])) // rate(flutter_errors_count_total[5m]) // flutter_apdex_score ``` -------------------------------- ### Run Grafana LGTM Local Collector Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt This command starts a local Grafana LGTM all-in-one collector for traces, metrics, logs, and the Grafana UI. Access Grafana at http://localhost:3000 with default credentials admin/admin. ```bash docker run -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti grafana/otel-lgtm # Open Grafana at http://localhost:3000 (default credentials: admin/admin) ``` -------------------------------- ### App Lifecycle Spans with UITracer Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Create spans for app lifecycle state transitions using `startAppLifecycleSpan()`. These spans start new root traces and are automatically called by `OTelLifecycleObserver`. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; // Automatically called by OTelLifecycleObserver on every lifecycle change. // To observe manually: final span = FlutterOTel.tracer.startAppLifecycleSpan( newState: AppLifecycleStates.paused, newStateId: OTel.spanId().bytes, startTime: DateTime.now(), previousState: AppLifecycleStates.active, previousStateId: previousStateBytes, previousStateDuration: Duration(minutes: 5), ); span.end(); // Span attributes include: // app_info.app_id, app_info.app_name // app_lifecycle.state = "paused" // app_lifecycle.state_id // app_lifecycle.previous_state = "active" // app_lifecycle.previous_state_id // app_lifecycle.duration = 300000 (ms) // app_lifecycle.timestamp // Log event (auto-emitted when enableAutoLogEvents is true): // eventName = "device.app.lifecycle" // body = "App lifecycle: paused" ``` -------------------------------- ### Navigation Spans with startNavigationChangeSpan() and recordNavChange() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Create spans for navigation changes using `startNavigationChangeSpan()` for manual control over span ending, or `recordNavChange()` for immediate start and end. These methods automatically include standard `NavigationSemantics` attributes. ```APIDOC ## `UITracer.startNavigationChangeSpan()` / `recordNavChange()` — Manual navigation spans Creates spans specifically representing navigation changes with all standard `NavigationSemantics` attributes. `recordNavChange()` starts and immediately ends the span; `startNavigationChangeSpan()` returns the span for the caller to end. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; // Fully manual navigation span (caller ends it) final span = FlutterOTel.tracer.startNavigationChangeSpan( newRouteName: '/checkout', newRoutePath: '/checkout', newRouteKey: 'checkout-key', newRouteArguments: '{}', routeSpanId: OTel.spanId(), newRouteStartTime: DateTime.now(), previousRouteName: '/cart', previousRoutePath: '/cart', previousRouteId: OTel.spanId(), routeChangeType: NavigationAction.push, routeDuration: Duration(milliseconds: 320), ); // ... do extra work if needed ... span.end(); // One-shot: start + end immediately (used by OTelNavigatorObserver internally) FlutterOTel.tracer.recordNavChange( '/settings', // newRouteName '/settings', // newRoutePath 'settings-key', // newRouteKey '{}', // newRouteArguments OTel.spanId(), // routeSpanId DateTime.now(), // newRouteStartTime '/home', // previousRouteName '/home', // previousRoutePath OTel.spanId(), // previousRouteSpanId NavigationAction.push, Duration(milliseconds: 150), ); ``` ``` -------------------------------- ### Run Local OpenTelemetry Collector with Docker Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Start a local OpenTelemetry collector using the Grafana LGTM Docker image. This provides a convenient way to collect and visualize traces, metrics, and logs during development. ```bash # Grafana LGTM stack (Loki, Grafana, Tempo, Mimir) — all-in-one docker run -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti grafana/otel-lgtm ``` -------------------------------- ### Initialize FlutterOTel with Options Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Configure FlutterOTel with various options for service name, version, endpoint, tracing, metrics, logs, and resources. Sensible defaults are provided for most optional parameters. ```dart await FlutterOTel.initialize( // Required serviceName: 'my-app', // Optional — all have sensible defaults appName: 'My App', // defaults to serviceName serviceVersion: '1.0.0', endpoint: 'https://collector:4317', // defaults to localhost:4317 // Traces spanProcessor: null, // auto-creates BatchSpanProcessor sampler: AlwaysOnSampler(), flushTracesInterval: Duration(seconds: 30), // Metrics enableMetrics: true, metricExporter: null, // auto-creates platform-specific exporter metricReader: null, // auto-creates PeriodicExportingMetricReader // Logs enableLogs: true, logRecordExporter: null, // auto-creates platform-specific exporter logRecordProcessor: null, logPrint: false, // bridge Dart print() to OTel logs enableAutoLogEvents: true, // auto-emit lifecycle/nav/error events // Resources resourceAttributes: null, detectPlatformResources: true, commonAttributesFunction: null, // called on every span creation // Security secure: true, dartasticApiKey: null, tenantId: null, ); ``` -------------------------------- ### Makefile Commands for Development Tasks Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/DEVELOPMENT.md The Makefile provides convenient shortcuts for common development workflows. Use 'make help' to see all available commands. ```makefile make help # Show available commands ``` ```makefile make install # Install dependencies ``` ```makefile make test # Run tests ``` ```makefile make coverage # Run tests with coverage ``` ```makefile make analyze # Analyze code ``` ```makefile make format # Format code ``` ```makefile make all # Run all checks ``` -------------------------------- ### FlutterOTel.initialize() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt For widget and unit tests, use SimpleSpanProcessor with ConsoleExporter to avoid gRPC timers conflicting with FakeAsync, and disable metrics/logs for speed. ```APIDOC ## `FlutterOTel.initialize()` — Testing configuration For widget and unit tests, use `SimpleSpanProcessor` with `ConsoleExporter` to avoid gRPC timers conflicting with `FakeAsync`, and disable metrics/logs for speed. ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; Future initOTelForTest() async { await FlutterOTel.initialize( endpoint: 'http://localhost:4317', serviceName: 'test-service', spanProcessor: SimpleSpanProcessor(ConsoleExporter()), enableMetrics: false, // no metric export timers enableLogs: false, // no log export timers flushTracesInterval: null, // no periodic flush timer detectPlatformResources: false, enableAutoLogEvents: false, ); } void main() { setUp(() async { await FlutterOTel.reset(); // clean state between tests await initOTelForTest(); }); tearDown(() async { await FlutterOTel.reset(); }); testWidgets('tracks button click', (tester) async { await tester.pumpWidget( MaterialApp( navigatorObservers: [FlutterOTel.routeObserver], home: const MyHomePage(), ), ); await tester.tap(find.text('Submit')); await tester.pump(); // Verify spans/spans via ConsoleExporter output or mock exporter }); } ``` ``` -------------------------------- ### App Lifecycle Spans with startAppLifecycleSpan() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Generate spans for application lifecycle state transitions using `startAppLifecycleSpan()`. These spans are typically called automatically by `OTelLifecycleObserver` but can be invoked manually. Each span initiates a new root trace and includes attributes detailing the state changes. ```APIDOC ## `UITracer.startAppLifecycleSpan()` — Lifecycle spans Creates spans for app lifecycle state transitions (active, paused, resumed, inactive, detached, hidden). Called automatically by `OTelLifecycleObserver` but can be used manually. Each span starts a new root trace. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; // Automatically called by OTelLifecycleObserver on every lifecycle change. // To observe manually: final span = FlutterOTel.tracer.startAppLifecycleSpan( newState: AppLifecycleStates.paused, newStateId: OTel.spanId().bytes, startTime: DateTime.now(), previousState: AppLifecycleStates.active, previousStateId: previousStateBytes, previousStateDuration: Duration(minutes: 5), ); span.end(); // Span attributes include: // app_info.app_id, app_info.app_name // app_lifecycle.state = "paused" // app_lifecycle.state_id // app_lifecycle.previous_state = "active" // app_lifecycle.previous_state_id // app_lifecycle.duration = 300000 (ms) // app_lifecycle.timestamp // Log event (auto-emitted when enableAutoLogEvents is true): // eventName = "device.app.lifecycle" // body = "App lifecycle: paused" ``` ``` -------------------------------- ### Manually Track Screen Spans Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Manually create and end spans for screens to track user navigation and time spent on different parts of the application. This creates a new trace for each screen span started. ```dart // Start a screen span (creates a new trace) final span = FlutterOTel().startScreenSpan('checkout'); // ... user interacts with screen ... // End when leaving FlutterOTel().endScreenSpan('checkout'); ``` -------------------------------- ### Publish to pub.dev Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Command to publish the package to pub.dev. Ensure version is updated and changelog is complete before running. ```bash flutter pub publish ``` -------------------------------- ### FlutterOTel.startScreenSpan() / endScreenSpan() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Manually create root spans for named screens, useful for tracking engagement beyond automatic navigation, such as modal dialogs or overlays. ```APIDOC ## `FlutterOTel.startScreenSpan()` / `endScreenSpan()` ### Description Creates a root span for a named screen. Useful for tracking session-level screen engagement that the `NavigatorObserver` doesn't cover (e.g., modal dialogs, overlays). ### Methods - `startScreenSpan(String screenName, {Map? attributes})` - `endScreenSpan(String screenName)` ### Span Naming Convention `screen.` ### Attributes - `view.name` - `ui.type='screen'` ``` -------------------------------- ### FlutterOTel.initialize() — SDK initialization Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt The primary entry point for initializing the Flutterrific OpenTelemetry SDK. It sets up the necessary providers, observers, and timers, and automatically configures exporters based on the platform. ```APIDOC ## FlutterOTel.initialize() ### Description The primary entry point. Must be called before using any other SDK method. Sets up the global `TracerProvider`, `MeterProvider`, and `LoggerProvider`, registers the lifecycle observer, creates the navigation observer, and starts the periodic flush timer. Automatically selects gRPC or HTTP exporters based on `kIsWeb`. ### Method `FlutterOTel.initialize()` ### Parameters - **serviceName** (string) - Required - The name of the service. - **serviceVersion** (string) - Required - The version of the service. - **endpoint** (string) - Required - The OTel collector endpoint. - **sampler** (Sampler) - Optional - The trace sampler to use. Defaults to `AlwaysOnSampler()` if not provided. - **flushTracesInterval** (Duration) - Optional - The interval for flushing traces. Defaults to 30 seconds. - **enableMetrics** (bool) - Optional - Whether to enable metrics collection. Defaults to true. - **enableLogs** (bool) - Optional - Whether to enable logs collection. Defaults to true. - **enableAutoLogEvents** (bool) - Optional - Whether to auto-emit lifecycle, navigation, and error log events. Defaults to true. - **logPrint** (bool) - Optional - Whether to bridge Dart `print()` to OTel logs. Defaults to false. - **commonAttributesFunction** (Function) - Optional - A function that returns common attributes to be added to every span. - **detectPlatformResources** (bool) - Optional - Whether to auto-detect and add platform resource information. Defaults to true. - **secure** (bool) - Optional - Whether to use a secure connection (HTTPS/GRPCS). Defaults to true. ### Request Example ```dart import 'package:flutter/material.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); FlutterError.onError = (FlutterErrorDetails details) { FlutterOTel.reportError( 'FlutterError.onError', details.exception, details.stack, ); }; PlatformDispatcher.instance.onError = (error, stack) { FlutterOTel.reportError('PlatformError', error, stack); return true; }; await FlutterOTel.initialize( serviceName: 'my-flutter-app', serviceVersion: '1.0.0', endpoint: 'https://otel-collector.example.com:4317', sampler: AlwaysOnSampler(), flushTracesInterval: Duration(seconds: 30), enableMetrics: true, enableLogs: true, enableAutoLogEvents: true, logPrint: false, commonAttributesFunction: () => { UserSemantics.userId.key: 'user-42', UserSemantics.userRole.key: 'admin', }.toAttributes(), detectPlatformResources: true, secure: true, ); runApp(const MyApp()); } ``` ``` -------------------------------- ### Initialize Flutterrific OpenTelemetry SDK Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Call `FlutterOTel.initialize()` before other SDK methods. It configures global providers, observers, and timers. Automatically selects exporters based on `kIsWeb` and allows customization of tracing, metrics, logging, and common attributes. ```dart import 'package:flutter/material.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Wire up Flutter error reporting to OTel before initialize FlutterError.onError = (FlutterErrorDetails details) { FlutterOTel.reportError( 'FlutterError.onError', details.exception, details.stack, ); }; // Catch async/platform errors PlatformDispatcher.instance.onError = (error, stack) { FlutterOTel.reportError('PlatformError', error, stack); return true; }; await FlutterOTel.initialize( serviceName: 'my-flutter-app', serviceVersion: '1.0.0', endpoint: 'https://otel-collector.example.com:4317', // Traces sampler: AlwaysOnSampler(), flushTracesInterval: Duration(seconds: 30), // Metrics enableMetrics: true, // Logs enableLogs: true, enableAutoLogEvents: true, // auto-emit lifecycle/nav/error log events logPrint: false, // set true to bridge Dart print() to OTel // Add common attributes to every span (e.g., user context) commonAttributesFunction: () => { UserSemantics.userId.key: 'user-42', UserSemantics.userRole.key: 'admin', }.toAttributes(), detectPlatformResources: true, // auto-detect OS, host, process info secure: true, ); runApp(const MyApp()); } ``` -------------------------------- ### Configure OpenTelemetry for Testing Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt For widget and unit tests, initialize OpenTelemetry with `SimpleSpanProcessor` and `ConsoleExporter`. Disable metrics and logs, and set `flushTracesInterval` to null to avoid conflicts with test runners like `FakeAsync`. ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; Future initOTelForTest() async { await FlutterOTel.initialize( endpoint: 'http://localhost:4317', serviceName: 'test-service', spanProcessor: SimpleSpanProcessor(ConsoleExporter()), enableMetrics: false, // no metric export timers enableLogs: false, // no log export timers flushTracesInterval: null, // no periodic flush timer detectPlatformResources: false, enableAutoLogEvents: false, ); } void main() { setUp(() async { await FlutterOTel.reset(); // clean state between tests await initOTelForTest(); }); tearDown(() async { await FlutterOTel.reset(); }); testWidgets('tracks button click', (tester) async { await tester.pumpWidget( MaterialApp( navigatorObservers: [FlutterOTel.routeObserver], home: const MyHomePage(), ), ); await tester.tap(find.text('Submit')); await tester.pump(); // Verify spans/spans via ConsoleExporter output or mock exporter }); } ``` -------------------------------- ### FlutterOTel.meter() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Returns a UIMeter for creating OTel instruments (counters, histograms, gauges, observable gauges) directly when FlutterOTelMetrics conveniences are insufficient. ```APIDOC ## `FlutterOTel.meter()` — Direct meter access Returns a `UIMeter` for creating OTel instruments (counters, histograms, gauges, observable gauges) directly when `FlutterOTelMetrics` conveniences are insufficient. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; final meter = FlutterOTel.meter(name: 'my-feature', version: '1.0.0'); // Counter final loginCounter = meter.createCounter( name: 'user.logins', description: 'Number of successful logins', unit: '{logins}', ); loginCounter.add(1, {'auth.method': 'oauth'}.toAttributes()); // Histogram final queryHistogram = meter.createHistogram( name: 'db.query.duration', description: 'Database query execution time', unit: 'ms', ); queryHistogram.record(42.5, {'db.table': 'products'}.toAttributes()); // Observable gauge (value pulled at export time) meter.createObservableGauge( name: 'memory.heap_used', description: 'Current heap usage', unit: 'By', callback: (result) { result.observe( ProcessInfo.currentRss.toDouble(), OTel.attributes(), ); }, ); ``` ``` -------------------------------- ### Configure OpenTelemetry with Dart Define for Development Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Use `--dart-define` to set environment variables for local development, including service name, endpoint, and enabling console logging. ```bash flutter run \ --dart-define=OTEL_SERVICE_NAME=my-flutter-app \ --dart-define=OTEL_SERVICE_VERSION=1.0.0 \ --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \ --dart-define=OTEL_EXPORTER_OTLP_PROTOCOL=grpc \ --dart-define=OTEL_LOG_LEVEL=DEBUG \ --dart-define=OTEL_CONSOLE_EXPORTER=true ``` -------------------------------- ### Create Feature Branch Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Create a new branch for your feature development. ```bash git checkout -b feature/your-feature-name ``` -------------------------------- ### Format Code Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Format your code according to the project's style guidelines using the Dart formatter. ```bash dart format . ``` -------------------------------- ### Configure OpenTelemetry with Dart Define for Production Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Configure OpenTelemetry for production builds using `--dart-define`, specifying the service name, version, remote collector endpoint, and authentication headers. ```bash flutter build apk \ --dart-define=OTEL_SERVICE_NAME=my-flutter-app \ --dart-define=OTEL_SERVICE_VERSION=2.1.0 \ --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com:4317 \ --dart-define=OTEL_EXPORTER_OTLP_HEADERS=api-key=your-secret-key \ --dart-define=OTEL_LOG_LEVEL=INFO ``` -------------------------------- ### Manual Screen Spans with startScreenSpan/endScreenSpan Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Manually create root spans for screens, useful for tracking engagement not covered by NavigatorObserver, such as modal dialogs. Ensure to call endScreenSpan in dispose. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; class _SubscriptionDialogState extends State { @override void initState() { super.initState(); FlutterOTel().startScreenSpan( 'subscription_dialog', attributes: {'dialog.source': 'checkout_flow'}.toAttributes(), ); } @override void dispose() { FlutterOTel().endScreenSpan('subscription_dialog'); super.dispose(); } } // Span name: "screen.subscription_dialog" // Attributes: view.name='subscription_dialog', ui.type='screen' ``` -------------------------------- ### Push Branch Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Push your feature branch to your forked repository. ```bash git push origin feature/your-feature-name ``` -------------------------------- ### Custom Span Creation with FlutterOTel.tracer Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Use `FlutterOTel.tracer.startSpan()` for manual instrumentation of custom operations like API calls. Spans must be ended manually using `span.end()`. Convenience methods `recordSpan` and `recordSpanAsync` are available for synchronous and asynchronous operations respectively, automatically handling span creation and ending. ```APIDOC ## `FlutterOTel.tracer` — Custom span creation Use `FlutterOTel.tracer.startSpan()` for custom instrumentation, e.g., wrapping API calls or complex operations. Spans must be ended manually. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; Future fetchUser(String userId) async { final tracer = FlutterOTel.tracer; final span = tracer.startSpan( 'fetch_user', kind: SpanKind.client, attributes: { UserSemantics.userId.key: userId, 'http.method': 'GET', 'http.url': '/api/users/$userId', }.toAttributes(), ); try { final user = await apiClient.getUser(userId); span.addEventNow( 'user_fetched', {'user.name': user.name, 'response.code': 200}.toAttributes(), ); span.setStatus(SpanStatusCode.Ok); return user; } catch (e, stackTrace) { span.recordException(e, stackTrace: stackTrace, escaped: true); span.setStatus(SpanStatusCode.Error, e.toString()); rethrow; } finally { span.end(); // Always end the span } } // Convenience: record span with automatic end (sync) final result = FlutterOTel.tracer.recordSpan( name: 'compute_total', fn: () => cart.items.fold(0.0, (sum, item) => sum + item.price), ); // Convenience: record span with automatic end (async) final data = await FlutterOTel.tracer.recordSpanAsync( name: 'load_dashboard', fn: () async => await dashboardService.loadData(), ); ``` ``` -------------------------------- ### Initialize OpenTelemetry in Flutter App Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Call `FlutterOTel.initialize` in your `main` function. Ensure `WidgetsFlutterBinding.ensureInitialized()` is called first. Error handling can be set up using `FlutterError.onError`. ```dart import 'package:flutter/material.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize error handling FlutterError.onError = (FlutterErrorDetails details) { FlutterOTel.reportError( 'FlutterError.onError', details.exception, details.stack); }; // Initialize OpenTelemetry — all three signals enabled by default await FlutterOTel.initialize( serviceName: 'my-flutter-app', serviceVersion: '1.0.0', // endpoint defaults to localhost:4317 (gRPC) or 4318 (HTTP on web) ); runApp(MyApp()); } ``` -------------------------------- ### OTelGoRouterRedirect Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Wraps a GoRouter redirect function to record OTel spans for redirect decisions without changing redirect logic. ```APIDOC ## `OTelGoRouterRedirect` — GoRouter redirect instrumentation Wraps a GoRouter `redirect` function to record OTel spans for redirect decisions without changing redirect logic. ```dart import 'package:go_router/go_router.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; String? _handleRedirect(BuildContext context, GoRouterState state) { final isLoggedIn = AuthService.instance.isLoggedIn; if (!isLoggedIn && state.uri.toString() != '/login') { return '/login'; } return null; // no redirect } final otelRedirect = OTelGoRouterRedirect(_handleRedirect); final router = GoRouter( redirect: otelRedirect.callRedirect, observers: [FlutterOTel.routeObserver], routes: [ GoRoute(path: '/', builder: (ctx, state) => const HomeScreen()), GoRoute(path: '/login', builder: (ctx, state) => const LoginScreen()), GoRoute(path: '/dashboard', builder: (ctx, state) => const DashboardScreen()), ], ); // Each redirect records a navigation span with action=NavigationAction.redirect ``` ``` -------------------------------- ### Analyze Code Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Run the Dart analyzer to check for static analysis issues and linting rule violations. ```bash flutter analyze ``` -------------------------------- ### Create Custom Spans with FlutterOTel.tracer Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Use `FlutterOTel.tracer.startSpan()` for custom instrumentation. Spans must be ended manually. Convenience methods `recordSpan` and `recordSpanAsync` automatically end the span. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; Future fetchUser(String userId) async { final tracer = FlutterOTel.tracer; final span = tracer.startSpan( 'fetch_user', kind: SpanKind.client, attributes: { UserSemantics.userId.key: userId, 'http.method': 'GET', 'http.url': '/api/users/$userId', }.toAttributes(), ); try { final user = await apiClient.getUser(userId); span.addEventNow( 'user_fetched', {'user.name': user.name, 'response.code': 200}.toAttributes(), ); span.setStatus(SpanStatusCode.Ok); return user; } catch (e, stackTrace) { span.recordException(e, stackTrace: stackTrace, escaped: true); span.setStatus(SpanStatusCode.Error, e.toString()); rethrow; } finally { span.end(); // Always end the span } } // Convenience: record span with automatic end (sync) final result = FlutterOTel.tracer.recordSpan( name: 'compute_total', fn: () => cart.items.fold(0.0, (sum, item) => sum + item.price), ); // Convenience: record span with automatic end (async) final data = await FlutterOTel.tracer.recordSpanAsync( name: 'load_dashboard', fn: () async => await dashboardService.loadData(), ); ``` -------------------------------- ### Commit Changes Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/CONTRIBUTING.md Commit your changes with a descriptive message following the project's commit message guidelines. ```bash git commit -m "Add feature: description of your changes" ``` -------------------------------- ### Manual Navigation Spans with UITracer Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Create spans for navigation changes using `startNavigationChangeSpan()` for manual ending or `recordNavChange()` for automatic ending. These include standard `NavigationSemantics` attributes. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; // Fully manual navigation span (caller ends it) final span = FlutterOTel.tracer.startNavigationChangeSpan( newRouteName: '/checkout', newRoutePath: '/checkout', newRouteKey: 'checkout-key', newRouteArguments: '{}', routeSpanId: OTel.spanId(), newRouteStartTime: DateTime.now(), previousRouteName: '/cart', previousRoutePath: '/cart', previousRouteId: OTel.spanId(), routeChangeType: NavigationAction.push, routeDuration: Duration(milliseconds: 320), ); // ... do extra work if needed ... span.end(); // One-shot: start + end immediately (used by OTelNavigatorObserver internally) FlutterOTel.tracer.recordNavChange( '/settings', // newRouteName '/settings', // newRoutePath 'settings-key', // newRouteKey '{}', // newRouteArguments OTel.spanId(), // routeSpanId DateTime.now(), // newRouteStartTime '/home', // previousRouteName '/home', // previousRoutePath OTel.spanId(), // previousRouteSpanId NavigationAction.push, Duration(milliseconds: 150), ); ``` -------------------------------- ### FlutterOTel.routeObserver — Navigation auto-instrumentation Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt An `OTelNavigatorObserver` to be registered with your router or `MaterialApp` to automatically instrument navigation events with spans and log events. ```APIDOC ## FlutterOTel.routeObserver — Navigation auto-instrumentation ### Description The `OTelNavigatorObserver` that must be registered with GoRouter or `MaterialApp` to enable automatic navigation tracing and log events. Each route push/pop/replace/remove creates an independent span and (when `enableAutoLogEvents` is true) a structured `browser.navigation` log event. ### Method `FlutterOTel.routeObserver` ### Usage Register `FlutterOTel.routeObserver` in your `GoRouter` observers or `MaterialApp`'s `navigatorObservers`. ### Example with GoRouter ```dart import 'package:go_router/go_router.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; final router = GoRouter( observers: [FlutterOTel.routeObserver], routes: [ GoRoute(path: '/', builder: (ctx, state) => const HomeScreen()), GoRoute(path: '/details/:id', builder: (ctx, state) => DetailsScreen( id: state.pathParameters['id']!, )), ], ); ``` ### Example with Standard Navigator ```dart import 'package:flutter/material.dart'; import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; MaterialApp( navigatorObservers: [FlutterOTel.routeObserver], home: const HomeScreen(), ); ``` ### Emitted Spans and Logs Each navigation automatically emits a span like: - **Span name**: `navigation.action` - **Attributes**: `navigation.route.name`, `navigation.previous_route_name`, `navigation.action` (push|pop|replace|remove), `navigation.route.path`, `navigation.route.id` - **Log event**: `eventName="browser.navigation"`, `body="Navigation: /home -> /details/42"` ``` -------------------------------- ### FlutterOTel.logger() / UILogger Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Provides a structured logger for emitting OTel events, Flutter errors, lifecycle events, and navigation events. Standard log levels are also available. ```APIDOC ## `FlutterOTel.logger()` / `UILogger` ### Description Returns a `UILogger` that wraps the SDK `Logger` with Flutter-specific helpers for emitting structured log events. ### Methods - Standard log levels: `trace`, `debug`, `info`, `warn`, `error`, `fatal` - `emitEvent(String eventName, {String? body, Map? attributes, Severity? severity})` - `emitFlutterError(FlutterErrorDetails details)` - `emitLifecycleEvent(String state, {String? previousState, Duration? duration})` - `emitNavigationEvent(String toRoute, {String? fromRoute, String? action})` ### Example Usage ```dart final logger = FlutterOTel.logger('checkout'); // Standard severity levels logger.info('Checkout started'); logger.error('Charge failed', attributes: {'attempt': 3}.toAttributes()); // Structured OTel Event logger.emitEvent( 'checkout.completed', body: 'Order placed successfully', attributes: { 'order.id': orderId, 'order.total': totalAmount, }.toAttributes(), severity: Severity.INFO, ); // Flutter error handling FlutterError.onError = (details) { FlutterOTel.logger('flutter.errors').emitFlutterError(details); }; // Manual lifecycle event FlutterOTel.logger('flutter.lifecycle').emitLifecycleEvent('paused'); // Manual navigation event FlutterOTel.logger('flutter.navigation').emitNavigationEvent('/details/42', fromRoute: '/home'); ``` ### Event Naming Conventions - `device.app.error` for `emitFlutterError` - `ui.lifecycle.` for `emitLifecycleEvent` - `ui.navigation.` for `emitNavigationEvent` ### Attributes for `emitFlutterError` - `error.type` - `error.message` - `error.widget_context` - `exception.stacktrace` ``` -------------------------------- ### Direct Meter Access for Custom Instruments Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Obtain a `UIMeter` via `FlutterOTel.meter()` to create custom counters, histograms, or observable gauges when `FlutterOTelMetrics` conveniences are insufficient. Ensure to use `.toAttributes()` for attribute maps. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; final meter = FlutterOTel.meter(name: 'my-feature', version: '1.0.0'); // Counter final loginCounter = meter.createCounter( name: 'user.logins', description: 'Number of successful logins', unit: '{logins}', ); loginCounter.add(1, {'auth.method': 'oauth'}.toAttributes()); // Histogram final queryHistogram = meter.createHistogram( name: 'db.query.duration', description: 'Database query execution time', unit: 'ms', ); queryHistogram.record(42.5, {'db.table': 'products'}.toAttributes()); // Observable gauge (value pulled at export time) meter.createObservableGauge( name: 'memory.heap_used', description: 'Current heap usage', unit: 'By', callback: (result) { result.observe( ProcessInfo.currentRss.toDouble(), OTel.attributes(), ); }, ); ``` -------------------------------- ### Run Specific Flutter Test File Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/test/README.md Execute tests from a particular file, such as `lifecycle_observer_test.dart`. This is useful for focused testing. ```bash flutter test test/lifecycle_observer_test.dart ``` -------------------------------- ### Record Custom Metrics Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Use `FlutterOTelMetrics.recordMetric` for simple metric recording or obtain a `Meter` instance from `FlutterOTel.meter` for more advanced metric creation, such as counters. ```dart // Using FlutterOTelMetrics FlutterOTelMetrics.recordMetric( name: 'my_custom_metric', value: 42, unit: 'ms', metricType: 'histogram', ); // Or directly with a Meter final meter = FlutterOTel.meter(name: 'my-feature'); meter.createCounter(name: 'feature.usage', unit: '{count}') .add(1, {'feature.name': 'dark_mode'}.toAttributes()); ``` -------------------------------- ### Structured Logging with UILogger Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Utilize UILogger for structured log events, including standard severity levels and Flutter-specific helpers like emitEvent, emitFlutterError, emitLifecycleEvent, and emitNavigationEvent. Recommended for business events. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; final logger = FlutterOTel.logger('checkout'); // Standard severity levels logger.info('Checkout started'); logger.warn('Payment method expired'); logger.error('Charge failed', attributes: {'attempt': 3}.toAttributes()); // Structured OTel Event (recommended for business events) logger.emitEvent( 'checkout.completed', body: 'Order placed successfully', attributes: { 'order.id': orderId, 'order.total': totalAmount, 'payment.method': 'credit_card', 'items.count': itemCount, }.toAttributes(), severity: Severity.INFO, ); // Flutter error from FlutterErrorDetails FlutterError.onError = (details) { FlutterOTel.logger('flutter.errors').emitFlutterError(details); // emits: eventName="device.app.error", severity=ERROR // attributes: error.type, error.message, error.widget_context, exception.stacktrace }; // Manual lifecycle event FlutterOTel.logger('flutter.lifecycle').emitLifecycleEvent( 'paused', previousState: 'active', duration: Duration(minutes: 3), ); // Manual navigation event FlutterOTel.logger('flutter.navigation').emitNavigationEvent( '/details/42', fromRoute: '/home', action: 'push', ); ``` -------------------------------- ### Configure OpenTelemetry via Dart Defines Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Set standard OpenTelemetry environment variables when running your Flutter app using the `--dart-define` flag. This is useful for configuring service name, endpoint, and protocol. ```bash flutter run \ --dart-define=OTEL_SERVICE_NAME=my-flutter-app \ --dart-define=OTEL_SERVICE_VERSION=1.0.0 \ --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=https://collector:4317 \ --dart-define=OTEL_EXPORTER_OTLP_PROTOCOL=grpc \ --dart-define=OTEL_EXPORTER_OTLP_HEADERS=api-key=your-api-key ``` -------------------------------- ### Track Widget Interactions with OpenTelemetry Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Automatically track common widget interactions like button presses and error boundaries using provided extensions. For more granular control, use the interaction tracker for specific events like clicks and scrolls. ```dart // Track button interactions ElevatedButton( onPressed: handleSubmit, child: Text('Submit'), ).withOTelButtonTracking('submit_form'); // Error boundaries RiskyWidget().withOTelErrorBoundary('risky_operation'); // Track interactions via the interaction tracker FlutterOTel.interactionTracker.trackButtonClick(context, 'submit_btn'); FlutterOTel.interactionTracker.trackScroll(context, 'feed_list', scrollPosition); FlutterOTel.interactionTracker.trackSwipeGesture(context, 'card', 'left'); ``` -------------------------------- ### OTelWidgetExtension.withOTelErrorBoundary() Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt A widget extension that wraps any widget with an error boundary, automatically recording widget build errors as OTel spans. It overrides `ErrorWidget.builder` within a `Builder` scope. ```APIDOC ## `OTelWidgetExtension.withOTelErrorBoundary()` — Widget error boundary A widget extension that overrides `ErrorWidget.builder` within a `Builder` scope to automatically record widget build errors as OTel spans. Wraps any widget with error telemetry. ### Parameters - **boundaryName** (string) - Required - The name to use for the error span when an error occurs within this boundary. ### Example Usage ```dart @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ ProductList(products: products), CheckoutButton(onTap: handleCheckout), ], ).withOTelErrorBoundary('home_screen'), ); } ``` ### Resulting Span Details (on error) - **name**: `error.` - **attributes**: `error.context='widget_build'`, `error.type`, `error.message`, `error.widget` ``` -------------------------------- ### Record Custom Metrics with FlutterOTelMetrics Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Use `recordMetric` for generic counters, gauges, and histograms. Convenience methods like `recordNavigationMetric`, `recordPerformanceMetric`, and `recordError` simplify common recording tasks. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; // Generic histogram (default) FlutterOTelMetrics.recordMetric( name: 'cart.item_count', value: 5, unit: '{items}', metricType: 'histogram', attributes: {'user.segment': 'premium'}, ); // Counter FlutterOTelMetrics.recordMetric( name: 'feature.dark_mode.toggle', value: 1, unit: '{toggles}', metricType: 'counter', ); // Gauge FlutterOTelMetrics.recordMetric( name: 'cache.size', value: cacheManager.sizeBytes, unit: 'By', metricType: 'gauge', ); // Navigation timing convenience method FlutterOTelMetrics.recordNavigationMetric( '/home', '/product/123', Duration(milliseconds: 280), ); // Performance convenience method FlutterOTelMetrics.recordPerformanceMetric( 'image_decode', Duration(milliseconds: 45), attributes: {'image.format': 'webp', 'image.size': '512x512'}, ); // Error count convenience method FlutterOTelMetrics.recordError( 'NetworkException', message: 'Connection timed out', location: 'ProductRepository.fetchProducts', ); ``` -------------------------------- ### Emit Custom OpenTelemetry Events Source: https://github.com/mindfulsoftwarellc/flutterrific_opentelemetry/blob/main/README.md Create and emit structured custom events using the logger instance obtained from `FlutterOTel.logger`. Events can include a body and custom attributes for detailed context. ```dart final logger = FlutterOTel.logger('checkout'); // Emit a structured event logger.emitEvent('checkout.completed', body: 'Order placed successfully', attributes: { 'order.id': orderId, 'order.total': total, 'payment.method': 'credit_card', }.toAttributes(), ); ``` -------------------------------- ### FlutterOTel Lifecycle Management with WidgetsBindingObserver Source: https://context7.com/mindfulsoftwarellc/flutterrific_opentelemetry/llms.txt Implement `WidgetsBindingObserver` to manage `FlutterOTel` lifecycle. Call `FlutterOTel.forceFlush()` in `didChangeAppLifecycleState` when the app state is `detached` to ensure telemetry is flushed before exit. Ensure `dispose()` is called to flush and unregister the observer. ```dart import 'package:flutterrific_opentelemetry/flutterrific_opentelemetry.dart'; class MyAppState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.detached) { // Ensure all telemetry is flushed before the app process exits FlutterOTel.forceFlush(); } } @override void dispose() { FlutterOTel().dispose(); // flush + unregister lifecycle observer WidgetsBinding.instance.removeObserver(this); super.dispose(); } } ```