LCOV - code coverage report
Current view: top level - lib/src/metrics - metric_reader.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 87.5 % 40 35
Test Date: 2026-07-11 13:35:19 Functions: - 0 0

            Line data    Source code
       1              : // Copyright The OpenTelemetry Authors
       2              : // SPDX-License-Identifier: Apache-2.0
       3              : 
       4              : import 'dart:async';
       5              : 
       6              : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart'
       7              :     show OTelLog;
       8              : 
       9              : import 'data/metric_data.dart';
      10              : import 'meter_provider.dart';
      11              : import 'metric_exporter.dart';
      12              : 
      13              : /// MetricReader is responsible for collecting metrics from a MeterProvider
      14              : /// and passing them to a MetricExporter.
      15              : abstract class MetricReader {
      16              :   /// The MeterProvider this reader is associated with.
      17              :   MeterProvider? _meterProvider;
      18              : 
      19              :   /// Register a MeterProvider with this reader.
      20              :   ///
      21              :   /// This allows the reader to collect metrics from the provider.
      22          108 :   void registerMeterProvider(MeterProvider provider) {
      23          108 :     _meterProvider = provider;
      24              :   }
      25              : 
      26              :   /// Get the MeterProvider this reader is associated with.
      27          214 :   MeterProvider? get meterProvider => _meterProvider;
      28              : 
      29              :   /// Collect metrics from the MeterProvider.
      30              :   ///
      31              :   /// This method triggers the collection of metrics, and returns the
      32              :   /// collected data as an object containing resource and metric information.
      33              :   Future<MetricData> collect();
      34              : 
      35              :   /// Force flush metrics through the associated exporter.
      36              :   ///
      37              :   /// Returns true if the flush was successful, false otherwise.
      38              :   Future<bool> forceFlush();
      39              : 
      40              :   /// Shutdown the metric reader.
      41              :   ///
      42              :   /// This should clean up any resources and perform final exports.
      43              :   Future<bool> shutdown();
      44              : }
      45              : 
      46              : /// PeriodicExportingMetricReader is a MetricReader that periodically
      47              : /// collects metrics and exports them.
      48              : class PeriodicExportingMetricReader extends MetricReader {
      49              :   /// The exporter to send metrics to.
      50              :   final MetricExporter _exporter;
      51              : 
      52              :   /// The configured exporter that this reader sends metrics to.
      53            2 :   MetricExporter get exporter => _exporter;
      54              : 
      55              :   /// How often to collect and export metrics.
      56              :   final Duration _interval;
      57              : 
      58              :   /// Maximum time to wait for export operations.
      59              :   final Duration _timeout;
      60              : 
      61              :   /// Timer for periodic collection.
      62              :   Timer? _timer;
      63              : 
      64              :   /// Creates a new PeriodicExportingMetricReader.
      65              :   ///
      66              :   /// [interval] How often to collect and export metrics (default: 60 seconds).
      67              :   /// [timeout] Maximum time to wait for export operations (default: 30 seconds).
      68          107 :   PeriodicExportingMetricReader(
      69              :     this._exporter, {
      70              :     Duration interval = const Duration(seconds: 60),
      71              :     Duration timeout = const Duration(seconds: 30),
      72              :   })  : _interval = interval,
      73              :         _timeout = timeout {
      74              :     // Start the timer
      75          107 :     _startTimer();
      76              :   }
      77              : 
      78              :   /// Start the periodic collection timer.
      79          107 :   void _startTimer() {
      80          107 :     _timer?.cancel();
      81          323 :     _timer = Timer.periodic(_interval, (_) => _collectAndExport());
      82              :   }
      83              : 
      84              :   /// Collect and export metrics.
      85          106 :   Future<void> _collectAndExport() async {
      86          106 :     if (meterProvider == null) return;
      87              : 
      88              :     try {
      89              :       // Collect metrics
      90          106 :       final data = await collect();
      91              : 
      92              :       // Export metrics
      93          212 :       if (data.metrics.isNotEmpty) {
      94            4 :         final exportFuture = _exporter.export(data);
      95              : 
      96              :         // Apply timeout to export
      97            2 :         await exportFuture.timeout(
      98            2 :           _timeout,
      99            0 :           onTimeout: () {
     100            0 :             print('Metric export timed out after $_timeout');
     101              :             return false;
     102              :           },
     103              :         );
     104              :       }
     105              :     } catch (e) {
     106            0 :       print('Error during metric collection/export: $e');
     107              :     }
     108              :   }
     109              : 
     110          106 :   @override
     111              :   Future<MetricData> collect() async {
     112          106 :     if (meterProvider == null) {
     113            1 :       if (OTelLog.isLogMetrics()) {
     114            1 :         OTelLog.logMetric(
     115              :           'PeriodicExportingMetricReader: No meter provider registered',
     116              :         );
     117              :       }
     118              :       // Return an empty container with no metrics
     119            1 :       return MetricData.empty();
     120              :     }
     121              : 
     122              :     // Get the meter provider as an SDK MeterProvider to access the metric storage
     123          106 :     final sdkMeterProvider = meterProvider as MeterProvider;
     124              : 
     125              :     // Collect metrics from all instruments in the meter provider
     126          106 :     final metrics = await sdkMeterProvider.collectAllMetrics();
     127              : 
     128          106 :     if (OTelLog.isLogMetrics()) {
     129          106 :       OTelLog.logMetric(
     130          212 :         'PeriodicExportingMetricReader: Collected ${metrics.length} metrics',
     131              :       );
     132              :     }
     133              : 
     134          318 :     return MetricData(resource: meterProvider!.resource, metrics: metrics);
     135              :   }
     136              : 
     137            3 :   @override
     138              :   Future<bool> forceFlush() async {
     139              :     try {
     140              :       // Collect and export immediately
     141            3 :       await _collectAndExport();
     142            6 :       return await _exporter.forceFlush();
     143              :     } catch (e) {
     144            0 :       print('Error during forceFlush: $e');
     145              :       return false;
     146              :     }
     147              :   }
     148              : 
     149          106 :   @override
     150              :   Future<bool> shutdown() async {
     151          212 :     _timer?.cancel();
     152          106 :     _timer = null;
     153              : 
     154              :     try {
     155              :       // Perform one final collection and export
     156          106 :       await _collectAndExport();
     157          212 :       return await _exporter.shutdown();
     158              :     } catch (e) {
     159            0 :       print('Error during shutdown: $e');
     160              :       return false;
     161              :     }
     162              :   }
     163              : }
        

Generated by: LCOV version 2.0-1