Skip to content

Commit

Permalink
Add basic fallback error handler (#46)
Browse files Browse the repository at this point in the history
This commit adds a proper basic fallback error handler that shows debeug information and the context of when the error has occured.

This replaces the default Sanic error handler which does not show any debug information when development mode is off.

The styling for the error handler is very basic and serves as a placeholder until a proper design can be done for it.

This will likely be a v0.3.0 feature rather than v0.1.0 one.
  • Loading branch information
syeopite committed Jun 17, 2024
1 parent adfdaf5 commit 0231fa8
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 2 deletions.
18 changes: 18 additions & 0 deletions locales/en_US/LC_MESSAGES/priviblur.po
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,24 @@ msgstr "Priviblur was unable to complete the request to Tumblr before timing out
msgid "priviblur_error_invalid_internal_tumblr_redirect"
msgstr "Error: Tumblr HTTP 301 redirect points to foreign URL"

msgid "priviblur_error_generic"
msgstr "An unknown exception has occured!"

msgid "priviblur_error_generic_description"
msgstr "It looks like you have found a bug in Priviblur."

msgid "priviblur_error_generic_description_2"
msgstr "Please report it here at GitHub"

msgid "priviblur_error_generic_technical_details_error_name"
msgstr "Error: {0}"

msgid "priviblur_error_generic_technical_details_error_message"
msgstr "Message: {0}"

msgid "priviblur_error_generic_technical_details_error_context"
msgstr "Context:"

msgid "post_community_label_mature_heading"
msgstr "Community Label: Mature"

Expand Down
34 changes: 34 additions & 0 deletions src/exceptions/error_handlers/_base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import inspect

class ErrorHandlerGroup:
"""Manages and registers error handler functions for specific exceptions
Expand All @@ -24,3 +26,35 @@ def register_handlers_into_app(self, app):
for handler, exceptions in self.registered_handlers.items():
for exception in exceptions:
app.error_handler.add(exception, handler)


def create_user_friendly_error_message(request, exception):
# Taken from https://github.com/searxng/searxng/blob/f5eb56b63f250c7804e5e1cf4426e550bc933906/searx/metrics/error_recorder.py
exception_class = exception.__class__
exception_name = exception_class.__qualname__
exception_module = exception_class.__module__

if exception_module is None or exception_module == str.__class__.__module__:
processed_exception_name = exception_name
else:
processed_exception_name = f"{exception_module}.{exception_name}"

exception_message = str(exception) or None

frame = inspect.trace()
context = []

for trace in reversed(frame):
if trace.filename.startswith(request.app.ctx.PRIVIBLUR_PARENT_DIR_PATH):
local_path = trace.filename[len(request.app.ctx.PRIVIBLUR_PARENT_DIR_PATH) + 1:]
else:
local_path = trace.filename
occurrence = f"File \"{local_path}\" line {trace.lineno}: in {trace.function}"

# Add code context is applicable
if trace.code_context:
occurrence = f"{occurrence}\n {trace.code_context[0].strip()}"

context.append(occurrence)

return processed_exception_name, exception_message, '\n'.join(context)
17 changes: 17 additions & 0 deletions src/exceptions/error_handlers/miscellaneous_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,20 @@ async def invalid_redirect(request, exception):
},
status=502
)


@miscellaneous_errors.register(Exception)
async def generic_error(request, exception):
name, message, context = _base.create_user_friendly_error_message(request, exception)

return await sanic_ext.render(
"misc/generic_error.jinja",
context={
"app": request.app,
"exception": exception,
"exception_name": name,
"exception_message": message,
"exception_context": context,
},
status=500
)
5 changes: 3 additions & 2 deletions src/server.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import os
import asyncio
import logging
import urllib.parse
import functools
import copy

import sanic
import aiohttp
Expand Down Expand Up @@ -61,6 +59,9 @@
app.ctx.PRIVIBLUR_CONFIG = config
app.ctx.translate = i18n.translate

app.ctx.PRIVIBLUR_PARENT_DIR_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))


@app.listener("before_server_start")
async def initialize(app):
priviblur_backend = app.ctx.PRIVIBLUR_CONFIG.backend
Expand Down
80 changes: 80 additions & 0 deletions src/templates/misc/generic_error.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{# Displays information about some generic exception to the user #}

{% extends "base.jinja" %}
{% block head %}
<style>
#priviblur-error > div {
background-color: var(--color-top-level-card-bg);
border-radius: 25px;
padding: 25px;
max-width: 100%;
}
#priviblur-error h2 {
margin: 0;
}
#priviblur-error {
color: var(--color-text);
}
#priviblur-error details {
margin-top: 20px;
}
#priviblur-error pre {
text-wrap: wrap;
}
#priviblur-error a {
text-decoration: underline;
}
#error-header {
font-size: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
}
@media screen and (min-width: 768px) {
#priviblur-error pre {
text-wrap: nowrap;
}
#priviblur-error > div {
max-width: unset;
width: fit-content;
}
}
}
</style>

{% endblock %}
{% block title %}Error{% endblock %}

{% block center %}
<section id="priviblur-error">
<div class="card">
<div id="error-header">
<h2>{{translate(request.ctx.language, "priviblur_error_generic")}}</h2>
<p>{{translate(request.ctx.language, "priviblur_error_generic_description")}}</p>
<a href="https://github.com/syeopite/priviblur/issues/new">{{translate(request.ctx.language, "priviblur_error_generic_description_2")}}</a>
</div>

<div>
<details open="">
<summary>Technical details</summary>
<pre>{{translate(request.ctx.language, "priviblur_error_generic_technical_details_error_name", substitution=exception_name)}}</pre>
{% if exception_message %}<pre>{{translate(request.ctx.language, "priviblur_error_generic_technical_details_error_message", substitution='"' + exception_message + '"')}}</pre>{% endif %}
<pre>{{-translate(request.ctx.language, "priviblur_error_generic_technical_details_error_context")}}</pre>
<pre>{{exception_context}}</pre>
</details>
</div>

</div>
</section>
{%- endblock %}

0 comments on commit 0231fa8

Please sign in to comment.