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

Generated by: LCOV version 2.0-1