Skip to content

Commit 9481893

Browse files
Add Span.add_link() method to add link after span start. (#3618)
* Add Span.add_link() method to add link after span start. * Update CHANGELOG.md * Update CHANGELOG.md * Unmark Span.add_link() as abstract method and add warning. * Add Span.add_link() method to add link after span start. * Update CHANGELOG.md * Update CHANGELOG.md * Unmark Span.add_link() as abstract method and add warning. * Update warning message for Span.add_link(). --------- Co-authored-by: Diego Hurtado <[email protected]>
1 parent ac3c189 commit 9481893

File tree

5 files changed

+86
-4
lines changed

5 files changed

+86
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
([#3567](https://github.com/open-telemetry/opentelemetry-python/pull/3567))
1414
- Fix flush error when no LoggerProvider configured for LoggingHandler
1515
([#3608](https://github.com/open-telemetry/opentelemetry-python/pull/3608))
16+
- Add `Span.add_link()` method to add link after span start
17+
([#3618](https://github.com/open-telemetry/opentelemetry-python/pull/3618))
1618
- Fix `OTLPMetricExporter` ignores `preferred_aggregation` property
1719
([#3603](https://github.com/open-telemetry/opentelemetry-python/pull/3603))
1820
- Logs: set `observed_timestamp` field

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@
8585
from deprecated import deprecated
8686

8787
from opentelemetry import context as context_api
88-
from opentelemetry.attributes import BoundedAttributes # type: ignore
8988
from opentelemetry.context.context import Context
9089
from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER
9190
from opentelemetry.trace.propagation import (
@@ -144,9 +143,7 @@ def __init__(
144143
attributes: types.Attributes = None,
145144
) -> None:
146145
super().__init__(context)
147-
self._attributes = BoundedAttributes(
148-
attributes=attributes
149-
) # type: types.Attributes
146+
self._attributes = attributes
150147

151148
@property
152149
def attributes(self) -> types.Attributes:

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import re
44
import types as python_types
55
import typing
6+
import warnings
67

78
from opentelemetry.trace.status import Status, StatusCode
89
from opentelemetry.util import types
@@ -117,6 +118,26 @@ def add_event(
117118
timestamp if the `timestamp` argument is omitted.
118119
"""
119120

121+
def add_link( # pylint: disable=no-self-use
122+
self,
123+
context: "SpanContext",
124+
attributes: types.Attributes = None,
125+
) -> None:
126+
"""Adds a `Link`.
127+
128+
Adds a single `Link` with the `SpanContext` of the span to link to and,
129+
optionally, attributes passed as arguments. Implementations may ignore
130+
calls with an invalid span context.
131+
132+
Note: It is preferred to add links at span creation, instead of calling
133+
this method later since samplers can only consider information already
134+
present during span creation.
135+
"""
136+
warnings.warn(
137+
"Span.add_link() not implemented and will be a no-op. "
138+
"Use opentelemetry-sdk >= 1.23 to add links after span creation"
139+
)
140+
120141
@abc.abstractmethod
121142
def update_name(self, name: str) -> None:
122143
"""Updates the `Span` name.
@@ -523,6 +544,13 @@ def add_event(
523544
) -> None:
524545
pass
525546

547+
def add_link(
548+
self,
549+
context: "SpanContext",
550+
attributes: types.Attributes = None,
551+
) -> None:
552+
pass
553+
526554
def update_name(self, name: str) -> None:
527555
pass
528556

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,30 @@ def add_event(
850850
)
851851
)
852852

853+
@_check_span_ended
854+
def _add_link(self, link: trace_api.Link) -> None:
855+
self._links.append(link)
856+
857+
def add_link(
858+
self,
859+
context: SpanContext,
860+
attributes: types.Attributes = None,
861+
) -> None:
862+
if context is None or not context.is_valid:
863+
return
864+
865+
attributes = BoundedAttributes(
866+
self._limits.max_link_attributes,
867+
attributes,
868+
max_value_len=self._limits.max_attribute_length,
869+
)
870+
self._add_link(
871+
trace_api.Link(
872+
context=context,
873+
attributes=attributes,
874+
)
875+
)
876+
853877
def _readable_span(self) -> ReadableSpan:
854878
return ReadableSpan(
855879
name=self._name,

opentelemetry-sdk/tests/trace/test_trace.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,37 @@ def test_links(self):
900900
with self.assertRaises(TypeError):
901901
root.links[1].attributes["name"] = "new_neighbour"
902902

903+
def test_add_link(self):
904+
id_generator = RandomIdGenerator()
905+
other_context = trace_api.SpanContext(
906+
trace_id=id_generator.generate_trace_id(),
907+
span_id=id_generator.generate_span_id(),
908+
is_remote=False,
909+
)
910+
911+
with self.tracer.start_as_current_span("root") as root:
912+
root.add_link(other_context, {"name": "neighbor"})
913+
914+
self.assertEqual(len(root.links), 1)
915+
self.assertEqual(
916+
root.links[0].context.trace_id, other_context.trace_id
917+
)
918+
self.assertEqual(
919+
root.links[0].context.span_id, other_context.span_id
920+
)
921+
self.assertEqual(root.links[0].attributes, {"name": "neighbor"})
922+
923+
with self.assertRaises(TypeError):
924+
root.links[0].attributes["name"] = "new_neighbour"
925+
926+
def test_add_link_with_invalid_span_context(self):
927+
other_context = trace_api.INVALID_SPAN_CONTEXT
928+
929+
with self.tracer.start_as_current_span("root") as root:
930+
root.add_link(other_context)
931+
932+
self.assertEqual(len(root.links), 0)
933+
903934
def test_update_name(self):
904935
with self.tracer.start_as_current_span("root") as root:
905936
# name

0 commit comments

Comments
 (0)