Skip to content

Commit 2663f11

Browse files
committed
Option for logging request and response bodies
1 parent 2417aff commit 2663f11

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

asgi_proxy/__init__.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from urllib.parse import urlparse
33

44

5-
def asgi_proxy(backend, log=None, timeout=None):
5+
def asgi_proxy(
6+
backend, log=None, timeout=None, log_request_body=None, log_response_body=None
7+
):
68
backend_host = urlparse(backend).netloc
79

810
async def asgi_proxy(scope, receive, send):
@@ -32,6 +34,10 @@ async def asgi_proxy(scope, receive, send):
3234
body += message.get("body", b"")
3335
more_body = message.get("more_body", False)
3436

37+
# Log request body if requested
38+
if log_request_body:
39+
log_request_body(body)
40+
3541
async with httpx.AsyncClient(timeout=timeout) as client:
3642
try:
3743
# Stream it, in case of long streaming responses
@@ -41,6 +47,10 @@ async def asgi_proxy(scope, receive, send):
4147
if log:
4248
log.info(f"Request: {method} {url}")
4349
log.info(f"Response: {resp.status_code} {resp.reason_phrase}")
50+
51+
# Initialize response body accumulator if needed
52+
response_body = b"" if log_response_body else None
53+
4454
# Start the response
4555
await send(
4656
{
@@ -57,6 +67,8 @@ async def asgi_proxy(scope, receive, send):
5767
# aiter_raw not aiter_bytes because we don't want
5868
# content decoding to have been applied
5969
async for chunk in resp.aiter_raw():
70+
if log_response_body:
71+
response_body += chunk
6072
await send(
6173
{
6274
"type": "http.response.body",
@@ -71,9 +83,18 @@ async def asgi_proxy(scope, receive, send):
7183
f"Client disconnected: {e.__class__.__name__}: {e}"
7284
)
7385
await send({"type": "http.response.body", "more_body": False})
86+
87+
# Log response body even if client disconnected
88+
if log_response_body and response_body is not None:
89+
log_response_body(response_body)
7490
return
7591

7692
await send({"type": "http.response.body", "more_body": False})
93+
94+
# Log response body if requested
95+
if log_response_body and response_body is not None:
96+
log_response_body(response_body)
97+
7798
except httpx.TimeoutException as ex:
7899
if log:
79100
log.error(f"Timeout error occurred: {ex.__class__.__name__}: {ex}")

asgi_proxy/__main__.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,31 @@
2222
"--timeout", type=float, default=None, help="Timeout in seconds"
2323
)
2424
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose logging")
25+
parser.add_argument(
26+
"-b",
27+
"--bodies",
28+
action="store_true",
29+
help="Log full request and response bodies",
30+
)
2531

2632
args = parser.parse_args()
2733

28-
if args.verbose:
34+
if args.verbose or args.bodies:
2935
import logging
3036

3137
logging.basicConfig(level=logging.INFO)
32-
app = asgi_proxy(args.url, log=logging, timeout=args.timeout)
38+
log_request_body = None
39+
log_response_body = None
40+
if args.bodies:
41+
log_request_body = logging.info
42+
log_response_body = logging.info
43+
app = asgi_proxy(
44+
args.url,
45+
log=logging,
46+
timeout=args.timeout,
47+
log_request_body=log_request_body,
48+
log_response_body=log_response_body,
49+
)
3350
else:
3451
app = asgi_proxy(args.url, timeout=args.timeout)
3552

0 commit comments

Comments
 (0)