Skip to content

Commit f306890

Browse files
authored
[Core] Allow operation-level tracing attributes (Azure#38164)
Our documentation advertises that the `tracing_attributes` keyword argument can be passed in at both the client constructor level and the operation/method level. This makes this actually the case. Per-operation customization of tracing attributes can help users mark/identify spans from specific operations when spans are handled through custom span processors. Signed-off-by: Paul Van Eck <[email protected]>
1 parent 923c388 commit f306890

File tree

7 files changed

+64
-5
lines changed

7 files changed

+64
-5
lines changed

sdk/core/azure-core/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Added a default implementation to handle token challenges in `BearerTokenCredentialPolicy` and `AsyncBearerTokenCredentialPolicy`.
88

9+
### Bugs Fixed
10+
11+
- Fixed an issue where the `tracing_attributes` keyword argument wasn't being handled at the request/method level. #38164
12+
913
### Other Changes
1014

1115
- Log "x-vss-e2eid" and "x-msedge-ref" headers in `HttpLoggingPolicy`.

sdk/core/azure-core/azure/core/pipeline/policies/_distributed_tracing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ def on_request(self, request: PipelineRequest[HTTPRequestType]) -> None:
9393
return
9494

9595
namer = ctxt.pop("network_span_namer", self._network_span_namer)
96+
tracing_attributes = ctxt.pop("tracing_attributes", self._tracing_attributes)
9697
span_name = namer(request.http_request)
9798

9899
span = span_impl_type(name=span_name, kind=SpanKind.CLIENT)
99-
for attr, value in self._tracing_attributes.items():
100+
for attr, value in tracing_attributes.items():
100101
span.add_attribute(attr, value)
101102
span.start()
102103

sdk/core/azure-core/azure/core/tracing/decorator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
9797
merge_span = kwargs.pop("merge_span", False)
9898
passed_in_parent = kwargs.pop("parent_span", None)
9999

100+
# Assume this will be popped in DistributedTracingPolicy.
101+
func_tracing_attributes = kwargs.pop("tracing_attributes", tracing_attributes)
102+
100103
span_impl_type = settings.tracing_implementation()
101104
if span_impl_type is None:
102105
return func(*args, **kwargs)
@@ -108,7 +111,7 @@ def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
108111
with change_context(passed_in_parent):
109112
name = name_of_span or get_function_and_class_name(func, *args)
110113
with span_impl_type(name=name, kind=kind) as span:
111-
for key, value in tracing_attributes.items():
114+
for key, value in func_tracing_attributes.items():
112115
span.add_attribute(key, value)
113116
return func(*args, **kwargs)
114117

sdk/core/azure-core/azure/core/tracing/decorator_async.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ async def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
106106
merge_span = kwargs.pop("merge_span", False)
107107
passed_in_parent = kwargs.pop("parent_span", None)
108108

109+
# Assume this will be popped in DistributedTracingPolicy.
110+
func_tracing_attributes = kwargs.get("tracing_attributes", tracing_attributes)
111+
109112
span_impl_type = settings.tracing_implementation()
110113
if span_impl_type is None:
111114
return await func(*args, **kwargs)
@@ -117,7 +120,7 @@ async def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
117120
with change_context(passed_in_parent):
118121
name = name_of_span or get_function_and_class_name(func, *args)
119122
with span_impl_type(name=name, kind=kind) as span:
120-
for key, value in tracing_attributes.items():
123+
for key, value in func_tracing_attributes.items():
121124
span.add_attribute(key, value)
122125
return await func(*args, **kwargs)
123126

sdk/core/azure-core/tests/async_tests/test_tracing_decorator_async.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async def check_name_is_different(self):
7878
time.sleep(0.001)
7979

8080
@distributed_trace_async(tracing_attributes={"foo": "bar"})
81-
async def tracing_attr(self):
81+
async def tracing_attr(self, **kwargs):
8282
time.sleep(0.001)
8383

8484
@distributed_trace_async(kind=SpanKind.PRODUCER)
@@ -105,6 +105,19 @@ async def test_decorator_tracing_attr(self, http_request):
105105
assert parent.children[1].kind == SpanKind.INTERNAL
106106
assert parent.children[1].attributes == {"foo": "bar"}
107107

108+
@pytest.mark.asyncio
109+
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
110+
async def test_decorator_tracing_attr_custom(self, http_request):
111+
with FakeSpan(name="parent") as parent:
112+
client = MockClient(http_request)
113+
await client.tracing_attr(tracing_attributes={"biz": "baz"})
114+
115+
assert len(parent.children) == 2
116+
assert parent.children[0].name == "MockClient.__init__"
117+
assert parent.children[1].name == "MockClient.tracing_attr"
118+
assert parent.children[1].kind == SpanKind.INTERNAL
119+
assert parent.children[1].attributes == {"biz": "baz"}
120+
108121
@pytest.mark.asyncio
109122
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
110123
async def test_decorator_has_different_name(self, http_request):

sdk/core/azure-core/tests/test_tracing_decorator.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def check_name_is_different(self):
7676
time.sleep(0.001)
7777

7878
@distributed_trace(tracing_attributes={"foo": "bar"})
79-
def tracing_attr(self):
79+
def tracing_attr(self, **kwargs):
8080
time.sleep(0.001)
8181

8282
@distributed_trace(kind=SpanKind.PRODUCER)
@@ -113,6 +113,18 @@ def test_decorator_tracing_attr(self, http_request):
113113
assert parent.children[1].kind == SpanKind.INTERNAL
114114
assert parent.children[1].attributes == {"foo": "bar"}
115115

116+
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
117+
def test_decorator_tracing_attr_custom(self, http_request):
118+
with FakeSpan(name="parent") as parent:
119+
client = MockClient(http_request)
120+
client.tracing_attr(tracing_attributes={"biz": "baz"})
121+
122+
assert len(parent.children) == 2
123+
assert parent.children[0].name == "MockClient.__init__"
124+
assert parent.children[1].name == "MockClient.tracing_attr"
125+
assert parent.children[1].kind == SpanKind.INTERNAL
126+
assert parent.children[1].attributes == {"biz": "baz"}
127+
116128
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
117129
def test_decorator_has_different_name(self, http_request):
118130
with FakeSpan(name="parent") as parent:

sdk/core/azure-core/tests/test_tracing_policy.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,29 @@ def test_distributed_tracing_policy_attributes(http_request, http_response):
118118
assert network_span.attributes.get("myattr") == "myvalue"
119119

120120

121+
@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES))
122+
def test_distributed_tracing_policy_attributes_per_operation(http_request, http_response):
123+
"""Test policy with no other policy and happy path"""
124+
settings.tracing_implementation.set_value(FakeSpan)
125+
with FakeSpan(name="parent") as root_span:
126+
policy = DistributedTracingPolicy(tracing_attributes={"myattr": "myvalue"})
127+
128+
request = http_request("GET", "http://localhost/temp?query=query")
129+
130+
pipeline_request = PipelineRequest(request, PipelineContext(None, tracing_attributes={"foo": "bar"}))
131+
policy.on_request(pipeline_request)
132+
133+
response = create_http_response(http_response, request, None)
134+
response.headers = request.headers
135+
response.status_code = 202
136+
137+
policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None)))
138+
139+
# Check on_response
140+
network_span = root_span.children[0]
141+
assert network_span.attributes.get("foo") == "bar"
142+
143+
121144
@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES))
122145
def test_distributed_tracing_policy_badurl(caplog, http_request, http_response):
123146
"""Test policy with a bad url that will throw, and be sure policy ignores it"""

0 commit comments

Comments
 (0)