Skip to content

Commit ec091ea

Browse files
authored
Merge branch 'main' into django-issue-fix
2 parents 86f804a + 6c2aa7f commit ec091ea

File tree

22 files changed

+994
-278
lines changed

22 files changed

+994
-278
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
([#3731](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3731))
3232
- Fix documentation order of sections and headers for Django, Flask, MySQL, mysqlclient, psycopg, psycopg2, pymysql, sqlalchemy instrumentations.
3333
([#3719](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3719))
34+
- `opentelemetry-instrumentation-asgi` Fixed an issue where FastAPI reports IP instead of URL.
35+
([#3670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3670))
36+
- `opentelemetry-instrumentation-httpx`: fix missing metric response attributes when tracing is disabled
37+
([#3615](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3615))
38+
- `opentelemetry-instrumentation-fastapi`: Don't pass bounded server_request_hook when using `FastAPIInstrumentor.instrument()`
39+
([#3701](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3701))
3440

3541
### Added
3642

@@ -44,6 +50,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4450
([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366))
4551
- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching
4652
([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699))
53+
- `opentelemetry-instrumentation`: botocore: Add support for AWS Step Functions semantic convention attributes
54+
([#3737](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3737))
55+
- `opentelemetry-instrumentation-botocore`: Add support for SNS semantic convention attribute aws.sns.topic.arn
56+
([#3734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3734))
57+
- `opentelemetry-instrumentation`: botocore: upgrade moto package from 5.0.9 to 5.1.11
58+
([#3736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3736))
4759

4860
## Version 1.36.0/0.57b0 (2025-07-29)
4961

instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616
from aio_pika.abc import AbstractChannel, AbstractMessage
1717

1818
from opentelemetry.instrumentation.utils import is_instrumentation_enabled
19+
from opentelemetry.semconv._incubating.attributes.messaging_attributes import (
20+
MESSAGING_MESSAGE_ID,
21+
MESSAGING_OPERATION,
22+
MESSAGING_SYSTEM,
23+
)
24+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
25+
NET_PEER_NAME,
26+
NET_PEER_PORT,
27+
)
1928
from opentelemetry.semconv.trace import (
2029
MessagingOperationValues,
2130
SpanAttributes,
2231
)
2332
from opentelemetry.trace import Span, SpanKind, Tracer
2433

25-
_DEFAULT_ATTRIBUTES = {SpanAttributes.MESSAGING_SYSTEM: "rabbitmq"}
34+
_DEFAULT_ATTRIBUTES = {MESSAGING_SYSTEM: "rabbitmq"}
2635

2736

2837
class SpanBuilder:
@@ -44,6 +53,8 @@ def set_operation(self, operation: MessagingOperationValues):
4453

4554
def set_destination(self, destination: str):
4655
self._destination = destination
56+
# TODO: Update this implementation once the semantic conventions for messaging stabilize
57+
# See: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/messaging.md
4758
self._attributes[SpanAttributes.MESSAGING_DESTINATION] = destination
4859

4960
def set_channel(self, channel: AbstractChannel):
@@ -61,17 +72,15 @@ def set_channel(self, channel: AbstractChannel):
6172
url = connection.url
6273
self._attributes.update(
6374
{
64-
SpanAttributes.NET_PEER_NAME: url.host,
65-
SpanAttributes.NET_PEER_PORT: url.port or 5672,
75+
NET_PEER_NAME: url.host,
76+
NET_PEER_PORT: url.port or 5672,
6677
}
6778
)
6879

6980
def set_message(self, message: AbstractMessage):
7081
properties = message.properties
7182
if properties.message_id:
72-
self._attributes[SpanAttributes.MESSAGING_MESSAGE_ID] = (
73-
properties.message_id
74-
)
83+
self._attributes[MESSAGING_MESSAGE_ID] = properties.message_id
7584
if properties.correlation_id:
7685
self._attributes[SpanAttributes.MESSAGING_CONVERSATION_ID] = (
7786
properties.correlation_id
@@ -81,10 +90,10 @@ def build(self) -> Optional[Span]:
8190
if not is_instrumentation_enabled():
8291
return None
8392
if self._operation:
84-
self._attributes[SpanAttributes.MESSAGING_OPERATION] = (
85-
self._operation.value
86-
)
93+
self._attributes[MESSAGING_OPERATION] = self._operation.value
8794
else:
95+
# TODO: Update this implementation once the semantic conventions for messaging stabilize
96+
# See: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/messaging.md
8897
self._attributes[SpanAttributes.MESSAGING_TEMP_DESTINATION] = True
8998
span = self._tracer.start_span(
9099
self._generate_span_name(),

instrumentation/opentelemetry-instrumentation-aio-pika/tests/consts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
AIOPIKA_VERSION_INFO = tuple(int(v) for v in aiopika_version.split("."))
77
MESSAGE_ID = "meesage_id"
88
CORRELATION_ID = "correlation_id"
9-
MESSAGING_SYSTEM = "rabbitmq"
9+
MESSAGING_SYSTEM_VALUE = "rabbitmq"
1010
EXCHANGE_NAME = "exchange_name"
1111
QUEUE_NAME = "queue_name"
1212
ROUTING_KEY = "routing_key"

instrumentation/opentelemetry-instrumentation-aio-pika/tests/test_callback_decorator.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
from opentelemetry.instrumentation.aio_pika.callback_decorator import (
2020
CallbackDecorator,
2121
)
22+
from opentelemetry.semconv._incubating.attributes.messaging_attributes import (
23+
MESSAGING_MESSAGE_ID,
24+
MESSAGING_OPERATION,
25+
MESSAGING_SYSTEM,
26+
)
27+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
28+
NET_PEER_NAME,
29+
NET_PEER_PORT,
30+
)
2231
from opentelemetry.semconv.trace import SpanAttributes
2332
from opentelemetry.trace import SpanKind, get_tracer
2433

@@ -30,7 +39,7 @@
3039
EXCHANGE_NAME,
3140
MESSAGE,
3241
MESSAGE_ID,
33-
MESSAGING_SYSTEM,
42+
MESSAGING_SYSTEM_VALUE,
3443
QUEUE_NAME,
3544
SERVER_HOST,
3645
SERVER_PORT,
@@ -40,13 +49,13 @@
4049
@skipIf(AIOPIKA_VERSION_INFO >= (8, 0), "Only for aio_pika 7")
4150
class TestInstrumentedQueueAioRmq7(TestCase):
4251
EXPECTED_ATTRIBUTES = {
43-
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
52+
MESSAGING_SYSTEM: MESSAGING_SYSTEM_VALUE,
4453
SpanAttributes.MESSAGING_DESTINATION: EXCHANGE_NAME,
45-
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
46-
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
47-
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
54+
NET_PEER_NAME: SERVER_HOST,
55+
NET_PEER_PORT: SERVER_PORT,
56+
MESSAGING_MESSAGE_ID: MESSAGE_ID,
4857
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
49-
SpanAttributes.MESSAGING_OPERATION: "receive",
58+
MESSAGING_OPERATION: "receive",
5059
}
5160

5261
def setUp(self):
@@ -80,13 +89,13 @@ def test_decorate_callback(self):
8089
@skipIf(AIOPIKA_VERSION_INFO <= (8, 0), "Only for aio_pika 8")
8190
class TestInstrumentedQueueAioRmq8(TestCase):
8291
EXPECTED_ATTRIBUTES = {
83-
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
92+
MESSAGING_SYSTEM: MESSAGING_SYSTEM_VALUE,
8493
SpanAttributes.MESSAGING_DESTINATION: EXCHANGE_NAME,
85-
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
86-
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
87-
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
94+
NET_PEER_NAME: SERVER_HOST,
95+
NET_PEER_PORT: SERVER_PORT,
96+
MESSAGING_MESSAGE_ID: MESSAGE_ID,
8897
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
89-
SpanAttributes.MESSAGING_OPERATION: "receive",
98+
MESSAGING_OPERATION: "receive",
9099
}
91100

92101
def setUp(self):

instrumentation/opentelemetry-instrumentation-aio-pika/tests/test_publish_decorator.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
from opentelemetry.instrumentation.aio_pika.publish_decorator import (
2222
PublishDecorator,
2323
)
24+
from opentelemetry.semconv._incubating.attributes.messaging_attributes import (
25+
MESSAGING_MESSAGE_ID,
26+
MESSAGING_SYSTEM,
27+
)
28+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
29+
NET_PEER_NAME,
30+
NET_PEER_PORT,
31+
)
2432
from opentelemetry.semconv.trace import SpanAttributes
2533
from opentelemetry.trace import SpanKind, get_tracer
2634

@@ -34,7 +42,7 @@
3442
EXCHANGE_NAME,
3543
MESSAGE,
3644
MESSAGE_ID,
37-
MESSAGING_SYSTEM,
45+
MESSAGING_SYSTEM_VALUE,
3846
ROUTING_KEY,
3947
SERVER_HOST,
4048
SERVER_PORT,
@@ -44,11 +52,11 @@
4452
@skipIf(AIOPIKA_VERSION_INFO >= (8, 0), "Only for aio_pika 7")
4553
class TestInstrumentedExchangeAioRmq7(TestCase):
4654
EXPECTED_ATTRIBUTES = {
47-
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
55+
MESSAGING_SYSTEM: MESSAGING_SYSTEM_VALUE,
4856
SpanAttributes.MESSAGING_DESTINATION: f"{EXCHANGE_NAME},{ROUTING_KEY}",
49-
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
50-
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
51-
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
57+
NET_PEER_NAME: SERVER_HOST,
58+
NET_PEER_PORT: SERVER_PORT,
59+
MESSAGING_MESSAGE_ID: MESSAGE_ID,
5260
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
5361
SpanAttributes.MESSAGING_TEMP_DESTINATION: True,
5462
}
@@ -123,11 +131,11 @@ def test_publish_works_with_not_recording_span_robust(self):
123131
@skipIf(AIOPIKA_VERSION_INFO <= (8, 0), "Only for aio_pika 8")
124132
class TestInstrumentedExchangeAioRmq8(TestCase):
125133
EXPECTED_ATTRIBUTES = {
126-
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
134+
MESSAGING_SYSTEM: MESSAGING_SYSTEM_VALUE,
127135
SpanAttributes.MESSAGING_DESTINATION: f"{EXCHANGE_NAME},{ROUTING_KEY}",
128-
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
129-
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
130-
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
136+
NET_PEER_NAME: SERVER_HOST,
137+
NET_PEER_PORT: SERVER_PORT,
138+
MESSAGING_MESSAGE_ID: MESSAGE_ID,
131139
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
132140
SpanAttributes.MESSAGING_TEMP_DESTINATION: True,
133141
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,15 +445,28 @@ def get_host_port_url_tuple(scope):
445445
"""Returns (host, port, full_url) tuple."""
446446
server = scope.get("server") or ["0.0.0.0", 80]
447447
port = server[1]
448+
449+
host_header = asgi_getter.get(scope, "host")
450+
if host_header:
451+
host_value = host_header[0]
452+
# Ensure host_value is a string, not bytes
453+
if isinstance(host_value, bytes):
454+
host_value = _decode_header_item(host_value)
455+
456+
url_host = host_value
457+
458+
else:
459+
url_host = server[0] + (":" + str(port) if str(port) != "80" else "")
448460
server_host = server[0] + (":" + str(port) if str(port) != "80" else "")
461+
449462
# using the scope path is enough, see:
450463
# - https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope (see: root_path and path)
451464
# - https://asgi.readthedocs.io/en/latest/specs/www.html#wsgi-compatibility (see: PATH_INFO)
452465
# PATH_INFO can be derived by stripping root_path from path
453466
# -> that means that the path should contain the root_path already, so prefixing it again is not necessary
454467
# - https://wsgi.readthedocs.io/en/latest/definitions.html#envvar-PATH_INFO
455468
full_path = scope.get("path", "")
456-
http_url = scope.get("scheme", "http") + "://" + server_host + full_path
469+
http_url = scope.get("scheme", "http") + "://" + url_host + full_path
457470
return server_host, port, http_url
458471

459472

instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,10 @@ async def test_host_header(self):
780780

781781
def update_expected_server(expected):
782782
expected[3]["attributes"].update(
783-
{SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8")}
783+
{
784+
SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8"),
785+
SpanAttributes.HTTP_URL: f"http://{hostname.decode('utf8')}/",
786+
}
784787
)
785788
return expected
786789

@@ -797,7 +800,10 @@ async def test_host_header_both_semconv(self):
797800

798801
def update_expected_server(expected):
799802
expected[3]["attributes"].update(
800-
{SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8")}
803+
{
804+
SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8"),
805+
SpanAttributes.HTTP_URL: f"http://{hostname.decode('utf8')}/",
806+
}
801807
)
802808
return expected
803809

@@ -1728,7 +1734,7 @@ def test_request_attributes(self):
17281734
SpanAttributes.HTTP_METHOD: "GET",
17291735
SpanAttributes.HTTP_HOST: "127.0.0.1",
17301736
SpanAttributes.HTTP_TARGET: "/",
1731-
SpanAttributes.HTTP_URL: "http://127.0.0.1/?foo=bar",
1737+
SpanAttributes.HTTP_URL: "http://test/?foo=bar",
17321738
SpanAttributes.NET_HOST_PORT: 80,
17331739
SpanAttributes.HTTP_SCHEME: "http",
17341740
SpanAttributes.HTTP_SERVER_NAME: "test",
@@ -1781,7 +1787,7 @@ def test_request_attributes_both_semconv(self):
17811787
SpanAttributes.HTTP_METHOD: "GET",
17821788
SpanAttributes.HTTP_HOST: "127.0.0.1",
17831789
SpanAttributes.HTTP_TARGET: "/",
1784-
SpanAttributes.HTTP_URL: "http://127.0.0.1/?foo=bar",
1790+
SpanAttributes.HTTP_URL: "http://test/?foo=bar",
17851791
SpanAttributes.NET_HOST_PORT: 80,
17861792
SpanAttributes.HTTP_SCHEME: "http",
17871793
SpanAttributes.HTTP_SERVER_NAME: "test",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def loader():
3535
"bedrock-runtime": _lazy_load(".bedrock", "_BedrockRuntimeExtension"),
3636
"dynamodb": _lazy_load(".dynamodb", "_DynamoDbExtension"),
3737
"lambda": _lazy_load(".lmbd", "_LambdaExtension"),
38+
"stepfunctions": _lazy_load(".sfns", "_StepFunctionsExtension"),
3839
"sns": _lazy_load(".sns", "_SnsExtension"),
3940
"sqs": _lazy_load(".sqs", "_SqsExtension"),
4041
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from opentelemetry.instrumentation.botocore.extensions.types import (
15+
_AttributeMapT,
16+
_AwsSdkExtension,
17+
_BotocoreInstrumentorContext,
18+
_BotoResultT,
19+
)
20+
from opentelemetry.semconv._incubating.attributes.aws_attributes import (
21+
AWS_STEP_FUNCTIONS_ACTIVITY_ARN,
22+
AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN,
23+
)
24+
from opentelemetry.trace.span import Span
25+
26+
27+
class _StepFunctionsExtension(_AwsSdkExtension):
28+
@staticmethod
29+
def _set_arn_attributes(source, target, setter_func):
30+
"""Helper to set ARN attributes if they exist in source."""
31+
activity_arn = source.get("activityArn")
32+
if activity_arn:
33+
setter_func(target, AWS_STEP_FUNCTIONS_ACTIVITY_ARN, activity_arn)
34+
35+
state_machine_arn = source.get("stateMachineArn")
36+
if state_machine_arn:
37+
setter_func(
38+
target, AWS_STEP_FUNCTIONS_STATE_MACHINE_ARN, state_machine_arn
39+
)
40+
41+
def extract_attributes(self, attributes: _AttributeMapT):
42+
self._set_arn_attributes(
43+
self._call_context.params,
44+
attributes,
45+
lambda target, key, value: target.__setitem__(key, value),
46+
)
47+
48+
def on_success(
49+
self,
50+
span: Span,
51+
result: _BotoResultT,
52+
instrumentor_context: _BotocoreInstrumentorContext,
53+
):
54+
self._set_arn_attributes(
55+
result,
56+
span,
57+
lambda target, key, value: target.set_attribute(key, value),
58+
)

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)

0 commit comments

Comments
 (0)