Skip to content

Commit

Permalink
Add Span.set_attributes method (#1520)
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-ryzhov authored Jan 21, 2021
1 parent b32365b commit 6489bf5
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added `end_on_exit` argument to `start_as_current_span`
([#1519](https://github.com/open-telemetry/opentelemetry-python/pull/1519)])
- Add `Span.set_attributes` method to set multiple values with one call
([#1520](https://github.com/open-telemetry/opentelemetry-python/pull/1520))

## [0.17b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v0.17b0) - 2021-01-20

Expand Down
16 changes: 16 additions & 0 deletions opentelemetry-api/src/opentelemetry/trace/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ def get_span_context(self) -> "SpanContext":
A :class:`opentelemetry.trace.SpanContext` with a copy of this span's immutable state.
"""

@abc.abstractmethod
def set_attributes(
self, attributes: typing.Dict[str, types.AttributeValue]
) -> None:
"""Sets Attributes.
Sets Attributes with the key and value passed as arguments dict.
Note: The behavior of `None` value attributes is undefined, and hence strongly discouraged.
"""

@abc.abstractmethod
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
"""Sets an Attribute.
Expand Down Expand Up @@ -450,6 +461,11 @@ def is_recording(self) -> bool:
def end(self, end_time: typing.Optional[int] = None) -> None:
pass

def set_attributes(
self, attributes: typing.Dict[str, types.AttributeValue]
) -> None:
pass

def set_attribute(self, key: str, value: types.AttributeValue) -> None:
pass

Expand Down
43 changes: 25 additions & 18 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from typing import (
Any,
Callable,
Dict,
Iterator,
MutableSequence,
Optional,
Expand Down Expand Up @@ -582,29 +583,35 @@ def to_json(self, indent=4):
def get_span_context(self):
return self.context

def set_attribute(self, key: str, value: types.AttributeValue) -> None:
if not _is_valid_attribute_value(value):
return

if not key:
logger.warning("invalid key (empty or null)")
return

def set_attributes(
self, attributes: Dict[str, types.AttributeValue]
) -> None:
with self._lock:
if self.end_time is not None:
logger.warning("Setting attribute on ended span.")
return

# Freeze mutable sequences defensively
if isinstance(value, MutableSequence):
value = tuple(value)
if isinstance(value, bytes):
try:
value = value.decode()
except ValueError:
logger.warning("Byte attribute could not be decoded.")
return
self.attributes[key] = value
for key, value in attributes.items():
if not _is_valid_attribute_value(value):
continue

if not key:
logger.warning("invalid key `%s` (empty or null)", key)
continue

# Freeze mutable sequences defensively
if isinstance(value, MutableSequence):
value = tuple(value)
if isinstance(value, bytes):
try:
value = value.decode()
except ValueError:
logger.warning("Byte attribute could not be decoded.")
return
self.attributes[key] = value

def set_attribute(self, key: str, value: types.AttributeValue) -> None:
return self.set_attributes({key: value})

@_check_span_ended
def _add_event(self, event: EventBase) -> None:
Expand Down
18 changes: 13 additions & 5 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,14 @@ def test_basic_span(self):

def test_attributes(self):
with self.tracer.start_as_current_span("root") as root:
root.set_attribute("component", "http")
root.set_attribute("http.method", "GET")
root.set_attribute(
"http.url", "https://example.com:779/path/12/?q=d#123"
root.set_attributes(
{
"component": "http",
"http.method": "GET",
"http.url": "https://example.com:779/path/12/?q=d#123",
}
)

root.set_attribute("http.status_code", 200)
root.set_attribute("http.status_text", "OK")
root.set_attribute("misc.pi", 3.14)
Expand Down Expand Up @@ -593,6 +596,10 @@ def test_attributes(self):

def test_invalid_attribute_values(self):
with self.tracer.start_as_current_span("root") as root:
root.set_attributes(
{"correct-value": "foo", "non-primitive-data-type": dict()}
)

root.set_attribute("non-primitive-data-type", dict())
root.set_attribute(
"list-of-mixed-data-types-numeric-first",
Expand All @@ -609,7 +616,8 @@ def test_invalid_attribute_values(self):
root.set_attribute("", 123)
root.set_attribute(None, 123)

self.assertEqual(len(root.attributes), 0)
self.assertEqual(len(root.attributes), 1)
self.assertEqual(root.attributes["correct-value"], "foo")

def test_byte_type_attribute_value(self):
with self.tracer.start_as_current_span("root") as root:
Expand Down

0 comments on commit 6489bf5

Please sign in to comment.