-
Notifications
You must be signed in to change notification settings - Fork 698
/
Copy pathspan.py
289 lines (215 loc) · 8.22 KB
/
span.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import abc
import types as python_types
import typing
from opentelemetry.trace.status import Status
from opentelemetry.util import types
class Span(abc.ABC):
"""A span represents a single operation within a trace."""
@abc.abstractmethod
def end(self, end_time: typing.Optional[int] = None) -> None:
"""Sets the current time as the span's end time.
The span's end time is the wall time at which the operation finished.
Only the first call to `end` should modify the span, and
implementations are free to ignore or raise on further calls.
"""
@abc.abstractmethod
def get_context(self) -> "SpanContext":
"""Gets the span's SpanContext.
Get an immutable, serializable identifier for this span that can be
used to create new child spans.
Returns:
A :class:`opentelemetry.trace.SpanContext` with a copy of this span's immutable state.
"""
@abc.abstractmethod
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
"""Sets an Attribute.
Sets a single Attribute with the key and value passed as arguments.
"""
@abc.abstractmethod
def add_event(
self,
name: str,
attributes: types.Attributes = None,
timestamp: typing.Optional[int] = None,
) -> None:
"""Adds an `Event`.
Adds a single `Event` with the name and, optionally, a timestamp and
attributes passed as arguments. Implementations should generate a
timestamp if the `timestamp` argument is omitted.
"""
@abc.abstractmethod
def add_lazy_event(
self,
name: str,
event_formatter: types.AttributesFormatter,
timestamp: typing.Optional[int] = None,
) -> None:
"""Adds an `Event`.
Adds a single `Event` with the name, an event formatter that calculates
the attributes lazily and, optionally, a timestamp. Implementations
should generate a timestamp if the `timestamp` argument is omitted.
"""
@abc.abstractmethod
def update_name(self, name: str) -> None:
"""Updates the `Span` name.
This will override the name provided via :func:`opentelemetry.trace.Tracer.start_span`.
Upon this update, any sampling behavior based on Span name will depend
on the implementation.
"""
@abc.abstractmethod
def is_recording_events(self) -> bool:
"""Returns whether this span will be recorded.
Returns true if this Span is active and recording information like
events with the add_event operation and attributes using set_attribute.
"""
@abc.abstractmethod
def set_status(self, status: Status) -> None:
"""Sets the Status of the Span. If used, this will override the default
Span status, which is OK.
"""
@abc.abstractmethod
def record_exception(self, exception: Exception) -> None:
"""Records an exception as a span event."""
def __enter__(self) -> "Span":
"""Invoked when `Span` is used as a context manager.
Returns the `Span` itself.
"""
return self
def __exit__(
self,
exc_type: typing.Optional[typing.Type[BaseException]],
exc_val: typing.Optional[BaseException],
exc_tb: typing.Optional[python_types.TracebackType],
) -> None:
"""Ends context manager and calls `end` on the `Span`."""
self.end()
class TraceFlags(int):
"""A bitmask that represents options specific to the trace.
The only supported option is the "sampled" flag (``0x01``). If set, this
flag indicates that the trace may have been sampled upstream.
See the `W3C Trace Context - Traceparent`_ spec for details.
.. _W3C Trace Context - Traceparent:
https://www.w3.org/TR/trace-context/#trace-flags
"""
DEFAULT = 0x00
SAMPLED = 0x01
@classmethod
def get_default(cls) -> "TraceFlags":
return cls(cls.DEFAULT)
@property
def sampled(self) -> bool:
return bool(self & TraceFlags.SAMPLED)
DEFAULT_TRACE_OPTIONS = TraceFlags.get_default()
class TraceState(typing.Dict[str, str]):
"""A list of key-value pairs representing vendor-specific trace info.
Keys and values are strings of up to 256 printable US-ASCII characters.
Implementations should conform to the `W3C Trace Context - Tracestate`_
spec, which describes additional restrictions on valid field values.
.. _W3C Trace Context - Tracestate:
https://www.w3.org/TR/trace-context/#tracestate-field
"""
@classmethod
def get_default(cls) -> "TraceState":
return cls()
DEFAULT_TRACE_STATE = TraceState.get_default()
class SpanContext:
"""The state of a Span to propagate between processes.
This class includes the immutable attributes of a :class:`.Span` that must
be propagated to a span's children and across process boundaries.
Args:
trace_id: The ID of the trace that this span belongs to.
span_id: This span's ID.
trace_flags: Trace options to propagate.
trace_state: Tracing-system-specific info to propagate.
is_remote: True if propagated from a remote parent.
"""
def __init__(
self,
trace_id: int,
span_id: int,
is_remote: bool,
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
trace_state: "TraceState" = DEFAULT_TRACE_STATE,
) -> None:
if trace_flags is None:
trace_flags = DEFAULT_TRACE_OPTIONS
if trace_state is None:
trace_state = DEFAULT_TRACE_STATE
self.trace_id = trace_id
self.span_id = span_id
self.trace_flags = trace_flags
self.trace_state = trace_state
self.is_remote = is_remote
def __repr__(self) -> str:
return (
"{}(trace_id={}, span_id={}, trace_state={!r}, is_remote={})"
).format(
type(self).__name__,
format_trace_id(self.trace_id),
format_span_id(self.span_id),
self.trace_state,
self.is_remote,
)
def is_valid(self) -> bool:
"""Get whether this `SpanContext` is valid.
A `SpanContext` is said to be invalid if its trace ID or span ID is
invalid (i.e. ``0``).
Returns:
True if the `SpanContext` is valid, false otherwise.
"""
return (
self.trace_id != INVALID_TRACE_ID
and self.span_id != INVALID_SPAN_ID
)
class DefaultSpan(Span):
"""The default Span that is used when no Span implementation is available.
All operations are no-op except context propagation.
"""
def __init__(self, context: "SpanContext") -> None:
self._context = context
def get_context(self) -> "SpanContext":
return self._context
def is_recording_events(self) -> bool:
return False
def end(self, end_time: typing.Optional[int] = None) -> None:
pass
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
pass
def add_event(
self,
name: str,
attributes: types.Attributes = None,
timestamp: typing.Optional[int] = None,
) -> None:
pass
def add_lazy_event(
self,
name: str,
event_formatter: types.AttributesFormatter,
timestamp: typing.Optional[int] = None,
) -> None:
pass
def update_name(self, name: str) -> None:
pass
def set_status(self, status: Status) -> None:
pass
def record_exception(self, exception: Exception) -> None:
pass
INVALID_SPAN_ID = 0x0000000000000000
INVALID_TRACE_ID = 0x00000000000000000000000000000000
INVALID_SPAN_CONTEXT = SpanContext(
trace_id=INVALID_TRACE_ID,
span_id=INVALID_SPAN_ID,
is_remote=False,
trace_flags=DEFAULT_TRACE_OPTIONS,
trace_state=DEFAULT_TRACE_STATE,
)
INVALID_SPAN = DefaultSpan(INVALID_SPAN_CONTEXT)
def format_trace_id(trace_id: int) -> str:
return "0x{:032x}".format(trace_id)
def format_span_id(span_id: int) -> str:
return "0x{:016x}".format(span_id)
def get_hexadecimal_trace_id(trace_id: int) -> str:
return "{:032x}".format(trace_id)
def get_hexadecimal_span_id(span_id: int) -> str:
return "{:016x}".format(span_id)