Add ability to log all exceptions (#2262)
* Add ability to log all exceptions * Fix linting 🙄 * Remove shorthand * Make `ErrorHandler.log` backwards-compat * Ignore mypy error * Don't store `noisy_exceptions` attribute in app * Added tests * Store noisy exceptions setting in config * Default to not-noisy if config key not available * Add CLI tests for `noisy-exceptions` * Remove debugging line I left in 😅 * Fix tests Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
This commit is contained in:
parent
71cc30e5cd
commit
f0f81ec458
|
@ -96,6 +96,11 @@ def main():
|
|||
help="number of worker processes [default 1]\n ",
|
||||
)
|
||||
parser.add_argument("-d", "--debug", dest="debug", action="store_true")
|
||||
parser.add_bool_arguments(
|
||||
"--noisy-exceptions",
|
||||
dest="noisy_exceptions",
|
||||
help="print stack traces for all exceptions",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--reload",
|
||||
|
@ -149,6 +154,7 @@ def main():
|
|||
f"Module is not a Sanic app, it is a {app_type_name}. "
|
||||
f"Perhaps you meant {args.module}.app?"
|
||||
)
|
||||
|
||||
if args.cert is not None or args.key is not None:
|
||||
ssl: Optional[Dict[str, Any]] = {
|
||||
"cert": args.cert,
|
||||
|
@ -165,7 +171,9 @@ def main():
|
|||
"debug": args.debug,
|
||||
"access_log": args.access_log,
|
||||
"ssl": ssl,
|
||||
"noisy_exceptions": args.noisy_exceptions,
|
||||
}
|
||||
|
||||
if args.auto_reload:
|
||||
kwargs["auto_reload"] = True
|
||||
|
||||
|
|
15
sanic/app.py
15
sanic/app.py
|
@ -962,6 +962,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
unix: Optional[str] = None,
|
||||
loop: None = None,
|
||||
reload_dir: Optional[Union[List[str], str]] = None,
|
||||
noisy_exceptions: Optional[bool] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Run the HTTP Server and listen until keyboard interrupt or term
|
||||
|
@ -994,6 +995,9 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
:type access_log: bool
|
||||
:param unix: Unix socket to listen on instead of TCP port
|
||||
:type unix: str
|
||||
:param noisy_exceptions: Log exceptions that are normally considered
|
||||
to be quiet/silent
|
||||
:type noisy_exceptions: bool
|
||||
:return: Nothing
|
||||
"""
|
||||
if reload_dir:
|
||||
|
@ -1032,6 +1036,9 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
if access_log is not None:
|
||||
self.config.ACCESS_LOG = access_log
|
||||
|
||||
if noisy_exceptions is not None:
|
||||
self.config.NOISY_EXCEPTIONS = noisy_exceptions
|
||||
|
||||
server_settings = self._helper(
|
||||
host=host,
|
||||
port=port,
|
||||
|
@ -1090,6 +1097,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
unix: Optional[str] = None,
|
||||
return_asyncio_server: bool = False,
|
||||
asyncio_server_kwargs: Dict[str, Any] = None,
|
||||
noisy_exceptions: Optional[bool] = None,
|
||||
) -> Optional[AsyncioServer]:
|
||||
"""
|
||||
Asynchronous version of :func:`run`.
|
||||
|
@ -1127,6 +1135,9 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
:param asyncio_server_kwargs: key-value arguments for
|
||||
asyncio/uvloop create_server method
|
||||
:type asyncio_server_kwargs: dict
|
||||
:param noisy_exceptions: Log exceptions that are normally considered
|
||||
to be quiet/silent
|
||||
:type noisy_exceptions: bool
|
||||
:return: AsyncioServer if return_asyncio_server is true, else Nothing
|
||||
"""
|
||||
|
||||
|
@ -1137,10 +1148,14 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
|||
protocol = (
|
||||
WebSocketProtocol if self.websocket_enabled else HttpProtocol
|
||||
)
|
||||
|
||||
# if access_log is passed explicitly change config.ACCESS_LOG
|
||||
if access_log is not None:
|
||||
self.config.ACCESS_LOG = access_log
|
||||
|
||||
if noisy_exceptions is not None:
|
||||
self.config.NOISY_EXCEPTIONS = noisy_exceptions
|
||||
|
||||
server_settings = self._helper(
|
||||
host=host,
|
||||
port=port,
|
||||
|
|
|
@ -27,6 +27,7 @@ DEFAULT_CONFIG = {
|
|||
"GRACEFUL_SHUTDOWN_TIMEOUT": 15.0, # 15 sec
|
||||
"KEEP_ALIVE_TIMEOUT": 5, # 5 seconds
|
||||
"KEEP_ALIVE": True,
|
||||
"NOISY_EXCEPTIONS": False,
|
||||
"PROXIES_COUNT": None,
|
||||
"REAL_IP_HEADER": None,
|
||||
"REGISTER": True,
|
||||
|
@ -51,6 +52,7 @@ class Config(dict):
|
|||
GRACEFUL_SHUTDOWN_TIMEOUT: float
|
||||
KEEP_ALIVE_TIMEOUT: int
|
||||
KEEP_ALIVE: bool
|
||||
NOISY_EXCEPTIONS: bool
|
||||
PROXIES_COUNT: Optional[int]
|
||||
REAL_IP_HEADER: Optional[str]
|
||||
REGISTER: bool
|
||||
|
|
|
@ -192,7 +192,8 @@ class ErrorHandler:
|
|||
@staticmethod
|
||||
def log(request, exception):
|
||||
quiet = getattr(exception, "quiet", False)
|
||||
if quiet is False:
|
||||
noisy = getattr(request.app.config, "NOISY_EXCEPTIONS", False)
|
||||
if quiet is False or noisy is True:
|
||||
try:
|
||||
url = repr(request.url)
|
||||
except AttributeError:
|
||||
|
|
|
@ -23,6 +23,7 @@ async def app_info_dump(app: Sanic, _):
|
|||
"access_log": app.config.ACCESS_LOG,
|
||||
"auto_reload": app.auto_reload,
|
||||
"debug": app.debug,
|
||||
"noisy_exceptions": app.config.NOISY_EXCEPTIONS,
|
||||
}
|
||||
logger.info(json.dumps(app_data))
|
||||
|
||||
|
|
|
@ -182,3 +182,21 @@ def test_version(cmd):
|
|||
version_string = f"Sanic {__version__}; Routing {__routing_version__}\n"
|
||||
|
||||
assert out == version_string.encode("utf-8")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cmd,expected",
|
||||
(
|
||||
("--noisy-exceptions", True),
|
||||
("--no-noisy-exceptions", False),
|
||||
),
|
||||
)
|
||||
def test_noisy_exceptions(cmd, expected):
|
||||
command = ["sanic", "fake.server.app", cmd]
|
||||
out, err, exitcode = capture(command)
|
||||
lines = out.split(b"\n")
|
||||
|
||||
app_info = lines[26]
|
||||
info = json.loads(app_info)
|
||||
|
||||
assert info["noisy_exceptions"] is expected
|
||||
|
|
|
@ -2,10 +2,11 @@ import asyncio
|
|||
import logging
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic import Sanic, handlers
|
||||
from sanic.exceptions import Forbidden, InvalidUsage, NotFound, ServerError
|
||||
from sanic.handlers import ErrorHandler
|
||||
from sanic.response import stream, text
|
||||
|
@ -227,3 +228,18 @@ def test_single_arg_exception_handler_notice(exception_handler_app, caplog):
|
|||
"v22.3, the legacy style lookup method will not work at all."
|
||||
)
|
||||
assert response.status == 400
|
||||
|
||||
|
||||
def test_error_handler_noisy_log(exception_handler_app, monkeypatch):
|
||||
err_logger = Mock()
|
||||
monkeypatch.setattr(handlers, "error_logger", err_logger)
|
||||
|
||||
exception_handler_app.config["NOISY_EXCEPTIONS"] = False
|
||||
exception_handler_app.test_client.get("/1")
|
||||
err_logger.exception.assert_not_called()
|
||||
|
||||
exception_handler_app.config["NOISY_EXCEPTIONS"] = True
|
||||
request, _ = exception_handler_app.test_client.get("/1")
|
||||
err_logger.exception.assert_called_with(
|
||||
"Exception occurred while handling uri: %s", repr(request.url)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user