Use wsgi.errors to for errors
This commit is contained in:
parent
54b9995e22
commit
24815255be
@ -204,19 +204,23 @@ class ThreadedHTTPSServer(socketserver.ThreadingMixIn, HTTPSServer):
|
||||
return super().process_request_thread(request, client_address)
|
||||
|
||||
|
||||
class ServerHandler(wsgiref.simple_server.ServerHandler):
|
||||
|
||||
|
||||
def log_exception(self, exc_info):
|
||||
logger.error("An exception occurred during request: %s",
|
||||
exc_info[1], exc_info=exc_info)
|
||||
|
||||
|
||||
class RequestHandler(wsgiref.simple_server.WSGIRequestHandler):
|
||||
"""HTTP requests handler."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Store exception for logging
|
||||
self.error_stream = io.StringIO()
|
||||
super().__init__(*args, **kwargs)
|
||||
def log_request(self, code="-", size="-"):
|
||||
"""Disable request logging."""
|
||||
|
||||
def get_stderr(self):
|
||||
return self.error_stream
|
||||
|
||||
def log_message(self, *args, **kwargs):
|
||||
"""Disable inner logging management."""
|
||||
def log_error(self, format, *args):
|
||||
msg = format % args
|
||||
logger.error("An error occurred during request: %s" % msg)
|
||||
|
||||
def get_environ(self):
|
||||
env = super().get_environ()
|
||||
@ -228,12 +232,24 @@ class RequestHandler(wsgiref.simple_server.WSGIRequestHandler):
|
||||
return env
|
||||
|
||||
def handle(self):
|
||||
super().handle()
|
||||
# Log exception
|
||||
error = self.error_stream.getvalue().strip("\n")
|
||||
if error:
|
||||
logger.error(
|
||||
"An unhandled exception occurred during request:\n%s" % error)
|
||||
"""Copy of WSGIRequestHandler.handle with different ServerHandler"""
|
||||
|
||||
self.raw_requestline = self.rfile.readline(65537)
|
||||
if len(self.raw_requestline) > 65536:
|
||||
self.requestline = ''
|
||||
self.request_version = ''
|
||||
self.command = ''
|
||||
self.send_error(414)
|
||||
return
|
||||
|
||||
if not self.parse_request():
|
||||
return
|
||||
|
||||
handler = ServerHandler(
|
||||
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
|
||||
)
|
||||
handler.request_handler = self
|
||||
handler.run(self.server.get_app())
|
||||
|
||||
|
||||
class Application:
|
||||
@ -323,6 +339,7 @@ class Application:
|
||||
return read_allowed_items, write_allowed_items
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
with log.register_stream(environ["wsgi.errors"]):
|
||||
try:
|
||||
status, headers, answers = self._handle_request(environ)
|
||||
except Exception as e:
|
||||
@ -340,7 +357,8 @@ class Application:
|
||||
answer = answer.encode("ascii")
|
||||
status = "%d %s" % (
|
||||
status, client.responses.get(status, "Unknown"))
|
||||
headers = [("Content-Length", str(len(answer)))] + list(headers)
|
||||
headers = [
|
||||
("Content-Length", str(len(answer)))] + list(headers)
|
||||
answers = [answer]
|
||||
start_response(status, headers)
|
||||
return answers
|
||||
@ -990,12 +1008,13 @@ _application_config_path = None
|
||||
_application_lock = threading.Lock()
|
||||
|
||||
|
||||
def _init_application(config_path):
|
||||
def _init_application(config_path, wsgi_errors):
|
||||
global _application, _application_config_path
|
||||
with _application_lock:
|
||||
if _application is not None:
|
||||
return
|
||||
log.setup()
|
||||
with log.register_stream(wsgi_errors):
|
||||
_application_config_path = config_path
|
||||
configuration = config.load([config_path] if config_path else [],
|
||||
ignore_missing_paths=False)
|
||||
@ -1007,7 +1026,7 @@ def application(environ, start_response):
|
||||
config_path = environ.get("RADICALE_CONFIG",
|
||||
os.environ.get("RADICALE_CONFIG"))
|
||||
if _application is None:
|
||||
_init_application(config_path)
|
||||
_init_application(config_path, environ["wsgi.errors"])
|
||||
if _application_config_path != config_path:
|
||||
raise ValueError("RADICALE_CONFIG must not change: %s != %s" %
|
||||
(repr(config_path), repr(_application_config_path)))
|
||||
|
@ -22,6 +22,7 @@ http://docs.python.org/library/logging.config.html
|
||||
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import sys
|
||||
import threading
|
||||
@ -43,16 +44,64 @@ class RemoveTracebackFilter(logging.Filter):
|
||||
removeTracebackFilter = RemoveTracebackFilter()
|
||||
|
||||
|
||||
class ThreadStreamsHandler(logging.Handler):
|
||||
|
||||
terminator = "\n"
|
||||
|
||||
def __init__(self, fallback_stream, fallback_handler):
|
||||
super().__init__()
|
||||
self._streams = {}
|
||||
self.fallback_stream = fallback_stream
|
||||
self.fallback_handler = fallback_handler
|
||||
|
||||
def setFormatter(self, form):
|
||||
super().setFormatter(form)
|
||||
self.fallback_handler.setFormatter(form)
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
stream = self._streams.get(threading.get_ident())
|
||||
if stream is None:
|
||||
self.fallback_handler.emit(record)
|
||||
else:
|
||||
msg = self.format(record)
|
||||
stream.write(msg)
|
||||
stream.write(self.terminator)
|
||||
if hasattr(stream, "flush"):
|
||||
stream.flush()
|
||||
except Exception:
|
||||
self.handleError(record)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def register_stream(self, stream):
|
||||
if stream == self.fallback_stream:
|
||||
yield
|
||||
return
|
||||
key = threading.get_ident()
|
||||
self._streams[key] = stream
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
del self._streams[key]
|
||||
|
||||
|
||||
def get_default_handler():
|
||||
handler = logging.StreamHandler(sys.stderr)
|
||||
return handler
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def register_stream(stream):
|
||||
"""Register global errors stream for the current thread."""
|
||||
yield
|
||||
|
||||
|
||||
def setup():
|
||||
"""Set global logging up."""
|
||||
global register_stream, unregister_stream
|
||||
handler = get_default_handler()
|
||||
handler = ThreadStreamsHandler(sys.stderr, get_default_handler())
|
||||
logging.basicConfig(format=LOGGER_FORMAT, handlers=[handler])
|
||||
register_stream = handler.register_stream
|
||||
set_debug(True)
|
||||
|
||||
|
||||
|
@ -43,6 +43,7 @@ class BaseTest:
|
||||
data = data.encode("utf-8")
|
||||
args["wsgi.input"] = BytesIO(data)
|
||||
args["CONTENT_LENGTH"] = str(len(data))
|
||||
args["wsgi.errors"] = sys.stderr
|
||||
self.application._answer = self.application(args, self.start_response)
|
||||
|
||||
return (
|
||||
|
Loading…
Reference in New Issue
Block a user