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 : }
|