Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : library;
5 :
6 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
7 : import 'package:meta/meta.dart';
8 :
9 : import '../resource/resource.dart';
10 : import 'tracer.dart';
11 :
12 : part 'span_create.dart';
13 :
14 : /// SDK implementation of the APISpan interface.
15 : ///
16 : /// A Span represents a single operation within a trace. Spans can be nested
17 : /// to form a trace tree. Each trace contains a root span, which typically
18 : /// describes the entire operation and, optionally, one or more sub-spans
19 : /// for its sub-operations.
20 : ///
21 : /// This implementation delegates most functionality to the API Span implementation
22 : /// while adding SDK-specific behaviors like span processor notification.
23 : ///
24 : /// Note: Per [OTEP 0265](https://opentelemetry.io/docs/specs/semconv/general/events/),
25 : /// span events are being deprecated and will be replaced by the Logging API in future versions.
26 : ///
27 : /// More information:
28 : /// https://opentelemetry.io/docs/specs/otel/trace/sdk/
29 : class Span implements APISpan {
30 : final APISpan _delegate;
31 : final Tracer _sdkTracer;
32 :
33 : /// Private constructor for creating Span instances.
34 : ///
35 : /// @param delegate The API Span implementation to delegate to
36 : /// @param sdkTracer The SDK Tracer that created this Span
37 48 : Span._(APISpan delegate, Tracer sdkTracer)
38 : : _delegate = delegate,
39 : _sdkTracer = sdkTracer {
40 48 : if (OTelLog.isDebug()) {
41 144 : OTelLog.debug('SDKSpan: Created new span with name ${delegate.name}');
42 : }
43 : }
44 :
45 : /// Gets the resource associated with this span's tracer.
46 : ///
47 : /// @return The resource associated with this span
48 111 : Resource? get resource => _sdkTracer.resource;
49 :
50 42 : @override
51 : void end({DateTime? endTime, SpanStatusCode? spanStatus}) {
52 42 : if (OTelLog.isDebug()) {
53 42 : OTelLog.debug(
54 168 : 'SDKSpan: Starting to end span ${spanContext.spanId} with name $name',
55 : );
56 : }
57 :
58 : if (spanStatus != null) {
59 1 : setStatus(spanStatus);
60 : }
61 :
62 : try {
63 42 : if (OTelLog.isDebug()) {
64 126 : OTelLog.debug('SDKSpan: Calling delegate.end() for span $name');
65 : }
66 84 : _delegate.end(endTime: endTime, spanStatus: spanStatus);
67 42 : if (OTelLog.isDebug()) {
68 126 : OTelLog.debug('SDKSpan: Delegate.end() completed for span $name');
69 : }
70 :
71 : // Notify span processors that this span has ended
72 84 : final provider = _sdkTracer.provider;
73 42 : if (OTelLog.isDebug()) {
74 42 : OTelLog.debug(
75 126 : 'SDKSpan: Notifying ${provider.spanProcessors.length} span processors',
76 : );
77 : }
78 82 : for (final processor in provider.spanProcessors) {
79 : try {
80 40 : if (OTelLog.isDebug()) {
81 40 : OTelLog.debug(
82 80 : 'SDKSpan: Calling onEnd for processor ${processor.runtimeType}',
83 : );
84 : }
85 40 : processor.onEnd(this);
86 40 : if (OTelLog.isDebug()) {
87 40 : OTelLog.debug(
88 80 : 'SDKSpan: Successfully called onEnd for processor ${processor.runtimeType}',
89 : );
90 : }
91 : } catch (e, stackTrace) {
92 1 : if (OTelLog.isError()) {
93 1 : OTelLog.error(
94 2 : 'SDKSpan: Error calling onEnd for processor ${processor.runtimeType}: $e',
95 : );
96 2 : OTelLog.error('Stack trace: $stackTrace');
97 : }
98 : }
99 : }
100 : } catch (e, stackTrace) {
101 0 : if (OTelLog.isError()) OTelLog.error('SDKSpan: Error during end(): $e');
102 0 : if (OTelLog.isError()) OTelLog.error('Stack trace: $stackTrace');
103 : rethrow;
104 : }
105 : }
106 :
107 1 : @override
108 : set attributes(Attributes newAttributes) =>
109 2 : _delegate.attributes = newAttributes;
110 :
111 4 : @override
112 : void addAttributes(Attributes attributes) =>
113 8 : _delegate.addAttributes(attributes);
114 :
115 3 : @override
116 6 : void addEvent(SpanEvent spanEvent) => _delegate.addEvent(spanEvent);
117 :
118 6 : @override
119 : void addEventNow(String name, [Attributes? attributes]) =>
120 12 : _delegate.addEventNow(name, attributes);
121 :
122 1 : @override
123 : void addEvents(Map<String, Attributes?> spanEvents) =>
124 2 : _delegate.addEvents(spanEvents);
125 :
126 4 : @override
127 : void addLink(SpanContext spanContext, [Attributes? attributes]) =>
128 8 : _delegate.addLink(spanContext, attributes);
129 :
130 2 : @override
131 4 : void addSpanLink(SpanLink spanLink) => _delegate.addSpanLink(spanLink);
132 :
133 44 : @override
134 88 : DateTime? get endTime => _delegate.endTime;
135 :
136 9 : @override
137 18 : bool get isEnded => _delegate.isEnded;
138 :
139 7 : @override
140 14 : bool get isRecording => _delegate.isRecording;
141 :
142 39 : @override
143 78 : SpanKind get kind => _delegate.kind;
144 :
145 46 : @override
146 92 : String get name => _delegate.name;
147 :
148 38 : @override
149 76 : APISpan? get parentSpan => _delegate.parentSpan;
150 :
151 8 : @override
152 : void recordException(
153 : Object exception, {
154 : StackTrace? stackTrace,
155 : Attributes? attributes,
156 : bool? escaped,
157 : }) =>
158 16 : _delegate.recordException(
159 : exception,
160 : stackTrace: stackTrace,
161 : attributes: attributes,
162 : escaped: escaped,
163 : );
164 :
165 3 : @override
166 : void setBoolAttribute(String name, bool value) =>
167 6 : _delegate.setBoolAttribute(name, value);
168 :
169 2 : @override
170 : void setBoolListAttribute(String name, List<bool> value) =>
171 4 : _delegate.setBoolListAttribute(name, value);
172 :
173 3 : @override
174 : void setDoubleAttribute(String name, double value) =>
175 6 : _delegate.setDoubleAttribute(name, value);
176 :
177 2 : @override
178 : void setDoubleListAttribute(String name, List<double> value) =>
179 4 : _delegate.setDoubleListAttribute(name, value);
180 :
181 5 : @override
182 : void setIntAttribute(String name, int value) =>
183 10 : _delegate.setIntAttribute(name, value);
184 :
185 2 : @override
186 : void setIntListAttribute(String name, List<int> value) =>
187 4 : _delegate.setIntListAttribute(name, value);
188 :
189 10 : @override
190 : void setStatus(SpanStatusCode statusCode, [String? description]) {
191 20 : _delegate.setStatus(statusCode, description);
192 10 : if (OTelLog.isDebug()) {
193 10 : OTelLog.debug(
194 30 : 'SDKSpan: Set status to $statusCode for span ${spanContext.spanId}',
195 : );
196 : }
197 : }
198 :
199 5 : @override
200 : void setStringAttribute<T>(String name, String value) =>
201 10 : _delegate.setStringAttribute<T>(name, value);
202 :
203 2 : @override
204 : void setStringListAttribute<T>(String name, List<String> value) =>
205 4 : _delegate.setStringListAttribute<T>(name, value);
206 :
207 2 : @override
208 : void setDateTimeAsStringAttribute(String name, DateTime value) =>
209 4 : _delegate.setDateTimeAsStringAttribute(name, value);
210 :
211 48 : @override
212 96 : SpanContext get spanContext => _delegate.spanContext;
213 :
214 41 : @override
215 82 : List<SpanEvent>? get spanEvents => _delegate.spanEvents;
216 :
217 3 : @override
218 6 : SpanId get spanId => _delegate.spanId;
219 :
220 39 : @override
221 78 : List<SpanLink>? get spanLinks => _delegate.spanLinks;
222 :
223 39 : @override
224 78 : DateTime get startTime => _delegate.startTime;
225 :
226 41 : @override
227 82 : SpanStatusCode get status => _delegate.status;
228 :
229 41 : @override
230 82 : String? get statusDescription => _delegate.statusDescription;
231 :
232 2 : @override
233 : void updateName(String name) {
234 4 : _delegate.updateName(name);
235 :
236 4 : final provider = _sdkTracer.provider;
237 4 : for (final processor in provider.spanProcessors) {
238 2 : processor.onNameUpdate(this, name);
239 : }
240 : }
241 :
242 38 : @override
243 : InstrumentationScope get instrumentationScope =>
244 76 : _delegate.instrumentationScope;
245 :
246 6 : @override
247 12 : SpanContext? get parentSpanContext => _delegate.parentSpanContext;
248 :
249 34 : @override
250 : String toString() {
251 : final indent = ' ';
252 34 : final buffer = StringBuffer()
253 34 : ..writeln('Span {')
254 102 : ..writeln('$indent name: $name,')
255 102 : ..writeln('$indent spanContext: $spanContext,')
256 102 : ..writeln('$indent kind: $kind,')
257 112 : ..writeln('$indent parentSpan: ${parentSpan?.spanContext ?? "none"},')
258 102 : ..writeln('$indent instrumentationScope: $instrumentationScope,')
259 102 : ..writeln('$indent startTime: $startTime,')
260 102 : ..writeln('$indent endTime: $endTime,')
261 102 : ..writeln('$indent status: $status,')
262 102 : ..writeln('$indent statusDescription: $statusDescription,')
263 102 : ..writeln('$indent attributes: $attributes,');
264 :
265 : // Span Events
266 42 : if (spanEvents?.isNotEmpty ?? false) {
267 16 : buffer.writeln('$indent spanEvents: [');
268 24 : for (final e in spanEvents!) {
269 16 : buffer.writeln('$indent$indent$e,');
270 : }
271 16 : buffer.writeln('$indent ],');
272 : } else {
273 66 : buffer.writeln('$indent spanEvents: [],');
274 : }
275 :
276 : // Span Links
277 39 : if (spanLinks?.isNotEmpty ?? false) {
278 10 : buffer.writeln('$indent spanLinks: [');
279 15 : for (final l in spanLinks!) {
280 10 : buffer.writeln('$indent$indent$l,');
281 : }
282 10 : buffer.writeln('$indent ]');
283 : } else {
284 66 : buffer.writeln('$indent spanLinks: []');
285 : }
286 :
287 34 : buffer.writeln('}');
288 34 : return buffer.toString();
289 : }
290 :
291 : /// Returns whether this span context is valid
292 : /// A span context is valid when it has a non-zero traceId and a non-zero spanId.
293 9 : @override
294 18 : bool get isValid => spanContext.isValid;
295 :
296 40 : @visibleForTesting
297 : @override
298 : // ignore: invalid_use_of_visible_for_testing_member
299 80 : Attributes get attributes => _delegate.attributes;
300 :
301 : // This check is always true because the method is part of the interface implementation
302 : // and the delegate is already an APISpan.
303 : /// Checks if this object is an instance of the specified type.
304 : ///
305 : /// This method is used for type checking and compatibility with the API Span implementation.
306 : /// It returns true if the specified type is APISpan or the exact runtime type of this object.
307 : ///
308 : /// @param type The type to check against
309 : /// @return true if this object is an instance of the specified type, false otherwise
310 4 : bool isInstanceOf(Type type) => type == APISpan || runtimeType == type;
311 : }
|