Skip to content

Commit 8fecbab

Browse files
authored
Merge branch 'main' into fix-async-redis-client-tracing
2 parents d66a469 + 256d8ce commit 8fecbab

File tree

22 files changed

+449
-196
lines changed

22 files changed

+449
-196
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## Unreleased
9+
- `opentelemetry-instrumentation-asgi` Add `http.server.request.size` metric
10+
([#1867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1867))
11+
12+
### Fixed
13+
14+
- Update falcon instrumentation to follow semantic conventions
15+
([#1824](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1824))
916

1017
### Added
1118

@@ -15,6 +22,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1522
- Fix falcon instrumentation's usage of Span Status to only set the description if the status code is ERROR.
1623
([#1840](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1840))
1724
- Instrument all httpx versions >= 0.18. ([#1748](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1748))
25+
- Fix `Invalid type NoneType for attribute X (opentelemetry-instrumentation-aws-lambda)` error when some attributes do not exist
26+
([#1780](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1780))
27+
- Add metric instrumentation for celery
28+
([#1679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1679))
29+
- `opentelemetry-instrumentation-asgi` Add `http.server.response.size` metric
30+
([#1789](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1789))
31+
- `opentelemetry-instrumentation-grpc` Allow gRPC connections via Unix socket
32+
([#1833](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1833))
1833

1934
## Version 1.18.0/0.39b0 (2023-05-10)
2035

@@ -23,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2338
([#1706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1706))
2439
- `opentelemetry-instrumentation-pymemcache` Update instrumentation to support pymemcache >4
2540
([#1764](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1764))
41+
- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions of confluent_kafka
42+
([#1815](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1815))
2643

2744
### Added
2845

dev-requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ mypy-protobuf>=1.23
1616
protobuf~=3.13
1717
markupsafe>=2.0.1
1818
codespell==2.1.0
19-
requests==2.28.1
19+
requests==2.31.0
2020
ruamel.yaml==0.17.21

docs-requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ boto~=2.0
2626
botocore~=1.0
2727
boto3~=1.0
2828
celery>=4.0
29-
confluent-kafka>= 1.8.2,< 2.0.0
29+
confluent-kafka>= 1.8.2,<= 2.2.0
3030
elasticsearch>=2.0,<9.0
3131
flask~=2.0
3232
falcon~=2.0

instrumentation/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| [opentelemetry-instrumentation-boto3sqs](./opentelemetry-instrumentation-boto3sqs) | boto3 ~= 1.0 | No
1212
| [opentelemetry-instrumentation-botocore](./opentelemetry-instrumentation-botocore) | botocore ~= 1.0 | No
1313
| [opentelemetry-instrumentation-celery](./opentelemetry-instrumentation-celery) | celery >= 4.0, < 6.0 | No
14-
| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, < 2.0.0 | No
14+
| [opentelemetry-instrumentation-confluent-kafka](./opentelemetry-instrumentation-confluent-kafka) | confluent-kafka >= 1.8.2, <= 2.2.0 | No
1515
| [opentelemetry-instrumentation-dbapi](./opentelemetry-instrumentation-dbapi) | dbapi | No
1616
| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes
1717
| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 2.0 | No

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

+32
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,16 @@ def __init__(
506506
unit="ms",
507507
description="measures the duration of the inbound HTTP request",
508508
)
509+
self.server_response_size_histogram = self.meter.create_histogram(
510+
name=MetricInstruments.HTTP_SERVER_RESPONSE_SIZE,
511+
unit="By",
512+
description="measures the size of HTTP response messages (compressed).",
513+
)
514+
self.server_request_size_histogram = self.meter.create_histogram(
515+
name=MetricInstruments.HTTP_SERVER_REQUEST_SIZE,
516+
unit="By",
517+
description="Measures the size of HTTP request messages (compressed).",
518+
)
509519
self.active_requests_counter = self.meter.create_up_down_counter(
510520
name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
511521
unit="requests",
@@ -518,6 +528,7 @@ def __init__(
518528
self.server_request_hook = server_request_hook
519529
self.client_request_hook = client_request_hook
520530
self.client_response_hook = client_response_hook
531+
self.content_length_header = None
521532

522533
async def __call__(self, scope, receive, send):
523534
"""The ASGI application
@@ -593,6 +604,20 @@ async def __call__(self, scope, receive, send):
593604
self.active_requests_counter.add(
594605
-1, active_requests_count_attrs
595606
)
607+
if self.content_length_header:
608+
self.server_response_size_histogram.record(
609+
self.content_length_header, duration_attrs
610+
)
611+
request_size = asgi_getter.get(scope, "content-length")
612+
if request_size:
613+
try:
614+
request_size_amount = int(request_size[0])
615+
except ValueError:
616+
pass
617+
else:
618+
self.server_request_size_histogram.record(
619+
request_size_amount, duration_attrs
620+
)
596621
if token:
597622
context.detach(token)
598623

@@ -660,6 +685,13 @@ async def otel_send(message):
660685
setter=asgi_setter,
661686
)
662687

688+
content_length = asgi_getter.get(message, "content-length")
689+
if content_length:
690+
try:
691+
self.content_length_header = int(content_length[0])
692+
except ValueError:
693+
pass
694+
663695
await send(message)
664696

665697
return otel_send

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

+28-9
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,30 @@
4646
_expected_metric_names = [
4747
"http.server.active_requests",
4848
"http.server.duration",
49+
"http.server.response.size",
50+
"http.server.request.size",
4951
]
5052
_recommended_attrs = {
5153
"http.server.active_requests": _active_requests_count_attrs,
5254
"http.server.duration": _duration_attrs,
55+
"http.server.response.size": _duration_attrs,
56+
"http.server.request.size": _duration_attrs,
5357
}
5458

5559

5660
async def http_app(scope, receive, send):
5761
message = await receive()
62+
scope["headers"] = [(b"content-length", b"128")]
5863
assert scope["type"] == "http"
5964
if message.get("type") == "http.request":
6065
await send(
6166
{
6267
"type": "http.response.start",
6368
"status": 200,
64-
"headers": [[b"Content-Type", b"text/plain"]],
69+
"headers": [
70+
[b"Content-Type", b"text/plain"],
71+
[b"content-length", b"1024"],
72+
],
6573
}
6674
)
6775
await send({"type": "http.response.body", "body": b"*"})
@@ -94,6 +102,7 @@ async def error_asgi(scope, receive, send):
94102
assert isinstance(scope, dict)
95103
assert scope["type"] == "http"
96104
message = await receive()
105+
scope["headers"] = [(b"content-length", b"128")]
97106
if message.get("type") == "http.request":
98107
try:
99108
raise ValueError
@@ -103,7 +112,10 @@ async def error_asgi(scope, receive, send):
103112
{
104113
"type": "http.response.start",
105114
"status": 200,
106-
"headers": [[b"Content-Type", b"text/plain"]],
115+
"headers": [
116+
[b"Content-Type", b"text/plain"],
117+
[b"content-length", b"1024"],
118+
],
107119
}
108120
)
109121
await send({"type": "http.response.body", "body": b"*"})
@@ -126,7 +138,8 @@ def validate_outputs(self, outputs, error=None, modifiers=None):
126138
# Check http response start
127139
self.assertEqual(response_start["status"], 200)
128140
self.assertEqual(
129-
response_start["headers"], [[b"Content-Type", b"text/plain"]]
141+
response_start["headers"],
142+
[[b"Content-Type", b"text/plain"], [b"content-length", b"1024"]],
130143
)
131144

132145
exc_info = self.scope.get("hack_exc_info")
@@ -352,6 +365,7 @@ def test_traceresponse_header(self):
352365
response_start["headers"],
353366
[
354367
[b"Content-Type", b"text/plain"],
368+
[b"content-length", b"1024"],
355369
[b"traceresponse", f"{traceresponse}".encode()],
356370
[b"access-control-expose-headers", b"traceresponse"],
357371
],
@@ -565,6 +579,7 @@ def test_basic_metric_success(self):
565579
"http.flavor": "1.0",
566580
}
567581
metrics_list = self.memory_metrics_reader.get_metrics_data()
582+
# pylint: disable=too-many-nested-blocks
568583
for resource_metric in metrics_list.resource_metrics:
569584
for scope_metrics in resource_metric.scope_metrics:
570585
for metric in scope_metrics.metrics:
@@ -575,9 +590,14 @@ def test_basic_metric_success(self):
575590
dict(point.attributes),
576591
)
577592
self.assertEqual(point.count, 1)
578-
self.assertAlmostEqual(
579-
duration, point.sum, delta=5
580-
)
593+
if metric.name == "http.server.duration":
594+
self.assertAlmostEqual(
595+
duration, point.sum, delta=5
596+
)
597+
elif metric.name == "http.server.response.size":
598+
self.assertEqual(1024, point.sum)
599+
elif metric.name == "http.server.request.size":
600+
self.assertEqual(128, point.sum)
581601
elif isinstance(point, NumberDataPoint):
582602
self.assertDictEqual(
583603
expected_requests_count_attributes,
@@ -602,13 +622,12 @@ async def target_asgi(scope, receive, send):
602622
app = otel_asgi.OpenTelemetryMiddleware(target_asgi)
603623
self.seed_app(app)
604624
self.send_default_request()
605-
606625
metrics_list = self.memory_metrics_reader.get_metrics_data()
607626
assertions = 0
608627
for resource_metric in metrics_list.resource_metrics:
609628
for scope_metrics in resource_metric.scope_metrics:
610629
for metric in scope_metrics.metrics:
611-
if metric.name != "http.server.duration":
630+
if metric.name == "http.server.active_requests":
612631
continue
613632
for point in metric.data.data_points:
614633
if isinstance(point, HistogramDataPoint):
@@ -617,7 +636,7 @@ async def target_asgi(scope, receive, send):
617636
expected_target,
618637
)
619638
assertions += 1
620-
self.assertEqual(assertions, 1)
639+
self.assertEqual(assertions, 3)
621640

622641
def test_no_metric_for_websockets(self):
623642
self.scope = {

instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py

+51-43
Original file line numberDiff line numberDiff line change
@@ -201,30 +201,35 @@ def _set_api_gateway_v1_proxy_attributes(
201201
span.set_attribute(
202202
SpanAttributes.HTTP_METHOD, lambda_event.get("httpMethod")
203203
)
204-
span.set_attribute(SpanAttributes.HTTP_ROUTE, lambda_event.get("resource"))
205204

206205
if lambda_event.get("headers"):
207-
span.set_attribute(
208-
SpanAttributes.HTTP_USER_AGENT,
209-
lambda_event["headers"].get("User-Agent"),
210-
)
211-
span.set_attribute(
212-
SpanAttributes.HTTP_SCHEME,
213-
lambda_event["headers"].get("X-Forwarded-Proto"),
214-
)
215-
span.set_attribute(
216-
SpanAttributes.NET_HOST_NAME, lambda_event["headers"].get("Host")
217-
)
206+
if "User-Agent" in lambda_event["headers"]:
207+
span.set_attribute(
208+
SpanAttributes.HTTP_USER_AGENT,
209+
lambda_event["headers"]["User-Agent"],
210+
)
211+
if "X-Forwarded-Proto" in lambda_event["headers"]:
212+
span.set_attribute(
213+
SpanAttributes.HTTP_SCHEME,
214+
lambda_event["headers"]["X-Forwarded-Proto"],
215+
)
216+
if "Host" in lambda_event["headers"]:
217+
span.set_attribute(
218+
SpanAttributes.NET_HOST_NAME,
219+
lambda_event["headers"]["Host"],
220+
)
221+
if "resource" in lambda_event:
222+
span.set_attribute(SpanAttributes.HTTP_ROUTE, lambda_event["resource"])
218223

219-
if lambda_event.get("queryStringParameters"):
220-
span.set_attribute(
221-
SpanAttributes.HTTP_TARGET,
222-
f"{lambda_event.get('resource')}?{urlencode(lambda_event.get('queryStringParameters'))}",
223-
)
224-
else:
225-
span.set_attribute(
226-
SpanAttributes.HTTP_TARGET, lambda_event.get("resource")
227-
)
224+
if lambda_event.get("queryStringParameters"):
225+
span.set_attribute(
226+
SpanAttributes.HTTP_TARGET,
227+
f"{lambda_event['resource']}?{urlencode(lambda_event['queryStringParameters'])}",
228+
)
229+
else:
230+
span.set_attribute(
231+
SpanAttributes.HTTP_TARGET, lambda_event["resource"]
232+
)
228233

229234
return span
230235

@@ -237,35 +242,38 @@ def _set_api_gateway_v2_proxy_attributes(
237242
More info:
238243
https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
239244
"""
240-
span.set_attribute(
241-
SpanAttributes.NET_HOST_NAME,
242-
lambda_event["requestContext"].get("domainName"),
243-
)
244-
245-
if lambda_event["requestContext"].get("http"):
245+
if "domainName" in lambda_event["requestContext"]:
246246
span.set_attribute(
247-
SpanAttributes.HTTP_METHOD,
248-
lambda_event["requestContext"]["http"].get("method"),
249-
)
250-
span.set_attribute(
251-
SpanAttributes.HTTP_USER_AGENT,
252-
lambda_event["requestContext"]["http"].get("userAgent"),
253-
)
254-
span.set_attribute(
255-
SpanAttributes.HTTP_ROUTE,
256-
lambda_event["requestContext"]["http"].get("path"),
247+
SpanAttributes.NET_HOST_NAME,
248+
lambda_event["requestContext"]["domainName"],
257249
)
258250

259-
if lambda_event.get("rawQueryString"):
251+
if lambda_event["requestContext"].get("http"):
252+
if "method" in lambda_event["requestContext"]["http"]:
260253
span.set_attribute(
261-
SpanAttributes.HTTP_TARGET,
262-
f"{lambda_event['requestContext']['http'].get('path')}?{lambda_event.get('rawQueryString')}",
254+
SpanAttributes.HTTP_METHOD,
255+
lambda_event["requestContext"]["http"]["method"],
263256
)
264-
else:
257+
if "userAgent" in lambda_event["requestContext"]["http"]:
265258
span.set_attribute(
266-
SpanAttributes.HTTP_TARGET,
267-
lambda_event["requestContext"]["http"].get("path"),
259+
SpanAttributes.HTTP_USER_AGENT,
260+
lambda_event["requestContext"]["http"]["userAgent"],
261+
)
262+
if "path" in lambda_event["requestContext"]["http"]:
263+
span.set_attribute(
264+
SpanAttributes.HTTP_ROUTE,
265+
lambda_event["requestContext"]["http"]["path"],
268266
)
267+
if lambda_event.get("rawQueryString"):
268+
span.set_attribute(
269+
SpanAttributes.HTTP_TARGET,
270+
f"{lambda_event['requestContext']['http']['path']}?{lambda_event['rawQueryString']}",
271+
)
272+
else:
273+
span.set_attribute(
274+
SpanAttributes.HTTP_TARGET,
275+
lambda_event["requestContext"]["http"]["path"],
276+
)
269277

270278
return span
271279

0 commit comments

Comments
 (0)