Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart'
5 : show OTelLog;
6 :
7 : import '../data/metric_data.dart';
8 : import '../metric_exporter.dart';
9 :
10 : /// A composite metric exporter that delegates to multiple exporters.
11 : ///
12 : /// This exporter implements the fan-out pattern, where metrics are exported
13 : /// to multiple backends simultaneously. It forwards all export, flush, and
14 : /// shutdown operations to each of its delegate exporters.
15 : ///
16 : /// This is useful for scenarios where you want to send metrics to multiple
17 : /// destinations, such as a local console and a remote collector.
18 : ///
19 : /// More information:
20 : /// https://opentelemetry.io/docs/specs/otel/metrics/sdk/#metricexporter
21 : class CompositeMetricExporter implements MetricExporter {
22 : /// The list of delegate exporters.
23 : final List<MetricExporter> _exporters;
24 :
25 : /// Whether this exporter has been shut down.
26 : bool _shutdown = false;
27 :
28 : /// Creates a new CompositeMetricExporter with the given list of exporters.
29 : ///
30 : /// @param exporters The list of exporters to which operations will be delegated
31 2 : CompositeMetricExporter(this._exporters);
32 :
33 : /// The delegate exporters this composite forwards to.
34 0 : List<MetricExporter> get exporters => List.unmodifiable(_exporters);
35 :
36 : /// Exports metrics to all delegate exporters.
37 : ///
38 : /// This method forwards the export operation to each delegate exporter.
39 : /// If any exporter fails, the composite exporter will still try to export
40 : /// to the remaining exporters, but will return false to indicate failure.
41 : ///
42 : /// @param data The metric data to export
43 : /// @return true if all exporters succeeded, false if any exporter failed
44 2 : @override
45 : Future<bool> export(MetricData data) async {
46 2 : if (_shutdown) {
47 1 : if (OTelLog.isLogExport()) {
48 1 : OTelLog.logExport(
49 : 'CompositeMetricExporter: Cannot export after shutdown',
50 : );
51 : }
52 : return false;
53 : }
54 :
55 : var success = true;
56 4 : for (final exporter in _exporters) {
57 : try {
58 2 : final result = await exporter.export(data);
59 : success = success && result;
60 : } catch (e) {
61 1 : if (OTelLog.isLogExport()) {
62 1 : OTelLog.logExport(
63 1 : 'CompositeMetricExporter: Export failed for $exporter: $e',
64 : );
65 : }
66 : success = false;
67 : }
68 : }
69 :
70 : return success;
71 : }
72 :
73 : /// Forces a flush of all delegate exporters.
74 : ///
75 : /// This method forwards the flush operation to each delegate exporter.
76 : /// If any exporter fails to flush, the composite exporter will still try
77 : /// to flush the remaining exporters, but will return false to indicate failure.
78 : ///
79 : /// @return true if all exporters were flushed successfully, false otherwise
80 2 : @override
81 : Future<bool> forceFlush() async {
82 2 : if (_shutdown) {
83 : return false;
84 : }
85 :
86 : var success = true;
87 4 : for (final exporter in _exporters) {
88 : try {
89 2 : final result = await exporter.forceFlush();
90 : success = success && result;
91 : } catch (e) {
92 : success = false;
93 : }
94 : }
95 :
96 : return success;
97 : }
98 :
99 : /// Shuts down all delegate exporters.
100 : ///
101 : /// This method forwards the shutdown operation to each delegate exporter.
102 : /// Once shut down, this exporter will no longer accept export requests.
103 : ///
104 : /// @return true if all exporters were shut down successfully, false otherwise
105 2 : @override
106 : Future<bool> shutdown() async {
107 2 : if (_shutdown) {
108 : return true;
109 : }
110 :
111 2 : _shutdown = true;
112 : var success = true;
113 4 : for (final exporter in _exporters) {
114 : try {
115 2 : final result = await exporter.shutdown();
116 : success = success && result;
117 : } catch (e) {
118 : success = false;
119 : }
120 : }
121 :
122 : return success;
123 : }
124 : }
|