Skip to content

Commit 1b2c59c

Browse files
authored
[OTel Plugin] Improve live test coverage (#34323)
Here, ServiceBus tests are added to verify span creation, links, and attributes. Signed-off-by: Paul Van Eck <[email protected]>
1 parent 208ea4b commit 1b2c59c

File tree

6 files changed

+297
-8
lines changed

6 files changed

+297
-8
lines changed

eng/tox/install_depend_packages.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949

5050
MAXIMUM_VERSION_GENERIC_OVERRIDES = {}
5151

52-
# SPECIFIC OVERRIDES provide additional filtering of upper and lower bound by
52+
# SPECIFIC OVERRIDES provide additional filtering of upper and lower bound by
5353
# binding an override to the specific package being processed. As an example, when
5454
# processing the latest or minimum deps for "azure-eventhub", the minimum version of "azure-core"
5555
# will be overridden to 1.25.0.
@@ -59,6 +59,7 @@
5959
"azure-eventhub-checkpointstoreblob": {"azure-core": "1.25.0", "azure-eventhub": "5.11.0"},
6060
"azure-eventhub-checkpointstoretable": {"azure-core": "1.25.0", "azure-eventhub": "5.11.0"},
6161
"azure-identity": {"msal": "1.23.0"},
62+
"azure-core-tracing-opentelemetry": {"azure-core": "1.28.0"},
6263
}
6364

6465
MAXIMUM_VERSION_SPECIFIC_OVERRIDES = {}
@@ -212,7 +213,7 @@ def process_bounded_versions(originating_pkg_name: str, pkg_name: str, versions:
212213
v for v in versions if parse_version(v) <= parse_version(restrictions[pkg_name])
213214
]
214215

215-
# upper bound package-specific
216+
# upper bound package-specific
216217
if (
217218
originating_pkg_name in MAXIMUM_VERSION_SPECIFIC_OVERRIDES
218219
and pkg_name in MAXIMUM_VERSION_SPECIFIC_OVERRIDES[originating_pkg_name]

sdk/core/azure-core-tracing-opentelemetry/dev_requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ opentelemetry-sdk<2.0.0,>=1.12.0
55
opentelemetry-instrumentation-requests>=0.32b0
66
requests
77
azure-storage-blob
8+
-e ../../servicebus/azure-servicebus

sdk/core/azure-core-tracing-opentelemetry/test-resources.bicep

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@minLength(6)
2+
@maxLength(21)
13
@description('The base resource name.')
24
param baseName string = resourceGroup().name
35

@@ -7,8 +9,10 @@ param location string = resourceGroup().location
79
@description('The client OID to grant access to test resources.')
810
param testApplicationOid string
911

12+
var sbVersion = '2017-04-01'
13+
1014
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
11-
name: '${baseName}storage'
15+
name: '${baseName}sa'
1216
location: location
1317
kind: 'StorageV2'
1418
sku: {
@@ -19,10 +23,69 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
1923
}
2024
}
2125

26+
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2017-04-01' = {
27+
name: '${baseName}sbnamespace'
28+
location: location
29+
sku: {
30+
name: 'Standard'
31+
}
32+
properties: {}
33+
}
34+
35+
resource serviceBusQueue 'Microsoft.ServiceBus/namespaces/queues@2017-04-01' = {
36+
parent: serviceBusNamespace
37+
name: '${baseName}sbqueue'
38+
properties: {
39+
lockDuration: 'PT5M'
40+
maxSizeInMegabytes: 4096
41+
requiresDuplicateDetection: false
42+
requiresSession: false
43+
defaultMessageTimeToLive: 'P10675199DT2H48M5.4775807S'
44+
deadLetteringOnMessageExpiration: false
45+
duplicateDetectionHistoryTimeWindow: 'PT10M'
46+
maxDeliveryCount: 10
47+
autoDeleteOnIdle: 'P10675199DT2H48M5.4775807S'
48+
enablePartitioning: false
49+
enableExpress: false
50+
}
51+
}
52+
53+
resource serviceBusTopic 'Microsoft.ServiceBus/namespaces/topics@2017-04-01' = {
54+
parent: serviceBusNamespace
55+
name: '${baseName}sbtopic'
56+
properties: {
57+
autoDeleteOnIdle: 'P10675199DT2H48M5.4775807S'
58+
defaultMessageTimeToLive: 'P10675199DT2H48M5.4775807S'
59+
duplicateDetectionHistoryTimeWindow: 'PT10M'
60+
enableBatchedOperations: true
61+
enableExpress: false
62+
enablePartitioning: false
63+
maxSizeInMegabytes: 4096
64+
requiresDuplicateDetection: false
65+
status: 'Active'
66+
supportOrdering: true
67+
}
68+
}
69+
70+
resource serviceBusSubscription 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2017-04-01' = {
71+
parent: serviceBusTopic
72+
name: '${baseName}sbtopic'
73+
properties: {}
74+
}
75+
76+
2277
var name = storageAccount.name
2378
var key = storageAccount.listKeys().keys[0].value
24-
var connectionString = 'DefaultEndpointsProtocol=https;AccountName=${name};AccountKey=${key}'
79+
var storageConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${name};AccountKey=${key}'
80+
var serviceBusConnectionString = listkeys(authRuleResourceId, sbVersion).primaryConnectionString
81+
82+
83+
var authRuleResourceId = resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', serviceBusNamespace.name, 'RootManageSharedAccessKey')
2584

