Skip to content

Commit bc9ca05

Browse files
authored
Merge branch 'main' into pr-moto-foo
2 parents d8efb38 + 6525da4 commit bc9ca05

File tree

5 files changed

+260
-91
lines changed

5 files changed

+260
-91
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929
([#3507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3507))
3030
- Fix documentation order of sections and headers for Django, Flask, MySQL, mysqlclient, psycopg, psycopg2, pymysql, sqlalchemy instrumentations.
3131
([#3719](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3719))
32+
- `opentelemetry-instrumentation-httpx`: fix missing metric response attributes when tracing is disabled
33+
([#3615](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3615))
3234

3335
### Added
3436

@@ -42,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4244
([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366))
4345
- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching
4446
([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699))
47+
- `opentelemetry-instrumentation-botocore`: Add support for SNS semantic convention attribute aws.sns.topic.arn
48+
([#3734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3734))
4549
- `opentelemetry-instrumentation`: botocore: upgrade moto package from 5.0.9 to 5.1.11
4650
([#3736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3736))
4751

instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/extensions/sns.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
_AwsSdkCallContext,
2525
_AwsSdkExtension,
2626
_BotocoreInstrumentorContext,
27+
_BotoResultT,
28+
)
29+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
30+
AWS_SNS_TOPIC_ARN,
2731
)
2832
from opentelemetry.semconv.trace import (
2933
MessagingDestinationKindValues,
@@ -161,6 +165,9 @@ def __init__(self, call_context: _AwsSdkCallContext):
161165

162166
def extract_attributes(self, attributes: _AttributeMapT):
163167
attributes[SpanAttributes.MESSAGING_SYSTEM] = "aws.sns"
168+
topic_arn = self._call_context.params.get("TopicArn")
169+
if topic_arn:
170+
attributes[AWS_SNS_TOPIC_ARN] = topic_arn
164171

165172
if self._op:
166173
self._op.extract_attributes(self._call_context, attributes)
@@ -170,3 +177,16 @@ def before_service_call(
170177
):
171178
if self._op:
172179
self._op.before_service_call(self._call_context, span)
180+
181+
def on_success(
182+
self,
183+
span: Span,
184+
result: _BotoResultT,
185+
instrumentor_context: _BotocoreInstrumentorContext,
186+
):
187+
if not span.is_recording():
188+
return
189+
190+
topic_arn = result.get("TopicArn")
191+
if topic_arn:
192+
span.set_attribute(AWS_SNS_TOPIC_ARN, topic_arn)

instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_sns.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
from moto import mock_aws
2222

2323
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
24+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
25+
AWS_SNS_TOPIC_ARN,
26+
)
2427
from opentelemetry.semconv.trace import (
2528
MessagingDestinationKindValues,
2629
SpanAttributes,
@@ -151,6 +154,10 @@ def test_publish_injects_span(self):
151154
)
152155

153156
span = self.assert_span(f"{self.topic_name} send")
157+
self.assertEqual(
158+
topic_arn,
159+
span.attributes[AWS_SNS_TOPIC_ARN],
160+
)
154161
self.assert_injected_span(message_attrs, span)
155162

156163
def test_publish_batch_to_topic(self):
@@ -188,6 +195,10 @@ def test_publish_batch_to_topic(self):
188195
MessagingDestinationKindValues.TOPIC.value,
189196
span.attributes[SpanAttributes.MESSAGING_DESTINATION_KIND],
190197
)
198+
self.assertEqual(
199+
topic_arn,
200+
span.attributes[AWS_SNS_TOPIC_ARN],
201+
)
191202
self.assertEqual(
192203
topic_arn,
193204
span.attributes[SpanAttributes.MESSAGING_DESTINATION],
@@ -199,3 +210,16 @@ def test_publish_batch_to_topic(self):
199210

200211
self.assert_injected_span(message1_attrs, span)
201212
self.assert_injected_span(message2_attrs, span)
213+
214+
@mock_aws
215+
def test_create_topic_span(self):
216+
_ = self.client.create_topic(Name=self.topic_name)
217+
spans = self.memory_exporter.get_finished_spans()
218+
self.assertEqual(1, len(spans))
219+
span = spans[0]
220+
self.assertEqual(SpanKind.CLIENT, span.kind)
221+
self.assertEqual("SNS.CreateTopic", span.name)
222+
self.assertEqual(
223+
self.topic_arn,
224+
span.attributes[AWS_SNS_TOPIC_ARN],
225+
)

instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,6 @@ def _apply_request_client_attributes_to_span(
417417

418418
def _apply_response_client_attributes_to_span(
419419
span: Span,
420-
metric_attributes: dict[str, typing.Any],
421420
status_code: int,
422421
http_version: str,
423422
semconv: _StabilityMode,
@@ -433,6 +432,30 @@ def _apply_response_client_attributes_to_span(
433432
http_status_code = http_status_to_status_code(status_code)
434433
span.set_status(http_status_code)
435434

435+
if http_status_code == StatusCode.ERROR and _report_new(semconv):
436+
# http semconv transition: new error.type
437+
span_attributes[ERROR_TYPE] = str(status_code)
438+
439+
if http_version and _report_new(semconv):
440+
# http semconv transition: http.flavor -> network.protocol.version
441+
_set_http_network_protocol_version(
442+
span_attributes,
443+
http_version.replace("HTTP/", ""),
444+
semconv,
445+
)
446+
447+
for key, val in span_attributes.items():
448+
span.set_attribute(key, val)
449+
450+
451+
def _apply_response_client_attributes_to_metrics(
452+
span: Span | None,
453+
metric_attributes: dict[str, typing.Any],
454+
status_code: int,
455+
http_version: str,
456+
semconv: _StabilityMode,
457+
) -> None:
458+
"""Apply response attributes to metric attributes."""
436459
# Set HTTP status code in metric attributes
437460
_set_status(
438461
span,
@@ -443,26 +466,12 @@ def _apply_response_client_attributes_to_span(
443466
sem_conv_opt_in_mode=semconv,
444467
)
445468

446-
if http_status_code == StatusCode.ERROR and _report_new(semconv):
447-
# http semconv transition: new error.type
448-
span_attributes[ERROR_TYPE] = str(status_code)
449-
450469
if http_version and _report_new(semconv):
451-
# http semconv transition: http.flavor -> network.protocol.version
452470
_set_http_network_protocol_version(
453471
metric_attributes,
454472
http_version.replace("HTTP/", ""),
455473
semconv,
456474
)
457-
if _report_new(semconv):
458-
_set_http_network_protocol_version(
459-
span_attributes,
460-
http_version.replace("HTTP/", ""),
461-
semconv,
462-
)
463-
464-
for key, val in span_attributes.items():
465-
span.set_attribute(key, val)
466475

467476

468477
class SyncOpenTelemetryTransport(httpx.BaseTransport):
@@ -592,11 +601,19 @@ def handle_request(
592601
_extract_response(response)
593602
)
594603

604+
# Always apply response attributes to metrics
605+
_apply_response_client_attributes_to_metrics(
606+
span,
607+
metric_attributes,
608+
status_code,
609+
http_version,
610+
self._sem_conv_opt_in_mode,
611+
)
612+
595613
if span.is_recording():
596614
# apply http client response attributes according to semconv
597615
_apply_response_client_attributes_to_span(
598616
span,
599-
metric_attributes,
600617
status_code,
601618
http_version,
602619
self._sem_conv_opt_in_mode,
@@ -777,11 +794,19 @@ async def handle_async_request(
777794
_extract_response(response)
778795
)
779796

797+
# Always apply response attributes to metrics
798+
_apply_response_client_attributes_to_metrics(
799+
span,
800+
metric_attributes,
801+
status_code,
802+
http_version,
803+
self._sem_conv_opt_in_mode,
804+
)
805+
780806
if span.is_recording():
781807
# apply http client response attributes according to semconv
782808
_apply_response_client_attributes_to_span(
783809
span,
784-
metric_attributes,
785810
status_code,
786811
http_version,
787812
self._sem_conv_opt_in_mode,
@@ -1001,11 +1026,19 @@ def _handle_request_wrapper( # pylint: disable=too-many-locals
10011026
_extract_response(response)
10021027
)
10031028

1029+
# Always apply response attributes to metrics
1030+
_apply_response_client_attributes_to_metrics(
1031+
span,
1032+
metric_attributes,
1033+
status_code,
1034+
http_version,
1035+
sem_conv_opt_in_mode,
1036+
)
1037+
10041038
if span.is_recording():
10051039
# apply http client response attributes according to semconv
10061040
_apply_response_client_attributes_to_span(
10071041
span,
1008-
metric_attributes,
10091042
status_code,
10101043
http_version,
10111044
sem_conv_opt_in_mode,
@@ -1109,11 +1142,19 @@ async def _handle_async_request_wrapper( # pylint: disable=too-many-locals
11091142
_extract_response(response)
11101143
)
11111144

1145+
# Always apply response attributes to metrics
1146+
_apply_response_client_attributes_to_metrics(
1147+
span,
1148+
metric_attributes,
1149+
status_code,
1150+
http_version,
1151+
sem_conv_opt_in_mode,
1152+
)
1153+
11121154
if span.is_recording():
11131155
# apply http client response attributes according to semconv
11141156
_apply_response_client_attributes_to_span(
11151157
span,
1116-
metric_attributes,
11171158
status_code,
11181159
http_version,
11191160
sem_conv_opt_in_mode,

0 commit comments

Comments
 (0)