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