Skip to content

Commit edc6044

Browse files
committed
more review updates
1 parent 9e04228 commit edc6044

6 files changed

Lines changed: 98 additions & 35 deletions

File tree

ext/opentelemetry-ext-asgi/README.rst

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,23 @@ Usage (Quart)
3737
app.run(debug=True)
3838
3939
40-
Usage (Django)
41-
--------------
40+
Usage (Django 3.0)
41+
------------------
4242

4343
Modify the application's ``asgi.py`` file as shown below.
4444

4545
.. code-block:: python
4646
4747
import os
48-
import django
49-
from channels.routing import get_default_application
48+
from django.core.asgi import get_asgi_application
5049
from opentelemetry.ext.asgi import OpenTelemetryMiddleware
5150
52-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
53-
django.setup()
51+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings')
5452
55-
application = get_default_application()
53+
application = get_asgi_application()
5654
application = OpenTelemetryMiddleware(application)
5755
56+
5857
References
5958
----------
6059

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import operator
2222
import typing
23+
import urllib
2324
from functools import wraps
2425

2526
from asgiref.compatibility import guarantee_single_callable
@@ -80,16 +81,16 @@ def collect_request_attributes(scope):
8081
server = scope.get("server") or ["0.0.0.0", 80]
8182
port = server[1]
8283
server_host = server[0] + (":" + str(port) if port != 80 else "")
83-
http_url = (
84-
scope.get("scheme", "http") + "://" + server_host + scope.get("path", "")
85-
if scope.get("scheme") and server_host and scope.get("path")
86-
else None
87-
)
84+
full_path = scope.get("root_path", "") + scope.get("path", "")
85+
http_url = scope.get("scheme", "http") + "://" + server_host + full_path
8886
if scope.get("query_string") and http_url:
89-
http_url = http_url + ("?" + scope.get("query_string").decode("utf8"))
87+
if isinstance(scope["query_string"], bytes):
88+
http_url = http_url + ("?" + scope.get("query_string").decode("utf8"))
89+
else:
90+
http_url = http_url + ("?" + urllib.parse.unquote(scope.get("query_string")))
9091

