Skip to content

Commit b7b38a5

Browse files
albanreyang
authored andcommitted
Span: add attribute, event, link setter to the API (#83)
1 parent 74a9534 commit b7b38a5

File tree

3 files changed

+107
-5
lines changed

3 files changed

+107
-5
lines changed

opentelemetry-api/src/opentelemetry/trace/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import typing
6666

6767
from opentelemetry import loader
68+
from opentelemetry import types
6869

6970
# TODO: quarantine
7071
ParentSpan = typing.Optional[typing.Union['Span', 'SpanContext']]
@@ -102,6 +103,35 @@ def get_context(self) -> 'SpanContext':
102103
A :class:`.SpanContext` with a copy of this span's immutable state.
103104
"""
104105

106+
def set_attribute(self: 'Span',
107+
key: str,
108+
value: types.AttributeValue,
109+
) -> None:
110+
"""Sets an Attribute.
111+
112+
Sets a single Attribute with the key and value passed as arguments.
113+
"""
114+
115+
def add_event(self: 'Span',
116+
name: str,
117+
attributes: types.Attributes = None,
118+
) -> None:
119+
"""Adds an Event.
120+
121+
Adds a single Event with the name and, optionally, attributes passed
122+
as arguments.
123+
"""
124+
125+
def add_link(self: 'Span',
126+
link_target_context: 'SpanContext',
127+
attributes: types.Attributes = None,
128+
) -> None:
129+
"""Adds a Link to another span.
130+
131+
Adds a single Link from this Span to another Span identified by the
132+
`SpanContext` passed as argument.
133+
"""
134+
105135

106136
class TraceOptions(int):
107137
"""A bitmask that represents options specific to the trace.

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,27 +230,31 @@ def get_context(self):
230230

231231
def set_attribute(self: 'Span',
232232
key: str,
233-
value: 'types.AttributeValue'
233+
value: types.AttributeValue,
234234
) -> None:
235235
if self.attributes is Span.empty_attributes:
236236
self.attributes = BoundedDict(MAX_NUM_ATTRIBUTES)
237237
self.attributes[key] = value
238238

239239
def add_event(self: 'Span',
240240
name: str,
241-
attributes: 'types.Attributes',
241+
attributes: types.Attributes = None,
242242
) -> None:
243243
if self.events is Span.empty_events:
244244
self.events = BoundedList(MAX_NUM_EVENTS)
245+
if attributes is None:
246+
attributes = Span.empty_attributes
245247
self.events.append(Event(name, attributes))
246248

247249
def add_link(self: 'Span',
248-
context: 'trace_api.SpanContext',
249-
attributes: 'types.Attributes',
250+
link_target_context: 'trace_api.SpanContext',
251+
attributes: types.Attributes = None,
250252
) -> None:
251253
if self.links is Span.empty_links:
252254
self.links = BoundedList(MAX_NUM_LINKS)
253-
self.links.append(Link(context, attributes))
255+
if attributes is None:
256+
attributes = Span.empty_attributes
257+
self.links.append(Link(link_target_context, attributes))
254258

255259
def start(self):
256260
if self.start_time is None:

opentelemetry-sdk/tests/trace/test_trace.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,74 @@ def test_start_span_explicit(self):
114114
self.assertIs(tracer.get_current_span(), root)
115115
self.assertIsNotNone(child.end_time)
116116

117+
def test_span_members(self):
118+
context = contextvars.ContextVar('test_span_members')
119+
tracer = trace.Tracer(context)
120+
121+
other_context1 = trace_api.SpanContext(
122+
trace_id=trace.generate_trace_id(),
123+
span_id=trace.generate_span_id()
124+
)
125+
other_context2 = trace_api.SpanContext(
126+
trace_id=trace.generate_trace_id(),
127+
span_id=trace.generate_span_id()
128+
)
129+
130+
self.assertIsNone(tracer.get_current_span())
131+
132+
with tracer.start_span('root') as root:
133+
root.set_attribute('component', 'http')
134+
root.set_attribute('http.method', 'GET')
135+
root.set_attribute('http.url',
136+
'https://example.com:779/path/12/?q=d#123')
137+
root.set_attribute('http.status_code', 200)
138+
root.set_attribute('http.status_text', 'OK')
139+
root.set_attribute('misc.pi', 3.14)
140+
141+
# Setting an attribute with the same key as an existing attribute
142+
# SHOULD overwrite the existing attribute's value.
143+
root.set_attribute('attr-key', 'attr-value1')
144+
root.set_attribute('attr-key', 'attr-value2')
145+
146+
root.add_event('event0')
147+
root.add_event('event1', {'name': 'birthday'})
148+
149+
root.add_link(other_context1)
150+
root.add_link(other_context2, {'name': 'neighbor'})
151+
152+
# The public API does not expose getters.
153+
# Checks by accessing the span members directly
154+
155+
self.assertEqual(len(root.attributes), 7)
156+
self.assertEqual(root.attributes['component'], 'http')
157+
self.assertEqual(root.attributes['http.method'], 'GET')
158+
self.assertEqual(root.attributes['http.url'],
159+
'https://example.com:779/path/12/?q=d#123')
160+
self.assertEqual(root.attributes['http.status_code'], 200)
161+
self.assertEqual(root.attributes['http.status_text'], 'OK')
162+
self.assertEqual(root.attributes['misc.pi'], 3.14)
163+
self.assertEqual(root.attributes['attr-key'], 'attr-value2')
164+
165+
self.assertEqual(len(root.events), 2)
166+
self.assertEqual(root.events[0],
167+
trace.Event(name='event0',
168+
attributes={}))
169+
self.assertEqual(root.events[1],
170+
trace.Event(name='event1',
171+
attributes={'name': 'birthday'}))
172+
173+
self.assertEqual(len(root.links), 2)
174+
self.assertEqual(root.links[0].context.trace_id,
175+
other_context1.trace_id)
176+
self.assertEqual(root.links[0].context.span_id,
177+
other_context1.span_id)
178+
self.assertEqual(root.links[0].attributes, {})
179+
self.assertEqual(root.links[1].context.trace_id,
180+
other_context2.trace_id)
181+
self.assertEqual(root.links[1].context.span_id,
182+
other_context2.span_id)
183+
self.assertEqual(root.links[1].attributes, {'name': 'neighbor'})
184+
117185

118186
class TestSpan(unittest.TestCase):
119187

0 commit comments

Comments
 (0)