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)
|
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):
|
class RequestHandler(wsgiref.simple_server.WSGIRequestHandler):
|
||||||
"""HTTP requests handler."""
|
"""HTTP requests handler."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def log_request(self, code="-", size="-"):
|
||||||
# Store exception for logging
|
"""Disable request logging."""
|
||||||
self.error_stream = io.StringIO()
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_stderr(self):
|
def log_error(self, format, *args):
|
||||||
return self.error_stream
|
msg = format % args
|
||||||
|
logger.error("An error occurred during request: %s" % msg)
|
||||||
def log_message(self, *args, **kwargs):
|
|
||||||
"""Disable inner logging management."""
|
|
||||||
|
|
||||||
def get_environ(self):
|
def get_environ(self):
|
||||||
env = super().get_environ()
|
env = super().get_environ()
|
||||||
@ -228,12 +232,24 @@ class RequestHandler(wsgiref.simple_server.WSGIRequestHandler):
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
super().handle()
|
"""Copy of WSGIRequestHandler.handle with different ServerHandler"""
|
||||||
# Log exception
|
|
||||||
error = self.error_stream.getvalue().strip("\n")
|
self.raw_requestline = self.rfile.readline(65537)
|
||||||
if error:
|
if len(self.raw_requestline) > 65536:
|
||||||
logger.error(
|
self.requestline = ''
|
||||||
"An unhandled exception occurred during request:\n%s" % error)
|
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:
|
class Application:
|
||||||
@ -323,26 +339,28 @@ class Application:
|
|||||||
return read_allowed_items, write_allowed_items
|
return read_allowed_items, write_allowed_items
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
try:
|
with log.register_stream(environ["wsgi.errors"]):
|
||||||
status, headers, answers = self._handle_request(environ)
|
|
||||||
except Exception as e:
|
|
||||||
try:
|
try:
|
||||||
method = str(environ["REQUEST_METHOD"])
|
status, headers, answers = self._handle_request(environ)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
method = "unknown"
|
try:
|
||||||
try:
|
method = str(environ["REQUEST_METHOD"])
|
||||||
path = str(environ.get("PATH_INFO", ""))
|
except Exception:
|
||||||
except Exception:
|
method = "unknown"
|
||||||
path = ""
|
try:
|
||||||
logger.error("An exception occurred during %s request on %r: "
|
path = str(environ.get("PATH_INFO", ""))
|
||||||
"%s", method, path, e, exc_info=True)
|
except Exception:
|
||||||
status, headers, answer = INTERNAL_SERVER_ERROR
|
path = ""
|
||||||
answer = answer.encode("ascii")
|
logger.error("An exception occurred during %s request on %r: "
|
||||||
status = "%d %s" % (
|
"%s", method, path, e, exc_info=True)
|
||||||
status, client.responses.get(status, "Unknown"))
|
status, headers, answer = INTERNAL_SERVER_ERROR
|
||||||
headers = [("Content-Length", str(len(answer)))] + list(headers)
|
answer = answer.encode("ascii")
|
||||||
answers = [answer]
|
status = "%d %s" % (
|
||||||
start_response(status, headers)
|
status, client.responses.get(status, "Unknown"))
|
||||||
|
headers = [
|
||||||
|
("Content-Length", str(len(answer)))] + list(headers)
|
||||||
|
answers = [answer]
|
||||||
|
start_response(status, headers)
|
||||||
return answers
|
return answers
|
||||||
|
|
||||||
def _handle_request(self, environ):
|
def _handle_request(self, environ):
|
||||||
@ -990,24 +1008,25 @@ _application_config_path = None
|
|||||||
_application_lock = threading.Lock()
|
_application_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def _init_application(config_path):
|
def _init_application(config_path, wsgi_errors):
|
||||||
global _application, _application_config_path
|
global _application, _application_config_path
|
||||||
with _application_lock:
|
with _application_lock:
|
||||||
if _application is not None:
|
if _application is not None:
|
||||||
return
|
return
|
||||||
log.setup()
|
log.setup()
|
||||||
_application_config_path = config_path
|
with log.register_stream(wsgi_errors):
|
||||||
configuration = config.load([config_path] if config_path else [],
|
_application_config_path = config_path
|
||||||
ignore_missing_paths=False)
|
configuration = config.load([config_path] if config_path else [],
|
||||||
log.set_debug(configuration.getboolean("logging", "debug"))
|
ignore_missing_paths=False)
|
||||||
_application = Application(configuration)
|
log.set_debug(configuration.getboolean("logging", "debug"))
|
||||||
|
_application = Application(configuration)
|
||||||
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
def application(environ, start_response):
|
||||||
config_path = environ.get("RADICALE_CONFIG",
|
config_path = environ.get("RADICALE_CONFIG",
|
||||||
os.environ.get("RADICALE_CONFIG"))
|
os.environ.get("RADICALE_CONFIG"))
|
||||||
if _application is None:
|
if _application is None:
|
||||||
_init_application(config_path)
|
_init_application(config_path, environ["wsgi.errors"])
|
||||||
if _application_config_path != config_path:
|
if _application_config_path != config_path:
|
||||||
raise ValueError("RADICALE_CONFIG must not change: %s != %s" %
|
raise ValueError("RADICALE_CONFIG must not change: %s != %s" %
|
||||||
(repr(config_path), repr(_application_config_path)))
|
(repr(config_path), repr(_application_config_path)))
|
||||||
|
@ -22,6 +22,7 @@ http://docs.python.org/library/logging.config.html
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@ -43,16 +44,64 @@ class RemoveTracebackFilter(logging.Filter):
|
|||||||
removeTracebackFilter = RemoveTracebackFilter()
|
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():
|
def get_default_handler():
|
||||||
handler = logging.StreamHandler(sys.stderr)
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def register_stream(stream):
|
||||||
|
"""Register global errors stream for the current thread."""
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
"""Set global logging up."""
|
"""Set global logging up."""
|
||||||
global register_stream, unregister_stream
|
global register_stream, unregister_stream
|
||||||
handler = get_default_handler()
|
handler = ThreadStreamsHandler(sys.stderr, get_default_handler())
|
||||||
logging.basicConfig(format=LOGGER_FORMAT, handlers=[handler])
|
logging.basicConfig(format=LOGGER_FORMAT, handlers=[handler])
|
||||||
|
register_stream = handler.register_stream
|
||||||
set_debug(True)
|
set_debug(True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ class BaseTest:
|
|||||||
data = data.encode("utf-8")
|
data = data.encode("utf-8")
|
||||||
args["wsgi.input"] = BytesIO(data)
|
args["wsgi.input"] = BytesIO(data)
|
||||||
args["CONTENT_LENGTH"] = str(len(data))
|
args["CONTENT_LENGTH"] = str(len(data))
|
||||||
|
args["wsgi.errors"] = sys.stderr
|
||||||
self.application._answer = self.application(args, self.start_response)
|
self.application._answer = self.application(args, self.start_response)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
Loading…
Reference in New Issue
Block a user