abersheeran / a2wsgi

Convert WSGI app to ASGI app or ASGI app to WSGI app.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question about exception handling

sralloza opened this issue · comments

I'm having problems detecting the root of some 500 error servers (using FastAPI, a2wsgi and Apache) because exceptions are not logged nor printed anywhere.

Given the following wsgi FastAPI app:

from a2wsgi import ASGIMiddleware
from fastapi import FastAPI

app = FastAPI()


@app.get("/good")
def good():
    return "good"


@app.get("/bad")
def bad():
    raise ValueError("some error")

application = ASGIMiddleware(app)

Executed by werkzeug simulating an wsgi server, as Apache with mod_wsgi:

from werkzeug import run_simple
run_simple("localhost", 80, application)

When accesing /good, the browser shows "good", as expected. But when accesing /bad, the browser outputs Internal Server Error, and no error is logged in the console. The console only outputs 127.0.0.1 - - [20/Oct/2020 22:33:44] "GET /bad HTTP/1.1" 500 -.

So, I'd like to know how could make mod_wsgi write the exception to the error log or (as I understand this issue may be outside the module's scope) how can I make a2wsgi print the exception to stderr, so then the mod_wsgi can treat the output as an error.
Thanks in advance.

commented

When you initiated this issue, I refactored the code of ASGIMiddleware. Maybe you can try the latest version of a2wsgi.

In addition, throwing exceptions is not what WSGI applications need to do, it should be done by WSGI Server. https://www.python.org/dev/peps/pep-3333/#the-start-response-callable


Can you use the following WSGI application to test and see if it has Traceback output?

def return_exc_info(environ, start_response):
    try:
        raise RuntimeError("Something went wrong")
    except RuntimeError:
        status = "500 Internal Server Error"
        output = b"Internal Server Error"
        headers = [
            ("Content-Type", "text/plain; charset=utf-8"),
            ("Content-Length", str(len(output))),
        ]
        start_response(status, headers, exc_info=sys.exc_info())
        yield output

First of all, thanks for the fast reply. I've read the PEP about wsgi and you're right, it's the server's job to check if the wsgi application raised an error.

I executed the code you sent and nothing is printed, but I think it's a werkzeug's problem.

Just in case someone has the same error, I decided to log myself the exception, using FastAPI's exception handling:

from uuid import uuid4
import logging

from starlette.responses import JSONResponse

logger = logging.getLogger(__name__)


@app.exception_handler(500)
def catch_errors(request, exc):
    error_id = uuid4()
    scope = request.scope
    request_info = (
        f"{request.client.host}: {scope['scheme'].upper()}/{scope['http_version']} "
        f"{scope['method']} {scope['path']}"
    )

    exc_info = (exc.__class__, exc, exc.__traceback__)
    logger.critical(
        "Unhandled exception [id=%s] in request '%s':",
        error_id,
        request_info,
        exc_info=exc_info,
    )

    return JSONResponse(
        status_code=500,
        content={
            "detail": "Internal Server Error, please contact the server administrator."
        },
        headers={"X-Error-ID": str(error_id)},
    )

By the way, amazing tool you have created. I'm using it to deploy a FastAPI backend using Apache, which doesn't support ASGI yet.

commented

Thank you for your sharing.