9192
result = {
92-
"component": scope["type"]
93+
"component": scope["type"],
9394
"http.scheme": scope.get("scheme"),
9495
"http.host": server_host,
9596
"host.port": port,
@@ -166,7 +167,7 @@ async def __call__(self, scope, receive, send):
166167
receive: An awaitable callable yielding dictionaries
167168
send: An awaitable callable taking a single dictionary as argument.
168169
"""
169-
if scope.get("type") not in ("http", "websocket"):
170+
if scope["type"] not in ("http", "websocket"):
170171
return await self.app(scope, receive, send)
171172

172173
token = context.attach(
@@ -187,23 +188,23 @@ async def wrapped_receive():
187188
span_name + " asgi." + scope["type"] + ".receive"
188189
) as receive_span:
189190
message = await receive()
190-
if payload["type"] == "websocket.receive":
191+
if message["type"] == "websocket.receive":
191192
set_status_code(receive_span, 200)
192-
receive_span.set_attribute("type", payload["type"])
193-
return payload
193+
receive_span.set_attribute("type", message["type"])
194+
return message
194195

195196
@wraps(send)
196197
async def wrapped_send(message):
197198
with self.tracer.start_as_current_span(
198199
span_name + " asgi." + scope["type"] + ".send"
199200
) as send_span:
200-
if payload["type"] == "http.response.start":
201-
status_code = payload["status"]
201+
if message["type"] == "http.response.start":
202+
status_code = message["status"]
202203
set_status_code(send_span, status_code)
203-
elif payload["type"] == "websocket.send":
204+
elif message["type"] == "websocket.send":
204205
set_status_code(send_span, 200)
205-
send_span.set_attribute("type", payload["type"])
206-
await send(payload)
206+
send_span.set_attribute("type", message["type"])
207+
await send(message)
207208

208209
await self.app(scope, wrapped_receive, wrapped_send)
209210
finally:

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

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import sys
1616
import unittest
1717
import unittest.mock as mock
18+
import urllib
1819

1920
import opentelemetry.ext.asgi as otel_asgi
2021
from opentelemetry import trace as trace_api
@@ -25,9 +26,9 @@
2526

2627

2728
async def http_app(scope, receive, send):
28-
payload = await receive()
29+
message = await receive()
2930
assert scope["type"] == "http"
30-
if payload.get("type") == "http.request":
31+
if message.get("type") == "http.request":
3132
await send(
3233
{
3334
"type": "http.response.start",
@@ -41,15 +42,15 @@ async def http_app(scope, receive, send):
4142
async def websocket_app(scope, receive, send):
4243
assert scope["type"] == "websocket"
4344
while True:
44-
payload = await receive()
45-
if payload.get("type") == "websocket.connect":
45+
message = await receive()
46+
if message.get("type") == "websocket.connect":
4647
await send({"type": "websocket.accept"})
4748

48-
if payload.get("type") == "websocket.receive":
49-
if payload.get("text") == "ping":
49+
if message.get("type") == "websocket.receive":
50+
if message.get("text") == "ping":
5051
await send({"type": "websocket.send", "text": "pong"})
5152

52-
if payload.get("type") == "websocket.disconnect":
53+
if message.get("type") == "websocket.disconnect":
5354
break
5455

5556

@@ -63,9 +64,9 @@ async def simple_asgi(scope, receive, send):
6364

6465
async def error_asgi(scope, receive, send):
6566
assert isinstance(scope, dict)
66-
assert scope.get("type") == "http"
67-
payload = await receive()
68-
if payload.get("type") == "http.request":
67+
assert scope["type"] == "http"
68+
message = await receive()
69+
if message.get("type") == "http.request":
6970
try:
7071
raise ValueError
7172
except ValueError:
@@ -294,7 +295,7 @@ def setUp(self):
294295
setup_testing_defaults(self.scope)
295296
self.span = mock.create_autospec(trace_api.Span, spec_set=True)
296297

297-
def test_request_attributes(self):
298+
def test_query_string_utf8(self):
298299
self.scope["query_string"] = b"foo=bar"
299300

300301
attrs = otel_asgi.collect_request_attributes(self.scope)
@@ -314,6 +315,26 @@ def test_request_attributes(self):
314315
},
315316
)
316317

318+
def test_query_string_percent_encoding(self):
319+
self.scope["query_string"] = urllib.parse.quote(b"foo=bar")
320+
321+
attrs = otel_asgi.collect_request_attributes(self.scope)
322+
self.assertDictEqual(
323+
attrs,
324+
{
325+
"component": "http",
326+
"http.method": "GET",
327+
"http.host": "127.0.0.1",
328+
"http.target": "/",
329+
"http.url": "http://127.0.0.1/?foo=bar",
330+
"host.port": 80,
331+
"http.scheme": "http",
332+
"http.flavor": "1.0",
333+
"net.peer.ip": "127.0.0.1",
334+
"net.peer.port": 32767,
335+
},
336+
)
337+
317338
def test_response_attributes(self):
318339
otel_asgi.set_status_code(self.span, 404)
319340
expected = (mock.call("http.status_code", 404),)

tests/util/src/opentelemetry/test/asgitestutil.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
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+
115
import asyncio
216

317
from asgiref.testing import ApplicationCommunicator
@@ -38,7 +52,7 @@ def tearDown(self):
3852
def seed_app(self, app):
3953
self.communicator = ApplicationCommunicator(app, self.scope)
4054

41-
def send(self, message):
55+
def send_input(self, message):
4256
asyncio.get_event_loop().run_until_complete(
4357
self.communicator.send_input(message)
4458
)

tests/util/src/opentelemetry/test/spantestutil.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
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+
115
import unittest
216
from importlib import reload
317

tests/util/src/opentelemetry/test/wsgitestutil.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
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+
115
import io
216
import wsgiref.util as wsgiref_util
317

0 commit comments

Comments
 (0)