Line data Source code
1 : // Licensed under the Apache License, Version 2.0
2 : // Copyright 2025, Michael Bushe, All rights reserved.
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 69 : CompositeMetricExporter(this._exporters);
32 :
33 : /// Exports metrics to all delegate exporters.
34 : ///
35 : /// This method forwards the export operation to each delegate exporter.
36 : /// If any exporter fails, the composite exporter will still try to export
37 : /// to the remaining exporters, but will return false to indicate failure.
38 : ///
39 : /// @param data The metric data to export
40 : /// @return true if all exporters succeeded, false if any exporter failed
41 2 : @override
42 : Future<bool> export(MetricData data) async {
43 2 : if (_shutdown) {
44 0 : if (OTelLog.isLogExport()) {
45 0 : OTelLog.logExport(
46 : 'CompositeMetricExporter: Cannot export after shutdown');
47 : }
48 : return false;
49 : }
50 :
51 : bool success = true;
52 4 : for (final exporter in _exporters) {
53 : try {
54 2 : final result = await exporter.export(data);
55 : success = success && result;
56 : } catch (e) {
57 0 : if (OTelLog.isLogExport()) {
58 0 : OTelLog.logExport(
59 0 : 'CompositeMetricExporter: Export failed for $exporter: $e');
60 : }
61 : success = false;
62 : }
63 : }
64 :
65 : return success;
66 : }
67 :
68 : /// Forces a flush of all delegate exporters.
69 : ///
70 : /// This method forwards the flush operation to each delegate exporter.
71 : /// If any exporter fails to flush, the composite exporter will still try
72 : /// to flush the remaining exporters, but will return false to indicate failure.
73 : ///
74 : /// @return true if all exporters were flushed successfully, false otherwise
75 2 : @override
76 : Future<bool> forceFlush() async {
77 2 : if (_shutdown) {
78 : return false;
79 : }
80 :
81 : bool success = true;
82 4 : for (final exporter in _exporters) {
83 : try {
84 2 : final result = await exporter.forceFlush();
85 : success = success && result;
86 : } catch (e) {
87 : success = false;
88 : }
89 : }
90 :
91 : return success;
92 : }
93 :
94 : /// Shuts down all delegate exporters.
95 : ///
96 : /// This method forwards the shutdown operation to each delegate exporter.
97 : /// Once shut down, this exporter will no longer accept export requests.
98 : ///
99 : /// @return true if all exporters were shut down successfully, false otherwise
100 58 : @override
101 : Future<bool> shutdown() async {
102 58 : if (_shutdown) {
103 : return true;
104 : }
105 :
106 58 : _shutdown = true;
107 : bool success = true;
108 116 : for (final exporter in _exporters) {
109 : try {
110 58 : final result = await exporter.shutdown();
111 : success = success && result;
112 : } catch (e) {
113 : success = false;
114 : }
115 : }
116 :
117 : return success;
118 : }
119 : }
|