Skip to content

Commit d05d655

Browse files
committed
Merge branch 'hectorhdzg/aiohttpmetrics' of https://github.com/hectorhdzg/opentelemetry-python-contrib into hectorhdzg/aiohttpmetrics
2 parents 328993d + a6ff92a commit d05d655

File tree

49 files changed

+3645
-1490
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3645
-1490
lines changed

CHANGELOG.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212
## Unreleased
1313

14+
### Fixed
15+
16+
- `opentelemetry-instrumentation-system-metrics`: fix loading on Google Cloud Run
17+
([#3533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3533))
18+
- `opentelemetry-instrumentation-fastapi`: fix wrapping of middlewares
19+
([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012))
20+
- `opentelemetry-instrumentation-starlette` Remove max version constraint on starlette
21+
([#3456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3456))
22+
- `opentelemetry-instrumentation-urllib3`: proper bucket boundaries in stable semconv http duration metrics
23+
([#3518](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3518))
24+
- `opentelemetry-instrumentation-urllib`: proper bucket boundaries in stable semconv http duration metrics
25+
([#3519](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3519))
26+
- `opentelemetry-instrumentation-falcon`: proper bucket boundaries in stable semconv http duration
27+
([#3525](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3525))
28+
- `opentelemetry-instrumentation-wsgi`: add explicit http duration buckets for stable semconv
29+
([#3527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3527))
30+
- `opentelemetry-instrumentation-asgi`: add explicit http duration buckets for stable semconv
31+
([#3526](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3526))
32+
- `opentelemetry-instrumentation-flask`: proper bucket boundaries in stable semconv http duration
33+
([#3523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3523))
34+
- `opentelemetry-instrumentation-django`: proper bucket boundaries in stable semconv http duration
35+
([#3524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3524))
36+
- `opentelemetry-instrumentation-grpc`: support non-list interceptors
37+
([#3520](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3520))
38+
- `opentelemetry-instrumentation-botocore` Ensure spans end on early stream closure for Bedrock Streaming APIs
39+
([#3481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3481))
40+
1441
### Breaking changes
1542

1643
- `opentelemetry-instrumentation-botocore` Use `cloud.region` instead of `aws.region` span attribute as per semantic conventions.
1744
([#3474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3474))
18-
45+
- `opentelemetry-instrumentation-fastapi`: Drop support for FastAPI versions earlier than `0.92`
46+
([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012))
1947

2048
### Added
2149
- `opentelemetry-instrumentation-aiohttp-client` Add support for HTTP metrics
@@ -49,7 +77,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4977
- `opentelemetry-instrumentation-botocore` Capture server attributes for botocore API calls
5078
([#3448](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3448))
5179

52-
5380
## Version 1.32.0/0.53b0 (2025-04-10)
5481

5582
### Added

RELEASING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@ The workflow will create two pull requests, one against the `main` and one again
4141
* Press the "Run workflow" button, then select the release branch from the dropdown list,
4242
e.g. `release/v1.9.x`, then enter the pull request number that you want to backport,
4343
then click the "Run workflow" button below that.
44+
* Add the label `backport` to the generated pull request.
45+
* In case label automation doesn't work, just close and reopen the PR so that the workflow will take into account the label automation we have in place.
4446
* Review and merge the backport pull request that it generates.
4547
* Merge a pull request to the release branch updating the `CHANGELOG.md`.
4648
* The heading for the unreleased entries should be `## Unreleased`.
4749
* Run the [Prepare patch release workflow](https://github.com/open-telemetry/opentelemetry-python-contrib/actions/workflows/prepare-patch-release.yml).
4850
* Press the "Run workflow" button, then select the release branch from the dropdown list,
4951
e.g. `release/v1.9.x`, and click the "Run workflow" button below that.
5052
* Review and merge the pull request that it creates for updating the version.
53+
* Note: If you are doing a patch release in `-contrib` repo, you should also do an equivalent patch release in `-core` repo (even if there's no fix to release), otherwise tests in CI will fail.
5154

5255
### Preparing a patch release for individual package
5356

docs/nitpick-exceptions.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ py-class=
4444
psycopg.Connection
4545
psycopg.AsyncConnection
4646
ObjectProxy
47+
fastapi.applications.FastAPI
4748

4849
any=
4950
; API

instrumentation/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
| --------------- | ------------------ | --------------- | -------------- |
44
| [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika >= 7.2.0, < 10.0.0 | No | development
55
| [opentelemetry-instrumentation-aiohttp-client](./opentelemetry-instrumentation-aiohttp-client) | aiohttp ~= 3.0 | Yes | migration
6-
| [opentelemetry-instrumentation-aiohttp-server](./opentelemetry-instrumentation-aiohttp-server) | aiohttp ~= 3.0 | No | development
6+
| [opentelemetry-instrumentation-aiohttp-server](./opentelemetry-instrumentation-aiohttp-server) | aiohttp ~= 3.0 | Yes | development
77
| [opentelemetry-instrumentation-aiokafka](./opentelemetry-instrumentation-aiokafka) | aiokafka >= 0.8, < 1.0 | No | development
88
| [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No | development
99
| [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | Yes | migration
@@ -22,7 +22,7 @@
2222
| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes | development
2323
| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 6.0 | No | development
2424
| [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 5.0.0 | Yes | migration
25-
| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | migration
25+
| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.92 | Yes | migration
2626
| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | migration
2727
| [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio >= 1.42.0 | No | development
2828
| [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No | migration
@@ -44,7 +44,7 @@
4444
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration
4545
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | development
4646
| [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | development
47-
| [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette >= 0.13, <0.15 | Yes | development
47+
| [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette >= 0.13 | Yes | development
4848
| [opentelemetry-instrumentation-system-metrics](./opentelemetry-instrumentation-system-metrics) | psutil >= 5 | No | development
4949
| [opentelemetry-instrumentation-threading](./opentelemetry-instrumentation-threading) | threading | No | development
5050
| [opentelemetry-instrumentation-tornado](./opentelemetry-instrumentation-tornado) | tornado >= 5.1.1 | Yes | development

instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,44 @@ async def hello(request):
5454
)
5555
from opentelemetry.propagate import extract
5656
from opentelemetry.propagators.textmap import Getter
57+
from opentelemetry.semconv._incubating.attributes.http_attributes import (
58+
HTTP_FLAVOR,
59+
HTTP_HOST,
60+
HTTP_METHOD,
61+
HTTP_ROUTE,
62+
HTTP_SCHEME,
63+
HTTP_SERVER_NAME,
64+
HTTP_STATUS_CODE,
65+
HTTP_TARGET,
66+
HTTP_URL,
67+
HTTP_USER_AGENT,
68+
)
69+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
70+
NET_HOST_NAME,
71+
NET_HOST_PORT,
72+
)
5773
from opentelemetry.semconv.metrics import MetricInstruments
58-
from opentelemetry.semconv.trace import SpanAttributes
5974
from opentelemetry.trace.status import Status, StatusCode
6075
from opentelemetry.util.http import get_excluded_urls, remove_url_credentials
6176

6277
_duration_attrs = [
63-
SpanAttributes.HTTP_METHOD,
64-
SpanAttributes.HTTP_HOST,
65-
SpanAttributes.HTTP_SCHEME,
66-
SpanAttributes.HTTP_STATUS_CODE,
67-
SpanAttributes.HTTP_FLAVOR,
68-
SpanAttributes.HTTP_SERVER_NAME,
69-
SpanAttributes.NET_HOST_NAME,
70-
SpanAttributes.NET_HOST_PORT,
71-
SpanAttributes.HTTP_ROUTE,
78+
HTTP_METHOD,
79+
HTTP_HOST,
80+
HTTP_SCHEME,
81+
HTTP_STATUS_CODE,
82+
HTTP_FLAVOR,
83+
HTTP_SERVER_NAME,
84+
NET_HOST_NAME,
85+
NET_HOST_PORT,
86+
HTTP_ROUTE,
7287
]
7388

7489
_active_requests_count_attrs = [
75-
SpanAttributes.HTTP_METHOD,
76-
SpanAttributes.HTTP_HOST,
77-
SpanAttributes.HTTP_SCHEME,
78-
SpanAttributes.HTTP_FLAVOR,
79-
SpanAttributes.HTTP_SERVER_NAME,
90+
HTTP_METHOD,
91+
HTTP_HOST,
92+
HTTP_SCHEME,
93+
HTTP_FLAVOR,
94+
HTTP_SERVER_NAME,
8095
]
8196

8297
tracer = trace.get_tracer(__name__)
@@ -140,29 +155,27 @@ def collect_request_attributes(request: web.Request) -> Dict:
140155
http_url += "?" + urllib.parse.unquote(query_string)
141156

142157
result = {
143-
SpanAttributes.HTTP_SCHEME: request.scheme,
144-
SpanAttributes.HTTP_HOST: server_host,
145-
SpanAttributes.NET_HOST_PORT: port,
146-
SpanAttributes.HTTP_ROUTE: _get_view_func(request),
147-
SpanAttributes.HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
148-
SpanAttributes.HTTP_TARGET: request.path,
149-
SpanAttributes.HTTP_URL: remove_url_credentials(http_url),
158+
HTTP_SCHEME: request.scheme,
159+
HTTP_HOST: server_host,
160+
NET_HOST_PORT: port,
161+
HTTP_ROUTE: _get_view_func(request),
162+
HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
163+
HTTP_TARGET: request.path,
164+
HTTP_URL: remove_url_credentials(http_url),
150165
}
151166

152167
http_method = request.method
153168
if http_method:
154-
result[SpanAttributes.HTTP_METHOD] = http_method
169+
result[HTTP_METHOD] = http_method
155170

156171
http_host_value_list = (
157172
[request.host] if not isinstance(request.host, list) else request.host
158173
)
159174
if http_host_value_list:
160-
result[SpanAttributes.HTTP_SERVER_NAME] = ",".join(
161-
http_host_value_list
162-
)
175+
result[HTTP_SERVER_NAME] = ",".join(http_host_value_list)
163176
http_user_agent = request.headers.get("user-agent")
164177
if http_user_agent:
165-
result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent
178+
result[HTTP_USER_AGENT] = http_user_agent
166179

167180
# remove None values
168181
result = {k: v for k, v in result.items() if v is not None}
@@ -183,7 +196,7 @@ def set_status_code(span, status_code: int) -> None:
183196
)
184197
)
185198
else:
186-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
199+
span.set_attribute(HTTP_STATUS_CODE, status_code)
187200
span.set_status(
188201
Status(http_status_to_status_code(status_code, server_span=True))
189202
)

instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/package.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@
1414

1515

1616
_instruments = ("aiohttp ~= 3.0",)
17+
18+
_supports_metrics = True

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
202202

203203
from opentelemetry import context, trace
204204
from opentelemetry.instrumentation._semconv import (
205+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
205206
_filter_semconv_active_request_count_attr,
206207
_filter_semconv_duration_attrs,
207208
_get_schema_url,
@@ -589,6 +590,7 @@ def __init__(
589590
name=HTTP_SERVER_REQUEST_DURATION,
590591
description="Duration of HTTP server requests.",
591592
unit="s",
593+
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
592594
)
593595
self.server_response_size_histogram = None
594596
if _report_old(sem_conv_opt_in_mode):
@@ -665,9 +667,9 @@ def __init__(
665667
# pylint: disable=too-many-statements
666668
async def __call__(
667669
self,
668-
scope: dict[str, Any],
669-
receive: Callable[[], Awaitable[dict[str, Any]]],
670-
send: Callable[[dict[str, Any]], Awaitable[None]],
670+
scope: typing.MutableMapping[str, Any],
671+
receive: Callable[[], Awaitable[typing.MutableMapping[str, Any]]],
672+
send: Callable[[typing.MutableMapping[str, Any]], Awaitable[None]],
671673
) -> None:
672674
"""The ASGI application
673675

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import opentelemetry.instrumentation.asgi as otel_asgi
2424
from opentelemetry import trace as trace_api
2525
from opentelemetry.instrumentation._semconv import (
26+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
2627
OTEL_SEMCONV_STABILITY_OPT_IN,
2728
_OpenTelemetrySemanticConventionStability,
2829
_server_active_requests_count_attrs_new,
@@ -1245,6 +1246,7 @@ async def test_asgi_metrics(self):
12451246
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
12461247

12471248
async def test_asgi_metrics_new_semconv(self):
1249+
# pylint: disable=too-many-nested-blocks
12481250
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
12491251
self.seed_app(app)
12501252
await self.send_default_request()
@@ -1274,6 +1276,11 @@ async def test_asgi_metrics_new_semconv(self):
12741276
for point in data_points:
12751277
if isinstance(point, HistogramDataPoint):
12761278
self.assertEqual(point.count, 3)
1279+
if metric.name == "http.server.request.duration":
1280+
self.assertEqual(
1281+
point.explicit_bounds,
1282+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
1283+
)
12771284
histogram_data_point_seen = True
12781285
if isinstance(point, NumberDataPoint):
12791286
number_data_point_seen = True
@@ -1284,6 +1291,7 @@ async def test_asgi_metrics_new_semconv(self):
12841291
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
12851292

12861293
async def test_asgi_metrics_both_semconv(self):
1294+
# pylint: disable=too-many-nested-blocks
12871295
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
12881296
self.seed_app(app)
12891297
await self.send_default_request()
@@ -1313,6 +1321,11 @@ async def test_asgi_metrics_both_semconv(self):
13131321
for point in data_points:
13141322
if isinstance(point, HistogramDataPoint):
13151323
self.assertEqual(point.count, 3)
1324+
if metric.name == "http.server.request.duration":
1325+
self.assertEqual(
1326+
point.explicit_bounds,
1327+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
1328+
)
13161329
histogram_data_point_seen = True
13171330
if isinstance(point, NumberDataPoint):
13181331
number_data_point_seen = True

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
from opentelemetry.instrumentation.boto.version import __version__
5353
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
5454
from opentelemetry.instrumentation.utils import unwrap
55-
from opentelemetry.semconv.trace import SpanAttributes
55+
from opentelemetry.semconv._incubating.attributes.http_attributes import (
56+
HTTP_METHOD,
57+
HTTP_STATUS_CODE,
58+
)
5659
from opentelemetry.trace import SpanKind, get_tracer
5760

5861
logger = logging.getLogger(__name__)
@@ -158,12 +161,8 @@ def _common_request( # pylint: disable=too-many-locals
158161
for key, value in meta.items():
159162
span.set_attribute(key, value)
160163

161-
span.set_attribute(
162-
SpanAttributes.HTTP_STATUS_CODE, getattr(result, "status")
163-
)
164-
span.set_attribute(
165-
SpanAttributes.HTTP_METHOD, getattr(result, "_method")
166-
)
164+
span.set_attribute(HTTP_STATUS_CODE, getattr(result, "status"))
165+
span.set_attribute(HTTP_METHOD, getattr(result, "_method"))
167166

168167
return result
169168

instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@
2828
)
2929

3030
from opentelemetry.instrumentation.boto import BotoInstrumentor
31-
from opentelemetry.semconv.trace import SpanAttributes
31+
from opentelemetry.semconv._incubating.attributes.http_attributes import (
32+
HTTP_METHOD,
33+
HTTP_STATUS_CODE,
34+
)
3235
from opentelemetry.test.test_base import TestBase
3336

3437

3538
def assert_span_http_status_code(span, code):
3639
"""Assert on the span's 'http.status_code' tag"""
37-
tag = span.attributes[SpanAttributes.HTTP_STATUS_CODE]
40+
tag = span.attributes[HTTP_STATUS_CODE]
3841
assert tag == code, f"{tag} != {code}"
3942

4043

@@ -60,7 +63,7 @@ def test_ec2_client(self):
6063
span = spans[0]
6164
self.assertEqual(span.attributes["aws.operation"], "DescribeInstances")
6265
assert_span_http_status_code(span, 200)
63-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "POST")
66+
self.assertEqual(span.attributes[HTTP_METHOD], "POST")
6467
self.assertEqual(span.attributes["aws.region"], "us-west-2")
6568

6669
# Create an instance
@@ -73,7 +76,7 @@ def test_ec2_client(self):
7376
assert_span_http_status_code(span, 200)
7477
self.assertEqual(span.attributes["endpoint"], "ec2")
7578
self.assertEqual(span.attributes["http_method"], "runinstances")
76-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "POST")
79+
self.assertEqual(span.attributes[HTTP_METHOD], "POST")
7780
self.assertEqual(span.attributes["aws.region"], "us-west-2")
7881
self.assertEqual(span.name, "ec2.command")
7982

@@ -120,7 +123,7 @@ def test_s3_client(self):
120123
self.assertEqual(len(spans), 1)
121124
span = spans[0]
122125
assert_span_http_status_code(span, 200)
123-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
126+
self.assertEqual(span.attributes[HTTP_METHOD], "GET")
124127
self.assertEqual(span.attributes["aws.operation"], "get_all_buckets")
125128

126129
# Create a bucket command
@@ -130,7 +133,7 @@ def test_s3_client(self):
130133
self.assertEqual(len(spans), 2)
131134
span = spans[1]
132135
assert_span_http_status_code(span, 200)
133-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "PUT")
136+
self.assertEqual(span.attributes[HTTP_METHOD], "PUT")
134137
self.assertEqual(span.attributes["path"], "/")
135138
self.assertEqual(span.attributes["aws.operation"], "create_bucket")
136139

@@ -143,7 +146,7 @@ def test_s3_client(self):
143146
assert_span_http_status_code(span, 200)
144147
self.assertEqual(span.attributes["endpoint"], "s3")
145148
self.assertEqual(span.attributes["http_method"], "head")
146-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "HEAD")
149+
self.assertEqual(span.attributes[HTTP_METHOD], "HEAD")
147150
self.assertEqual(span.attributes["aws.operation"], "head_bucket")
148151
self.assertEqual(span.name, "s3.command")
149152

@@ -240,7 +243,7 @@ def test_lambda_client(self):
240243
assert_span_http_status_code(span, 200)
241244
self.assertEqual(span.attributes["endpoint"], "lambda")
242245
self.assertEqual(span.attributes["http_method"], "get")
243-
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
246+
self.assertEqual(span.attributes[HTTP_METHOD], "GET")
244247
self.assertEqual(span.attributes["aws.region"], "us-east-2")
245248
self.assertEqual(span.attributes["aws.operation"], "list_functions")
246249

0 commit comments

Comments
 (0)