Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : library;
5 :
6 : import 'package:meta/meta.dart';
7 : import '../../dartastic_opentelemetry.dart';
8 : import 'tracer.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: Event Vision](https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/0265-event-vision.md)
23 : /// and [OTEP 4430: Span Event API deprecation plan](https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/4430-span-event-api-deprecation-plan.md),
24 : /// span events are planned for deprecation in favor of log-based events
25 : /// emitted via the Logs API; SDKs will provide options to render log-based
26 : /// events as span events for compatibility.
27 : ///
28 : /// More information:
29 : /// https://opentelemetry.io/docs/specs/otel/trace/sdk/
30 : class TracerProvider implements APITracerProvider {
31 : /// Registry of tracers managed by this provider.
32 : final Map<String, Tracer> _tracers = {};
33 :
34 : /// Span processors registered with this provider.
35 : final List<SpanProcessor> _spanProcessors = [];
36 :
37 : /// The underlying API TracerProvider implementation.
38 : final APITracerProvider _delegate;
39 :
40 : /// The resource associated with this provider.
41 : Resource? resource;
42 :
43 : /// The default sampler to use for new tracers.
44 : Sampler? sampler;
45 :
46 : /// The default exception handling options applied by [Tracer.withSpan] /
47 : /// [Tracer.withSpanAsync] for tracers created by this provider when no
48 : /// per-call options are supplied.
49 : ///
50 : /// Set globally via `OTel.initialize(spanExceptionOptions: ...)`. When
51 : /// null, a default [SpanExceptionOptions] (record exception + set error
52 : /// status) is used.
53 : SpanExceptionOptions? spanExceptionOptions;
54 :
55 : /// Clock used for span start, end, and event timestamps. Delegates to the
56 : /// underlying [APITracerProvider] so the SDK and API share a single
57 : /// source of truth. Defaults are platform-aware (native:
58 : /// `SystemTimeProvider`; web: `WebTimeProvider`); override via
59 : /// `OTel.initialize(timeProvider: ...)` or by assigning here.
60 1 : @override
61 2 : TimeProvider get timeProvider => _delegate.timeProvider;
62 2 : @override
63 4 : set timeProvider(TimeProvider value) => _delegate.timeProvider = value;
64 :
65 153 : @override
66 306 : bool get isShutdown => _delegate.isShutdown;
67 :
68 151 : @override
69 : set isShutdown(bool value) {
70 302 : _delegate.isShutdown = value;
71 : }
72 :
73 : /// Private constructor for creating TracerProvider instances.
74 : ///
75 : /// @param delegate The API TracerProvider implementation to delegate to
76 : /// @param resource Optional Resource describing the entity producing telemetry
77 : /// @param sampler Optional default sampler for tracers created by this provider
78 153 : TracerProvider._({
79 : required APITracerProvider delegate,
80 : this.resource,
81 : Sampler? sampler,
82 : }) : _delegate = delegate {
83 153 : if (OTelLog.isDebug()) {
84 133 : OTelLog.debug(
85 266 : 'TracerProvider: Created with resource: $resource, sampler: $sampler',
86 : );
87 133 : if (resource != null) {
88 0 : OTelLog.debug('Resource attributes:');
89 0 : resource!.attributes.toList().forEach((attr) {
90 0 : OTelLog.debug(' ${attr.key}: ${attr.value}');
91 : });
92 : }
93 : }
94 : }
95 :
96 151 : @override
97 : Future<bool> shutdown() async {
98 151 : if (OTelLog.isDebug()) {
99 138 : OTelLog.debug(
100 414 : 'TracerProvider: Shutting down with ${_spanProcessors.length} processors',
101 : );
102 : }
103 151 : if (OTelLog.isDebug()) {
104 138 : OTelLog.debug(
105 414 : 'TracerProvider: Shutting down with ${_spanProcessors.length} processors',
106 : );
107 : }
108 :
109 151 : if (!isShutdown) {
110 : // Shutdown all span processors
111 292 : for (final processor in _spanProcessors) {
112 141 : if (OTelLog.isDebug()) {
113 137 : OTelLog.debug(
114 274 : 'TracerProvider: Shutting down processor ${processor.runtimeType}',
115 : );
116 : }
117 141 : if (OTelLog.isDebug()) {
118 137 : OTelLog.debug(
119 274 : 'SDKTracerProvider: Shutting down processor ${processor.runtimeType}',
120 : );
121 : }
122 : try {
123 141 : await processor.shutdown();
124 141 : if (OTelLog.isDebug()) {
125 137 : OTelLog.debug(
126 274 : 'TracerProvider: Successfully shut down processor ${processor.runtimeType}',
127 : );
128 : }
129 : } catch (e) {
130 1 : if (OTelLog.isDebug()) {
131 1 : OTelLog.debug(
132 2 : 'TracerProvider: Error shutting down processor ${processor.runtimeType}: $e',
133 : );
134 : }
135 : }
136 : }
137 :
138 : // Clear cached tracers
139 302 : _tracers.clear();
140 151 : if (OTelLog.isDebug()) {
141 138 : OTelLog.debug('TracerProvider: Cleared cached tracers');
142 : }
143 :
144 : try {
145 302 : await _delegate.shutdown();
146 151 : if (OTelLog.isDebug()) {
147 138 : OTelLog.debug('TracerProvider: Delegate shutdown complete');
148 : }
149 : } catch (e) {
150 0 : if (OTelLog.isDebug()) {
151 0 : OTelLog.debug('TracerProvider: Error during delegate shutdown: $e');
152 : }
153 : }
154 :
155 151 : isShutdown = true;
156 289 : if (OTelLog.isDebug()) OTelLog.debug('TracerProvider: Shutdown complete');
157 : } else {
158 185 : if (OTelLog.isDebug()) OTelLog.debug('TracerProvider: Already shut down');
159 : }
160 151 : return isShutdown;
161 : }
162 :
163 60 : @override
164 : Tracer getTracer(
165 : String name, {
166 : String? version,
167 : String? schemaUrl,
168 : Attributes? attributes,
169 : Sampler? sampler,
170 : }) {
171 60 : if (OTelLog.isDebug()) {
172 55 : OTelLog.debug(
173 55 : 'TracerProvider: Getting tracer with name: $name, version: $version, schemaUrl: $schemaUrl',
174 : );
175 : }
176 60 : if (isShutdown) {
177 1 : throw StateError('TracerProvider has been shut down');
178 : }
179 :
180 : // Ensure resource is set before creating tracer
181 60 : ensureResourceIsSet();
182 :
183 60 : final key = '$name:${version ?? ''}';
184 120 : return _tracers.putIfAbsent(
185 : key,
186 120 : () => SDKTracerCreate.create(
187 120 : delegate: _delegate.getTracer(
188 : name,
189 : version: version,
190 : schemaUrl: schemaUrl,
191 : attributes: attributes,
192 : ),
193 : provider: this,
194 : sampler: sampler,
195 : ) as Tracer,
196 : );
197 : }
198 :
199 : /// Adds a span processor to this provider.
200 : ///
201 : /// Span processors are notified of span lifecycle events and are responsible
202 : /// for additional processing of spans, such as exporting them.
203 : ///
204 : /// @param processor The span processor to add
205 143 : void addSpanProcessor(SpanProcessor processor) {
206 143 : if (isShutdown) {
207 1 : throw StateError('TracerProvider has been shut down');
208 : }
209 143 : if (OTelLog.isDebug()) {
210 133 : OTelLog.debug(
211 266 : 'SDKTracerProvider: Adding span processor of type ${processor.runtimeType}',
212 : );
213 : }
214 286 : _spanProcessors.add(processor);
215 : }
216 :
217 : /// Gets all registered span processors.
218 : ///
219 : /// @return An unmodifiable list of all span processors
220 192 : List<SpanProcessor> get spanProcessors => List.unmodifiable(_spanProcessors);
221 :
222 : /// Whether any span processors are registered.
223 : ///
224 : /// Unlike [spanProcessors], this does not allocate a new unmodifiable list,
225 : /// making it suitable for hot paths such as [Tracer.isEnabled].
226 9 : bool get hasSpanProcessors => _spanProcessors.isNotEmpty;
227 :
228 : /// Ensures the resource for this provider is properly set.
229 : ///
230 : /// If no resource has been set, the default resource will be used.
231 60 : void ensureResourceIsSet() {
232 60 : if (resource == null) {
233 3 : resource = OTel.defaultResource;
234 3 : if (OTelLog.isDebug()) {
235 3 : OTelLog.debug('TracerProvider: Setting resource from default');
236 3 : if (resource != null) {
237 2 : OTelLog.debug('Resource attributes:');
238 10 : resource!.attributes.toList().forEach((attr) {
239 6 : if (attr.key == Service.serviceName.key) {
240 8 : OTelLog.debug(' ${attr.key}: ${attr.value}');
241 : }
242 : });
243 : }
244 : }
245 : }
246 : }
247 :
248 1 : @override
249 2 : String get endpoint => _delegate.endpoint;
250 :
251 1 : @override
252 : set endpoint(String value) {
253 2 : _delegate.endpoint = value;
254 : }
255 :
256 1 : @override
257 2 : String get serviceName => _delegate.serviceName;
258 :
259 1 : @override
260 : set serviceName(String value) {
261 2 : _delegate.serviceName = value;
262 : }
263 :
264 1 : @override
265 2 : String? get serviceVersion => _delegate.serviceVersion;
266 :
267 1 : @override
268 : set serviceVersion(String? value) {
269 2 : _delegate.serviceVersion = value;
270 : }
271 :
272 1 : @override
273 2 : bool get enabled => _delegate.enabled;
274 :
275 1 : @override
276 : set enabled(bool value) {
277 2 : _delegate.enabled = value;
278 : }
279 :
280 : /// Forces all span processors to flush any queued spans.
281 : ///
282 : /// This method is useful for ensuring that all spans are exported
283 : /// before the application terminates or when immediate visibility
284 : /// of spans is required.
285 : ///
286 : /// @return A Future that completes when all processors have been flushed
287 151 : Future<void> forceFlush() async {
288 151 : if (OTelLog.isDebug()) {
289 138 : OTelLog.debug(
290 414 : 'TracerProvider: Force flushing ${_spanProcessors.length} processors',
291 : );
292 : }
293 :
294 151 : if (isShutdown) {
295 98 : if (OTelLog.isDebug()) {
296 87 : OTelLog.debug(
297 : 'TracerProvider: Cannot force flush - provider is shut down',
298 : );
299 : }
300 : return;
301 : }
302 :
303 282 : for (var processor in _spanProcessors) {
304 : try {
305 136 : if (OTelLog.isDebug()) {
306 132 : OTelLog.debug(
307 264 : 'TracerProvider: Flushing processor ${processor.runtimeType}',
308 : );
309 : }
310 136 : await processor.forceFlush();
311 136 : if (OTelLog.isDebug()) {
312 132 : OTelLog.debug(
313 264 : 'TracerProvider: Successfully flushed processor ${processor.runtimeType}',
314 : );
315 : }
316 : } catch (e) {
317 1 : if (OTelLog.isDebug()) {
318 1 : OTelLog.debug(
319 2 : 'TracerProvider: Error flushing processor ${processor.runtimeType}: $e',
320 : );
321 : }
322 : }
323 : }
324 :
325 146 : if (OTelLog.isDebug()) {
326 133 : OTelLog.debug('TracerProvider: Force flush complete');
327 : }
328 : }
329 : }
|