Line data Source code
1 : // Licensed under the Apache License, Version 2.0
2 : // Copyright 2025, Michael Bushe, All rights reserved.
3 :
4 : library;
5 :
6 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
7 :
8 : import '../../dartastic_opentelemetry.dart';
9 :
10 : part 'tracer_provider_create.dart';
11 :
12 : /// SDK implementation of the APITracerProvider interface.
13 : ///
14 : /// The TracerProvider is the entry point to the tracing API. It is responsible
15 : /// for creating and managing Tracers, as well as configuring the tracing
16 : /// pipeline via SpanProcessors and Exporters.
17 : ///
18 : /// This implementation delegates some functionality to the API TracerProvider
19 : /// implementation while adding SDK-specific behaviors like span processor management
20 : /// and resource association.
21 : ///
22 : /// Note: Per [OTEP 0265](https://opentelemetry.io/docs/specs/semconv/general/events/),
23 : /// span events are being deprecated and will be replaced by the Logging API in future versions.
24 : ///
25 : /// More information:
26 : /// https://opentelemetry.io/docs/specs/otel/trace/sdk/
27 : class TracerProvider implements APITracerProvider {
28 : /// Registry of tracers managed by this provider.
29 : final Map<String, Tracer> _tracers = {};
30 :
31 : /// Span processors registered with this provider.
32 : final List<SpanProcessor> _spanProcessors = [];
33 :
34 : /// The underlying API TracerProvider implementation.
35 : final APITracerProvider _delegate;
36 :
37 : /// The resource associated with this provider.
38 : Resource? resource;
39 :
40 : /// The default sampler to use for new tracers.
41 : Sampler? sampler;
42 :
43 73 : @override
44 146 : bool get isShutdown => _delegate.isShutdown;
45 :
46 72 : @override
47 : set isShutdown(bool value) {
48 144 : _delegate.isShutdown = value;
49 : }
50 :
51 : /// Private constructor for creating TracerProvider instances.
52 : ///
53 : /// @param delegate The API TracerProvider implementation to delegate to
54 : /// @param resource Optional Resource describing the entity producing telemetry
55 : /// @param sampler Optional default sampler for tracers created by this provider
56 73 : TracerProvider._({
57 : required APITracerProvider delegate,
58 : this.resource,
59 : Sampler? sampler,
60 : }) : _delegate = delegate {
61 73 : if (OTelLog.isDebug()) {
62 73 : OTelLog.debug(
63 146 : 'TracerProvider: Created with resource: $resource, sampler: $sampler');
64 73 : if (resource != null) {
65 0 : OTelLog.debug('Resource attributes:');
66 0 : resource!.attributes.toList().forEach((attr) {
67 0 : OTelLog.debug(' ${attr.key}: ${attr.value}');
68 : });
69 : }
70 : }
71 : }
72 :
73 72 : @override
74 : Future<bool> shutdown() async {
75 72 : if (OTelLog.isDebug()) {
76 71 : OTelLog.debug(
77 213 : 'TracerProvider: Shutting down with ${_spanProcessors.length} processors');
78 : }
79 72 : if (OTelLog.isDebug()) {
80 71 : OTelLog.debug(
81 213 : 'TracerProvider: Shutting down with ${_spanProcessors.length} processors');
82 : }
83 :
84 72 : if (!isShutdown) {
85 : // Shutdown all span processors
86 144 : for (final processor in _spanProcessors) {
87 72 : if (OTelLog.isDebug()) {
88 71 : OTelLog.debug(
89 142 : 'TracerProvider: Shutting down processor ${processor.runtimeType}');
90 : }
91 72 : if (OTelLog.isDebug()) {
92 71 : OTelLog.debug(
93 142 : 'SDKTracerProvider: Shutting down processor ${processor.runtimeType}');
94 : }
95 : try {
96 72 : await processor.shutdown();
97 72 : if (OTelLog.isDebug()) {
98 71 : OTelLog.debug(
99 142 : 'TracerProvider: Successfully shut down processor ${processor.runtimeType}');
100 : }
101 : } catch (e) {
102 1 : if (OTelLog.isDebug()) {
103 1 : OTelLog.debug(
104 2 : 'TracerProvider: Error shutting down processor ${processor.runtimeType}: $e');
105 : }
106 : }
107 : }
108 :
109 : // Clear cached tracers
110 144 : _tracers.clear();
111 72 : if (OTelLog.isDebug()) {
112 71 : OTelLog.debug('TracerProvider: Cleared cached tracers');
113 : }
114 :
115 : try {
116 144 : await _delegate.shutdown();
117 72 : if (OTelLog.isDebug()) {
118 71 : OTelLog.debug('TracerProvider: Delegate shutdown complete');
119 : }
120 : } catch (e) {
121 0 : if (OTelLog.isDebug()) {
122 0 : OTelLog.debug('TracerProvider: Error during delegate shutdown: $e');
123 : }
124 : }
125 :
126 72 : isShutdown = true;
127 143 : if (OTelLog.isDebug()) OTelLog.debug('TracerProvider: Shutdown complete');
128 : } else {
129 61 : if (OTelLog.isDebug()) OTelLog.debug('TracerProvider: Already shut down');
130 : }
131 72 : return isShutdown;
132 : }
133 :
134 29 : @override
135 : Tracer getTracer(
136 : String name, {
137 : String? version,
138 : String? schemaUrl,
139 : Attributes? attributes,
140 : Sampler? sampler,
141 : }) {
142 29 : if (OTelLog.isDebug()) {
143 29 : OTelLog.debug(
144 29 : 'TracerProvider: Getting tracer with name: $name, version: $version, schemaUrl: $schemaUrl');
145 : }
146 29 : if (isShutdown) {
147 1 : throw StateError('TracerProvider has been shut down');
148 : }
149 :
150 : // Ensure resource is set before creating tracer
151 29 : ensureResourceIsSet();
152 :
153 29 : final key = '$name:${version ?? ''}';
154 58 : return _tracers.putIfAbsent(
155 : key,
156 58 : () => SDKTracerCreate.create(
157 58 : delegate: _delegate.getTracer(
158 : name,
159 : version: version,
160 : schemaUrl: schemaUrl,
161 : attributes: attributes,
162 : ),
163 : provider: this,
164 : sampler: sampler,
165 : ) as Tracer,
166 : );
167 : }
168 :
169 : /// Adds a span processor to this provider.
170 : ///
171 : /// Span processors are notified of span lifecycle events and are responsible
172 : /// for additional processing of spans, such as exporting them.
173 : ///
174 : /// @param processor The span processor to add
175 73 : void addSpanProcessor(SpanProcessor processor) {
176 73 : if (isShutdown) {
177 1 : throw StateError('TracerProvider has been shut down');
178 : }
179 73 : if (OTelLog.isDebug()) {
180 73 : OTelLog.debug(
181 146 : 'SDKTracerProvider: Adding span processor of type ${processor.runtimeType}');
182 : }
183 146 : _spanProcessors.add(processor);
184 : }
185 :
186 : /// Gets all registered span processors.
187 : ///
188 : /// @return An unmodifiable list of all span processors
189 87 : List<SpanProcessor> get spanProcessors => List.unmodifiable(_spanProcessors);
190 :
191 : /// Ensures the resource for this provider is properly set.
192 : ///
193 : /// If no resource has been set, the default resource will be used.
194 29 : void ensureResourceIsSet() {
195 29 : if (resource == null) {
196 2 : resource = OTel.defaultResource;
197 2 : if (OTelLog.isDebug()) {
198 2 : OTelLog.debug('TracerProvider: Setting resource from default');
199 2 : if (resource != null) {
200 1 : OTelLog.debug('Resource attributes:');
201 5 : resource!.attributes.toList().forEach((attr) {
202 4 : if (attr.key == 'tenant_id' || attr.key == 'service.name') {
203 4 : OTelLog.debug(' ${attr.key}: ${attr.value}');
204 : }
205 : });
206 : }
207 : }
208 : }
209 : }
210 :
211 1 : @override
212 2 : String get endpoint => _delegate.endpoint;
213 :
214 1 : @override
215 : set endpoint(String value) {
216 2 : _delegate.endpoint = value;
217 : }
218 :
219 1 : @override
220 2 : String get serviceName => _delegate.serviceName;
221 :
222 1 : @override
223 : set serviceName(String value) {
224 2 : _delegate.serviceName = value;
225 : }
226 :
227 1 : @override
228 2 : String? get serviceVersion => _delegate.serviceVersion;
229 :
230 1 : @override
231 : set serviceVersion(String? value) {
232 2 : _delegate.serviceVersion = value;
233 : }
234 :
235 1 : @override
236 2 : bool get enabled => _delegate.enabled;
237 :
238 1 : @override
239 : set enabled(bool value) {
240 2 : _delegate.enabled = value;
241 : }
242 :
243 : /// Forces all span processors to flush any queued spans.
244 : ///
245 : /// This method is useful for ensuring that all spans are exported
246 : /// before the application terminates or when immediate visibility
247 : /// of spans is required.
248 : ///
249 : /// @return A Future that completes when all processors have been flushed
250 72 : Future<void> forceFlush() async {
251 72 : if (OTelLog.isDebug()) {
252 71 : OTelLog.debug(
253 213 : 'TracerProvider: Force flushing ${_spanProcessors.length} processors');
254 : }
255 :
256 72 : if (isShutdown) {
257 31 : if (OTelLog.isDebug()) {
258 30 : OTelLog.debug(
259 : 'TracerProvider: Cannot force flush - provider is shut down');
260 : }
261 : return;
262 : }
263 :
264 136 : for (var processor in _spanProcessors) {
265 : try {
266 68 : if (OTelLog.isDebug()) {
267 67 : OTelLog.debug(
268 134 : 'TracerProvider: Flushing processor ${processor.runtimeType}');
269 : }
270 68 : await processor.forceFlush();
271 68 : if (OTelLog.isDebug()) {
272 67 : OTelLog.debug(
273 134 : 'TracerProvider: Successfully flushed processor ${processor.runtimeType}');
274 : }
275 : } catch (e) {
276 1 : if (OTelLog.isDebug()) {
277 1 : OTelLog.debug(
278 2 : 'TracerProvider: Error flushing processor ${processor.runtimeType}: $e');
279 : }
280 : }
281 : }
282 :
283 68 : if (OTelLog.isDebug()) {
284 67 : OTelLog.debug('TracerProvider: Force flush complete');
285 : }
286 : }
287 : }
|