From c623d10df9ff0e8ba1ad2fe6902a8e020faf8fc0 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Tue, 5 Nov 2024 21:36:17 +0100 Subject: [PATCH] docs(ASGI): clean up docs on logging setup --- docs/user/faq.rst | 49 ++++++++-------- docs/user/tutorial-asgi.rst | 110 ++++++++++++++++++++---------------- falcon/typing.py | 3 +- 3 files changed, 86 insertions(+), 76 deletions(-) diff --git a/docs/user/faq.rst b/docs/user/faq.rst index 6790c8773..414cf08d3 100644 --- a/docs/user/faq.rst +++ b/docs/user/faq.rst @@ -1348,47 +1348,44 @@ To include multiple values, simply use ``"; "`` to separate each name-value pair. For example, if you were to pass ``{'Cookie': 'xxx=yyy; hello=world'}``, you would get ``{'cookies': {'xxx': 'yyy', 'hello': 'world'}}``. -Why do I not see error tracebacks in ASGI applications? -------------------------------------------------------- +Why do I see no error tracebacks in my ASGI application? +-------------------------------------------------------- -When using Falcon with ASGI servers like Uvicorn, -you might notice that server errors do not display a traceback by default. -This behavior differs from WSGI applications, where errors are logged to `stderr`, -providing detailed tracebacks. +When using Falcon with an ASGI server like Uvicorn, +you might notice that server errors do not include any traceback by default. +This behavior differs from WSGI, where the PEP-3333 specification defines the +`wsgi.errors `__ stream +(which Falcon utilizes to log unhandled +:class:`internal server errors `). -The reason for this is that ASGI does not define a standardized way to log errors back to the application server, -unlike WSGI. Therefore, you need to configure logging manually to see these tracebacks. +Since there is no standardized way to log errors back to the ASGI server, +the framework simply opts to log them using the ``falcon`` +:class:`logger `. -Here’s how to set up logging in your ASGI Falcon application to capture error tracebacks: +The easiest way to get started is configuring the root logger via +:func:`logging.basicConfig`: .. code:: python import logging + import falcon import falcon.asgi logging.basicConfig( - format="%(asctime)s [%(levelname)s] %(message)s", - level=logging.INFO - ) + format="%(asctime)s [%(levelname)s] %(message)s", level=logging.INFO) + - class ThingsResource: + class FaultyResource: async def on_get(self, req, resp): raise ValueError('foo') - app = falcon.asgi.App() - things = ThingsResource() - app.add_route('/things', things) - -By adding the above logging configuration, you will see tracebacks like this in your console: -.. code-block:: none + app = falcon.asgi.App() + app.add_route('/things', FaultyResource()) - [ERROR] [FALCON] Unhandled exception in ASGI app - Traceback (most recent call last): - File "<...>", line 12, in on_get - raise ValueError('foo') - ValueError: foo +By adding the above logging configuration, you should now see tracebacks logged +to :any:`stderr ` when accessing ``/things``. -For additional details on this topic, -please refer to :ref:`debugging-asgi-applications`. \ No newline at end of file +For additional details on this topic, +please refer to :ref:`debugging_asgi_applications`. diff --git a/docs/user/tutorial-asgi.rst b/docs/user/tutorial-asgi.rst index 8450a9efd..2d10c9378 100644 --- a/docs/user/tutorial-asgi.rst +++ b/docs/user/tutorial-asgi.rst @@ -115,6 +115,68 @@ Woohoo, it works!!! Well, sort of. Onwards to adding some real functionality! +.. _debugging_asgi_applications: + +Debugging ASGI Applications +--------------------------- + +While developing and testing a Falcon ASGI application along the lines of this +tutorial, you may encounter unexpected issues or behaviors, be it a copy-paste +mistake, an idea that didn't work out, or unusual input where validation falls +outside of the scope of this tutorial. + +Unlike WSGI, the ASGI specification has no standard mechanism for logging +errors back to the application server, so Falcon falls back to the stdlib's +:mod:`logging` (using the ``falcon`` :class:`logger `). + +As a well-behaved library, Falcon does not configure any loggers since that +might interfere with the user's logging setup. +Here's how you can set up basic logging in your ASGI Falcon application via +:func:`logging.basicConfig`: + +.. code:: python + + import logging + + import falcon + + logging.basicConfig(level=logging.INFO) + + + class ErrorResource: + def on_get(self, req, resp): + raise Exception('Something went wrong!') + + + app = falcon.App() + app.add_route('/error', ErrorResource()) + +When the above route is accessed, Falcon will catch the unhandled exception and +automatically log an error message. Below is an example of what the log output +might look like: + +.. code-block:: none + + ERROR:falcon.asgi.app:Unhandled exception in ASGI application + Traceback (most recent call last): + File "/path/to/your/app.py", line 123, in __call__ + resp = resource.on_get(req, resp) + File "/path/to/your/app.py", line 7, in on_get + raise Exception("Something went wrong!") + Exception: Something went wrong! + +.. note:: + While logging is helpful for development and debugging, be mindful of + logging sensitive information. Ensure that log files are stored securely + and are not accessible to unauthorized users. + +.. note:: + Unhandled errors are only logged automatically by Falcon's default error + handler for :class:`Exception`. If you + :meth:`replace this handler ` with your + own generic :class:`Exception` handler, you are responsible for logging or + reporting these errors yourself. + .. _asgi_tutorial_config: Configuration @@ -963,54 +1025,6 @@ adding ``--cov-fail-under=100`` (or any other percent threshold) to our tests in multiple environments would most probably involve running ``coverage`` directly, and combining results. -.. _debugging-asgi-applications: - -Debugging ASGI Applications ---------------------------- -(This section also applies to WSGI applications) - -While developing and testing ASGI applications, understanding how to configure -and utilize logging can be helpful, especially when you encounter unexpected -issues or behaviors. - -By default, Falcon does not set up logging for you, -but Python's built-in :mod:`logging` module provides a flexible framework for -emitting and capturing log messages. Here's how you can set up basic logging in -your ASGI Falcon application: - -.. code:: python - - import logging - import falcon - - logging.basicConfig(level=logging.INFO) - - class ErrorResource: - def on_get(self, req, resp): - raise Exception('Something went wrong!') - - app = falcon.App() - app.add_route('/error', ErrorResource()) - -When the above route is accessed, Falcon will catch the unhandled exception and -automatically log an error message. Below is an example of what the log output -might look like: - -.. code-block:: none - - ERROR:falcon.asgi.app:Unhandled exception in ASGI application - Traceback (most recent call last): - File "path/to/falcon/app.py", line 123, in __call__ - resp = resource.on_get(req, resp) - File "/path/to/your/app.py", line 7, in on_get - raise Exception("Something went wrong!") - Exception: Something went wrong! - -.. note:: - While logging is helpful for development and debugging, be mindful of logging - sensitive information. Ensure that log files are stored securely and are not - accessible to unauthorized users. - What Now? --------- diff --git a/falcon/typing.py b/falcon/typing.py index 7fa44fc4e..b1532442a 100644 --- a/falcon/typing.py +++ b/falcon/typing.py @@ -39,8 +39,7 @@ def read(self, n: Optional[int] = ..., /) -> bytes: ... # ASGI class AsyncReadableIO(Protocol): - """Async file-like protocol that defines only a read method, and is - iterable. + """Async file-like protocol that defines only a read method, and is iterable. .. versionadded:: 4.0 """