LCOV - code coverage report
Current view: top level - lib/src/metrics - meter_provider.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.9 % 96 93
Test Date: 2026-08-27 23:42:02 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:meta/meta.dart';
       7              : 
       8              : import '../../dartastic_opentelemetry.dart';
       9              : import 'meter.dart';
      10              : 
      11              : part 'meter_provider_create.dart';
      12              : 
      13              : /// SDK implementation of the APIMeterProvider interface.
      14              : ///
      15              : /// The MeterProvider is the entry point to the metrics API. It is responsible
      16              : /// for creating and managing Meters, as well as configuring the metric pipeline
      17              : /// via MetricReaders and Views.
      18              : ///
      19              : /// This implementation delegates some functionality to the API MeterProvider
      20              : /// implementation while adding SDK-specific behaviors.
      21              : ///
      22              : /// More information:
      23              : /// https://opentelemetry.io/docs/specs/otel/metrics/sdk/
      24              : class MeterProvider implements APIMeterProvider {
      25              :   /// The underlying API MeterProvider implementation.
      26              :   final APIMeterProvider delegate;
      27              : 
      28              :   /// The resource associated with this MeterProvider.
      29              :   Resource? resource;
      30              : 
      31              :   /// List of metric readers associated with this MeterProvider.
      32              :   final List<MetricReader> _metricReaders = [];
      33              : 
      34              :   /// List of views for configuring metric collection.
      35              :   final List<View> _views = [];
      36              : 
      37              :   /// The ExemplarFilter used by this provider's meters.
      38              :   ExemplarFilter exemplarFilter = const TraceBasedExemplarFilter();
      39              : 
      40              :   /// Private constructor for creating MeterProvider instances.
      41              :   ///
      42              :   /// @param delegate The API MeterProvider implementation to delegate to
      43              :   /// @param resource Optional Resource describing the entity producing telemetry
      44          132 :   MeterProvider._({required this.delegate, this.resource}) {
      45          132 :     if (OTelLog.isDebug()) {
      46          369 :       OTelLog.debug('MeterProvider: Created with resource: $resource');
      47              :     }
      48              :   }
      49              : 
      50            2 :   @override
      51            4 :   String get endpoint => delegate.endpoint;
      52              : 
      53            2 :   @override
      54            4 :   set endpoint(String value) => delegate.endpoint = value;
      55              : 
      56            2 :   @override
      57            4 :   String get serviceName => delegate.serviceName;
      58              : 
      59            2 :   @override
      60            4 :   set serviceName(String value) => delegate.serviceName = value;
      61              : 
      62            2 :   @override
      63            4 :   String? get serviceVersion => delegate.serviceVersion;
      64              : 
      65            2 :   @override
      66            4 :   set serviceVersion(String? value) => delegate.serviceVersion = value;
      67              : 
      68           29 :   @override
      69              :   bool get enabled {
      70           29 :     return _enabledOverride ?? true;
      71              :   }
      72              : 
      73              :   // Track explicit enablement settings
      74              :   bool? _enabledOverride;
      75              : 
      76           10 :   @override
      77              :   set enabled(bool value) {
      78           10 :     _enabledOverride = value;
      79           20 :     delegate.enabled = value;
      80              :   }
      81              : 
      82          131 :   @override
      83          262 :   bool get isShutdown => delegate.isShutdown;
      84              : 
      85          130 :   @override
      86          260 :   set isShutdown(bool value) => delegate.isShutdown = value;
      87              : 
      88           31 :   @override
      89              :   APIMeter getMeter({
      90              :     required String name,
      91              :     String? version,
      92              :     String? schemaUrl,
      93              :     Attributes? attributes,
      94              :   }) {
      95              :     // Check if provider is shutdown
      96           31 :     if (isShutdown) {
      97              :       // Return a no-op meter instead of throwing
      98            1 :       if (OTelLog.isDebug()) {
      99            1 :         OTelLog.debug(
     100            1 :           'MeterProvider: Attempting to get meter "$name" after shutdown. Returning a no-op meter.',
     101              :         );
     102              :       }
     103            1 :       return NoopMeter(name: name, version: version, schemaUrl: schemaUrl);
     104              :     }
     105              : 
     106              :     // Create a unique key for this meter
     107           31 :     final meterKey = '$name:${version ?? ''}:${schemaUrl ?? ''}';
     108              : 
     109              :     // Return an existing meter if we already have one with this configuration
     110           62 :     if (_meters.containsKey(meterKey)) {
     111            6 :       return _meters[meterKey]!;
     112              :     }
     113              : 
     114              :     // Call the API implementation first
     115           62 :     final apiMeter = delegate.getMeter(
     116              :       name: name,
     117              :       version: version,
     118              :       schemaUrl: schemaUrl,
     119              :       attributes: attributes,
     120              :     );
     121              : 
     122              :     // Wrap it with our SDK implementation
     123           31 :     final meter = MeterCreate.create(delegate: apiMeter, provider: this);
     124              : 
     125              :     // Store the meter in the registry
     126           62 :     _meters[meterKey] = meter;
     127              : 
     128              :     // Initialize the instruments set for this meter
     129           62 :     _instruments[meterKey] = {};
     130              : 
     131           31 :     if (OTelLog.isLogMetrics()) {
     132           28 :       OTelLog.logMetric(
     133           28 :         'MeterProvider: Created meter "$name" (version: $version)',
     134              :       );
     135              :     }
     136              : 
     137              :     return meter;
     138              :   }
     139              : 
     140              :   /// Adds a MetricReader to this MeterProvider.
     141              :   ///
     142              :   /// MetricReaders are responsible for collecting and exporting metrics.
     143              :   /// They can be configured to collect metrics at different intervals and
     144              :   /// export them to different backends.
     145              :   ///
     146              :   /// @param reader The MetricReader to add
     147          130 :   void addMetricReader(MetricReader reader) {
     148          260 :     if (!_metricReaders.contains(reader)) {
     149          260 :       _metricReaders.add(reader);
     150          130 :       reader.registerMeterProvider(this);
     151              :     }
     152              :   }
     153              : 
     154              :   /// Adds a View to this MeterProvider.
     155              :   ///
     156              :   /// Views allow for customizing how metrics are collected and aggregated.
     157              :   /// They can be used to filter, transform, and aggregate metrics before
     158              :   /// they are exported.
     159              :   ///
     160              :   /// @param view The View to add
     161            2 :   void addView(View view) {
     162            4 :     _views.add(view);
     163              :   }
     164              : 
     165              :   /// Gets all views configured for this MeterProvider.
     166              :   ///
     167              :   /// @return An unmodifiable list of all views
     168            6 :   List<View> get views => List.unmodifiable(_views);
     169              : 
     170              :   /// Gets all metric readers associated with this MeterProvider.
     171              :   ///
     172              :   /// @return An unmodifiable list of all metric readers
     173           24 :   List<MetricReader> get metricReaders => List.unmodifiable(_metricReaders);
     174              : 
     175              :   /// Registry of all meters created by this provider
     176              :   final Map<String, Meter> _meters = {};
     177              : 
     178              :   /// Registry of active instruments across all meters
     179              :   final Map<String, Set<SDKInstrument>> _instruments = {};
     180              : 
     181              :   /// Registers an instrument with this provider.
     182              :   ///
     183              :   /// This allows the provider to track all active instruments for metrics collection.
     184              :   ///
     185              :   /// @param instrumentName The name of the instrument
     186              :   /// @param instrument The instrument to register
     187           29 :   void registerInstrument(String instrumentName, SDKInstrument instrument) {
     188           58 :     final meterKey = instrument.meter.name;
     189           58 :     if (!_instruments.containsKey(meterKey)) {
     190           58 :       _instruments[meterKey] = {};
     191              :     }
     192              : 
     193           87 :     _instruments[meterKey]!.add(instrument);
     194              : 
     195           29 :     if (OTelLog.isLogMetrics()) {
     196           26 :       OTelLog.logMetric(
     197          104 :         'MeterProvider: Registered instrument "${instrument.name}" for meter "${instrument.meter.name}"',
     198              :       );
     199              :     }
     200              :   }
     201              : 
     202              :   /// Collects all metrics from all instruments across all meters.
     203              :   ///
     204              :   /// This is called by metric readers to gather the current metrics.
     205              :   ///
     206              :   /// @return A list of all collected metrics
     207          126 :   Future<List<Metric>> collectAllMetrics() async {
     208          126 :     if (isShutdown) {
     209          115 :       return [];
     210              :     }
     211              : 
     212           17 :     final allMetrics = <Metric>[];
     213              : 
     214              :     // Collect from each meter's instruments
     215           48 :     for (final entry in _instruments.entries) {
     216           14 :       final meterName = entry.key;
     217           14 :       final instruments = entry.value;
     218              : 
     219           14 :       if (OTelLog.isLogMetrics()) {
     220           11 :         OTelLog.logMetric(
     221           22 :           'MeterProvider: Collecting metrics from ${instruments.length} instruments in meter "$meterName"',
     222              :         );
     223              :       }
     224              : 
     225              :       // Collect metrics from each instrument
     226           28 :       for (final instrument in instruments) {
     227              :         try {
     228           14 :           final metrics = instrument.collectMetrics();
     229           14 :           if (metrics.isNotEmpty) {
     230           14 :             allMetrics.addAll(metrics);
     231              : 
     232           14 :             if (OTelLog.isLogMetrics()) {
     233           11 :               OTelLog.logMetric(
     234           33 :                 'MeterProvider: Collected ${metrics.length} metrics from instrument "${instrument.name}"',
     235              :               );
     236              :             }
     237              :           }
     238              :         } catch (e) {
     239            0 :           if (OTelLog.isLogMetrics()) {
     240            0 :             OTelLog.logMetric(
     241            0 :               'MeterProvider: Error collecting metrics from instrument "${instrument.name}": $e',
     242              :             );
     243              :           }
     244              :         }
     245              :       }
     246              :     }
     247              : 
     248           17 :     if (OTelLog.isLogMetrics()) {
     249           14 :       OTelLog.logMetric(
     250           28 :         'MeterProvider: Collected ${allMetrics.length} total metrics',
     251              :       );
     252              :     }
     253              : 
     254              :     return allMetrics;
     255              :   }
     256              : 
     257              :   /// Force flushes metrics through all associated MetricReaders.
     258              :   ///
     259              :   /// This method forces an immediate collection and export of metrics
     260              :   /// through all registered metric readers.
     261              :   ///
     262              :   /// @return true if all flushes were successful, false otherwise
     263            4 :   @override
     264              :   Future<bool> forceFlush() async {
     265            4 :     if (isShutdown) {
     266            2 :       if (OTelLog.isLogExport()) {
     267            2 :         OTelLog.logExport('MeterProvider: Cannot flush after shutdown');
     268              :       }
     269              :       return false;
     270              :     }
     271              : 
     272            4 :     if (OTelLog.isLogExport()) {
     273            3 :       OTelLog.logExport(
     274            9 :         'MeterProvider: Force flushing metrics through ${_metricReaders.length} readers',
     275              :       );
     276              :     }
     277              : 
     278              :     var success = true;
     279            8 :     for (final reader in _metricReaders) {
     280            4 :       final result = await reader.forceFlush();
     281              :       success = success && result;
     282              :     }
     283              :     return success;
     284              :   }
     285              : 
     286              :   /// Shuts down this MeterProvider and all associated resources.
     287              :   ///
     288              :   /// This method shuts down all metric readers and prevents the creation
     289              :   /// of new meters. Any subsequent calls to getMeter() will return a no-op
     290              :   /// meter.
     291              :   ///
     292              :   /// @return true if shutdown was successful, false otherwise
     293          130 :   @override
     294              :   Future<bool> shutdown() async {
     295          130 :     if (isShutdown) {
     296              :       return true; // Already shut down
     297              :     }
     298              : 
     299              :     // Mark as shut down immediately to prevent new interactions
     300          130 :     isShutdown = true;
     301              : 
     302              :     var success = true;
     303              : 
     304              :     // Shutdown all metric readers
     305          258 :     for (final reader in _metricReaders) {
     306          128 :       final result = await reader.shutdown();
     307              :       success = success && result;
     308              :     }
     309              : 
     310              :     // Clear collections
     311          260 :     _metricReaders.clear();
     312          260 :     _views.clear();
     313              : 
     314              :     // Finally call the underlying API implementation
     315          260 :     await delegate.shutdown();
     316              : 
     317              :     return success;
     318              :   }
     319              : }
        

Generated by: LCOV version 2.0-1