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 118 : void registerMeterProvider(MeterProvider provider) {
23 118 : _meterProvider = provider;
24 : }
25 :
26 : /// Get the MeterProvider this reader is associated with.
27 236 : 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 10 : 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 117 : 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 117 : _startTimer();
76 : }
77 :
78 : /// How often metrics are exported.
79 2 : Duration get interval => _interval;
80 :
81 : /// Maximum time to wait for each export.
82 2 : Duration get timeout => _timeout;
83 :
84 : /// Start the periodic collection timer.
85 117 : void _startTimer() {
86 117 : _timer?.cancel();
87 353 : _timer = Timer.periodic(_interval, (_) => _collectAndExport());
88 : }
89 :
90 : /// Collect and export metrics.
91 116 : Future<void> _collectAndExport() async {
92 116 : if (meterProvider == null) return;
93 :
94 : try {
95 : // Collect metrics
96 115 : final data = await collect();
97 :
98 : // Export metrics
99 230 : if (data.metrics.isNotEmpty) {
100 6 : final exportFuture = _exporter.export(data);
101 :
102 : // Apply timeout to export
103 3 : await exportFuture.timeout(
104 3 : _timeout,
105 0 : onTimeout: () {
106 0 : print('Metric export timed out after $_timeout');
107 : return false;
108 : },
109 : );
110 : }
111 : } catch (e) {
112 0 : print('Error during metric collection/export: $e');
113 : }
114 : }
115 :
116 115 : @override
117 : Future<MetricData> collect() async {
118 115 : if (meterProvider == null) {
119 1 : if (OTelLog.isLogMetrics()) {
120 1 : OTelLog.logMetric(
121 : 'PeriodicExportingMetricReader: No meter provider registered',
122 : );
123 : }
124 : // Return an empty container with no metrics
125 1 : return MetricData.empty();
126 : }
127 :
128 : // Get the meter provider as an SDK MeterProvider to access the metric storage
129 115 : final sdkMeterProvider = meterProvider as MeterProvider;
130 :
131 : // Collect metrics from all instruments in the meter provider
132 115 : final metrics = await sdkMeterProvider.collectAllMetrics();
133 :
134 115 : if (OTelLog.isLogMetrics()) {
135 111 : OTelLog.logMetric(
136 222 : 'PeriodicExportingMetricReader: Collected ${metrics.length} metrics',
137 : );
138 : }
139 :
140 345 : return MetricData(resource: meterProvider!.resource, metrics: metrics);
141 : }
142 :
143 5 : @override
144 : Future<bool> forceFlush() async {
145 : try {
146 : // Collect and export immediately
147 5 : await _collectAndExport();
148 10 : return await _exporter.forceFlush();
149 : } catch (e) {
150 2 : print('Error during forceFlush: $e');
151 : return false;
152 : }
153 : }
154 :
155 116 : @override
156 : Future<bool> shutdown() async {
157 232 : _timer?.cancel();
158 116 : _timer = null;
159 :
160 : try {
161 : // Perform one final collection and export
162 116 : await _collectAndExport();
163 232 : return await _exporter.shutdown();
164 : } catch (e) {
165 2 : print('Error during shutdown: $e');
166 : return false;
167 : }
168 : }
169 : }
|