2685
output AZURE_STORAGE_ACCOUNT_NAME string = name
2786
output AZURE_STORAGE_ACCOUNT_KEY string = key
28-
output AZURE_STORAGE_CONNECTION_STRING string = connectionString
87+
output AZURE_STORAGE_CONNECTION_STRING string = storageConnectionString
88+
output AZURE_SERVICEBUS_CONNECTION_STRING string = serviceBusConnectionString
89+
output AZURE_SERVICEBUS_QUEUE_NAME string = serviceBusQueue.name
90+
output AZURE_SERVICEBUS_TOPIC_NAME string = serviceBusTopic.name
91+
output AZURE_SERVICEBUS_SUBSCRIPTION_NAME string = serviceBusSubscription.name

sdk/core/azure-core-tracing-opentelemetry/tests/conftest.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ def exporter():
3434
@pytest.fixture(scope="session")
3535
def config():
3636
return {
37-
"storage_account_name": os.environ["AZURE_STORAGE_ACCOUNT_NAME"],
38-
"storage_account_key": os.environ["AZURE_STORAGE_ACCOUNT_KEY"],
39-
"storage_connection_string": os.environ["AZURE_STORAGE_CONNECTION_STRING"],
37+
"storage_account_name": os.environ.get("AZURE_STORAGE_ACCOUNT_NAME"),
38+
"storage_account_key": os.environ.get("AZURE_STORAGE_ACCOUNT_KEY"),
39+
"storage_connection_string": os.environ.get("AZURE_STORAGE_CONNECTION_STRING"),
40+
"servicebus_connection_string": os.environ.get("AZURE_SERVICEBUS_CONNECTION_STRING"),
41+
"servicebus_queue_name": os.environ.get("AZURE_SERVICEBUS_QUEUE_NAME"),
42+
"servicebus_topic_name": os.environ.get("AZURE_SERVICEBUS_TOPIC_NAME"),
43+
"servicebus_subscription_name": os.environ.get("AZURE_SERVICEBUS_SUBSCRIPTION_NAME"),
4044
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# ------------------------------------
2+
# Copyright (c) Microsoft Corporation.
3+
# Licensed under the MIT License.
4+
# ------------------------------------
5+
import pytest
6+
7+
from azure.servicebus import ServiceBusClient, ServiceBusMessage
8+
from opentelemetry.trace import SpanKind
9+
10+
11+
class TestServiceBusTracing:
12+
def _verify_span_attributes(self, *, span):
13+
# Ensure all attributes are set and have a value.
14+
for attr in span.attributes:
15+
assert span.attributes[attr] is not None and span.attributes[attr] != ""
16+
17+
def _verify_message(self, *, span, dest, server_address):
18+
assert span.name == "ServiceBus.message"
19+
assert span.kind == SpanKind.PRODUCER
20+
self._verify_span_attributes(span=span)
21+
assert span.attributes["az.namespace"] == "Microsoft.ServiceBus"
22+
assert span.attributes["messaging.system"] == "servicebus"
23+
assert span.attributes["messaging.destination.name"] == dest
24+
assert span.attributes["server.address"] == server_address
25+
26+
def _verify_send(self, *, span, dest, server_address, message_count):
27+
assert span.name == "ServiceBus.send"
28+
assert span.kind == SpanKind.CLIENT
29+
self._verify_span_attributes(span=span)
30+
assert span.attributes["az.namespace"] == "Microsoft.ServiceBus"
31+
assert span.attributes["messaging.system"] == "servicebus"
32+
assert span.attributes["messaging.destination.name"] == dest
33+
assert span.attributes["messaging.operation"] == "publish"
34+
assert span.attributes["server.address"] == server_address
35+
if message_count > 1:
36+
assert span.attributes["messaging.batch.message_count"] == message_count
37+
38+
def _verify_receive(self, *, span, dest, server_address, message_count):
39+
assert span.name == "ServiceBus.receive"
40+
assert span.kind == SpanKind.CLIENT
41+
self._verify_span_attributes(span=span)
42+
assert span.attributes["az.namespace"] == "Microsoft.ServiceBus"
43+
assert span.attributes["messaging.system"] == "servicebus"
44+
assert span.attributes["messaging.destination.name"] == dest
45+
assert span.attributes["messaging.operation"] == "receive"
46+
assert span.attributes["server.address"] == server_address
47+
for link in span.links:
48+
assert "enqueuedTime" in link.attributes
49+
if message_count > 1:
50+
assert span.attributes["messaging.batch.message_count"] == message_count
51+
52+
def _verify_complete(self, *, span, dest, server_address):
53+
assert span.name == "ServiceBus.complete"
54+
assert span.kind == SpanKind.CLIENT
55+
self._verify_span_attributes(span=span)
56+
assert span.attributes["az.namespace"] == "Microsoft.ServiceBus"
57+
assert span.attributes["messaging.system"] == "servicebus"
58+
assert span.attributes["messaging.operation"] == "settle"
59+
assert span.attributes["server.address"] == server_address
60+
assert span.attributes["messaging.destination.name"] == dest
61+
62+
@pytest.mark.live_test_only
63+
def test_servicebus_client_tracing_queue(self, config, exporter, tracer):
64+
connection_string = config["servicebus_connection_string"]
65+
queue_name = config["servicebus_queue_name"]
66+
client = ServiceBusClient.from_connection_string(connection_string)
67+
68+
with tracer.start_as_current_span(name="root"):
69+
with client.get_queue_sender(queue_name) as sender:
70+
71+
# Sending a single message
72+
sender.send_messages(ServiceBusMessage("Test foo message"))
73+
74+
# Sending a batch of messages
75+
message_batch = sender.create_message_batch()
76+
message_batch.add_message(ServiceBusMessage("First batch foo message"))
77+
message_batch.add_message(ServiceBusMessage("Second batch foo message"))
78+
sender.send_messages(message_batch)
79+
80+
send_spans = exporter.get_finished_spans()
81+
server_address = sender.fully_qualified_namespace
82+
83+
# We expect 5 spans to have finished: 2 send spans, and 3 message spans.
84+
assert len(send_spans) == 5
85+
86+
# Verify the spans from the first send.
87+
self._verify_message(span=send_spans[0], dest=queue_name, server_address=server_address)
88+
self._verify_send(span=send_spans[1], dest=queue_name, server_address=server_address, message_count=1)
89+
90+
# Verify span links from single send.
91+
link = send_spans[1].links[0]
92+
assert link.context.span_id == send_spans[0].context.span_id
93+
assert link.context.trace_id == send_spans[0].context.trace_id
94+
95+
# Verify the spans from the second send.
96+
self._verify_message(span=send_spans[2], dest=queue_name, server_address=server_address)
97+
self._verify_message(span=send_spans[3], dest=queue_name, server_address=server_address)
98+
self._verify_send(span=send_spans[4], dest=queue_name, server_address=server_address, message_count=2)
99+
100+
# Verify span links from batch send.
101+
assert len(send_spans[4].links) == 2
102+
link = send_spans[4].links[0]
103+
assert link.context.span_id == send_spans[2].context.span_id
104+
assert link.context.trace_id == send_spans[2].context.trace_id
105+
106+
link = send_spans[4].links[1]
107+
assert link.context.span_id == send_spans[3].context.span_id
108+
assert link.context.trace_id == send_spans[3].context.trace_id
109+
110+
exporter.clear()
111+
112+
# Receive all the sent spans.
113+
receiver = client.get_queue_receiver(queue_name=queue_name)
114+
with receiver:
115+
received_msgs = receiver.receive_messages(max_message_count=3, max_wait_time=10)
116+
for msg in received_msgs:
117+
assert "foo" in str(msg)
118+
receiver.complete_message(msg)
119+
120+
receive_spans = exporter.get_finished_spans()
121+
122+
# We expect 4 spans to have finished: 1 receive span, and 3 settlement spans.
123+
assert len(receive_spans) == 4
124+
self._verify_receive(span=receive_spans[0], dest=queue_name, server_address=server_address, message_count=3)
125+
126+
# Verify span links from receive.
127+
assert len(receive_spans[0].links) == 3
128+
assert receive_spans[0].links[0].context.span_id == send_spans[0].context.span_id
129+
assert receive_spans[0].links[1].context.span_id == send_spans[2].context.span_id
130+
assert receive_spans[0].links[2].context.span_id == send_spans[3].context.span_id
131+
132+
# Verify settlement spans.
133+
self._verify_complete(span=receive_spans[1], dest=queue_name, server_address=server_address)
134+
self._verify_complete(span=receive_spans[2], dest=queue_name, server_address=server_address)
135+
self._verify_complete(span=receive_spans[3], dest=queue_name, server_address=server_address)
136+
137+
@pytest.mark.live_test_only
138+
def test_servicebus_client_tracing_topic(self, config, exporter, tracer):
139+
connection_string = config["servicebus_connection_string"]
140+
topic_name = config["servicebus_topic_name"]
141+
subscription_name = config["servicebus_subscription_name"]
142+
client = ServiceBusClient.from_connection_string(connection_string)
143+
144+
with tracer.start_as_current_span(name="root"):
145+
with client.get_topic_sender(topic_name) as sender:
146+
147+
# Sending a single message
148+
sender.send_messages(ServiceBusMessage("Test foo message"))
149+
150+
# Sending a batch of messages
151+
message_batch = sender.create_message_batch()
152+
message_batch.add_message(ServiceBusMessage("First batch foo message"))
153+
message_batch.add_message(ServiceBusMessage("Second batch foo message"))
154+
sender.send_messages(message_batch)
155+
156+
send_spans = exporter.get_finished_spans()
157+
server_address = sender.fully_qualified_namespace
158+
159+
# We expect 5 spans to have finished: 2 send spans, and 3 message spans.
160+
assert len(send_spans) == 5
161+
162+
# Verify the spans from the first send.
163+
self._verify_message(span=send_spans[0], dest=topic_name, server_address=server_address)
164+
self._verify_send(span=send_spans[1], dest=topic_name, server_address=server_address, message_count=1)
165+
166+
# Verify span links from single send.
167+
link = send_spans[1].links[0]
168+
assert link.context.span_id == send_spans[0].context.span_id
169+
assert link.context.trace_id == send_spans[0].context.trace_id
170+
171+
# Verify the spans from the second send.
172+
self._verify_message(span=send_spans[2], dest=topic_name, server_address=server_address)
173+
self._verify_message(span=send_spans[3], dest=topic_name, server_address=server_address)
174+
self._verify_send(span=send_spans[4], dest=topic_name, server_address=server_address, message_count=2)
175+
176+
# Verify span links from batch send.
177+
assert len(send_spans[4].links) == 2
178+
link = send_spans[4].links[0]
179+
assert link.context.span_id == send_spans[2].context.span_id
180+
assert link.context.trace_id == send_spans[2].context.trace_id
181+
182+
link = send_spans[4].links[1]
183+
assert link.context.span_id == send_spans[3].context.span_id
184+
assert link.context.trace_id == send_spans[3].context.trace_id
185+
186+
exporter.clear()
187+
188+
# Receive all the sent spans.
189+
receiver = client.get_subscription_receiver(topic_name, subscription_name)
190+
with receiver:
191+
received_msgs = receiver.receive_messages(max_message_count=3, max_wait_time=10)
192+
for msg in received_msgs:
193+
assert "foo" in str(msg)
194+
receiver.complete_message(msg)
195+
196+
receive_spans = exporter.get_finished_spans()
197+
198+
# We expect 4 spans to have finished: 1 receive span, and 3 settlement spans.
199+
assert len(receive_spans) == 4
200+
self._verify_receive(span=receive_spans[0], dest=topic_name, server_address=server_address, message_count=3)
201+
202+
assert len(receive_spans[0].links) == 3
203+
assert receive_spans[0].links[0].context.span_id == send_spans[0].context.span_id
204+
assert receive_spans[0].links[1].context.span_id == send_spans[2].context.span_id
205+
assert receive_spans[0].links[2].context.span_id == send_spans[3].context.span_id
206+
207+
# Verify settlement spans.
208+
self._verify_complete(span=receive_spans[1], dest=topic_name, server_address=server_address)
209+
self._verify_complete(span=receive_spans[2], dest=topic_name, server_address=server_address)
210+
self._verify_complete(span=receive_spans[3], dest=topic_name, server_address=server_address)

sdk/core/azure-core-tracing-opentelemetry/tests/test_storage_live.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,18 @@ def test_blob_service_client_tracing(self, config, exporter, tracer):
2727

2828
http_span: ReadableSpan = spans[0]
2929
assert http_span.kind == SpanKind.CLIENT
30+
assert http_span.parent
3031
assert http_span.parent.span_id == spans[1].context.span_id
3132

33+
assert http_span.attributes
34+
assert http_span.attributes["http.request.method"] == "GET"
35+
assert http_span.attributes["url.full"]
36+
assert http_span.attributes["server.address"]
37+
assert http_span.attributes["http.response.status_code"] == 200
38+
assert http_span.attributes["az.client_request_id"]
39+
assert http_span.attributes["az.service_request_id"]
40+
3241
method_span: ReadableSpan = spans[1]
3342
assert method_span.kind == SpanKind.INTERNAL
43+
assert method_span.parent
3444
assert method_span.parent.span_id == spans[2].context.span_id

0 commit comments

Comments
 (0)