diff --git a/MANIFEST.in b/MANIFEST.in old mode 100755 new mode 100644 diff --git a/error_tracker/django/__init__.py b/error_tracker/django/__init__.py index 0d6c946..217c55e 100644 --- a/error_tracker/django/__init__.py +++ b/error_tracker/django/__init__.py @@ -25,19 +25,19 @@ def get_exception_model(): from .models import ErrorModel model_path = APP_ERROR_DB_MODEL if model_path is None: - warnings.warn("APP_ERROR_DB_MODEL is not set using default model") + if not APP_ERROR_SUPPRESS_DEFAULT_WARNING: + warnings.warn("APP_ERROR_DB_MODEL is not set. Using default model") return ErrorModel try: return django_apps.get_model(model_path, require_ready=False) except ValueError: - model = get_class_from_path(model_path, ModelMixin, raise_exception=False, - warning_message="Model " + model_path + " is not importable") + model = get_class_from_path(model_path, ModelMixin, raise_exception=False, warning_message=f"Model {model_path} is not importable") if model is not None: return model warnings.warn("APP_ERROR_DB_MODEL must be of the form 'app_label.model_name'") except LookupError: model = get_class_from_path(model_path, ModelMixin, raise_exception=False, - warning_message="Model " + model_path + " is not importable") + warning_message=f"Model {model_path} is not importable") if model is not None: return model warnings.warn( diff --git a/error_tracker/django/admin.py b/error_tracker/django/admin.py index 7e5722e..5878a06 100644 --- a/error_tracker/django/admin.py +++ b/error_tracker/django/admin.py @@ -14,6 +14,7 @@ def has_add_permission(self, request): 'path', 'method', 'exception_name', + 'exception_text', 'count', 'created_on', 'last_seen', @@ -25,7 +26,7 @@ def has_add_permission(self, request): 'notification_sent', 'ticket_raised', ) - search_fields = ('host', 'path', 'exception_name',) + search_fields = ('host', 'path', 'exception_name', 'exception_text',) change_form_template = 'error_tracker/admin/change_form.html' def changeform_view(self, request, object_id=None, form_url='', extra_context=None): diff --git a/error_tracker/django/middleware.py b/error_tracker/django/middleware.py index 63d9e2f..fcb6b41 100644 --- a/error_tracker/django/middleware.py +++ b/error_tracker/django/middleware.py @@ -61,21 +61,10 @@ def _raise_ticket(request, error): @staticmethod def _post_process(request, frame_str, frames, error): - send_notification = True - raise_ticket = True - - if request is not None: - message = ('URL: %s' % request.path) + '\n\n' - else: - message = "" + message = f'URL: {request.path}' + '\n\n' if request is not None else "" message += frame_str - - if APP_ERROR_NOTIFICATION_ONCE is True and error.notification_sent is True: - send_notification = False - - if APP_ERROR_TICKET_ONCE is True and error.ticket_raised is True: - raise_ticket = False - + send_notification = APP_ERROR_NOTIFICATION_ONCE is not True or error.notification_sent is not True + raise_ticket = APP_ERROR_TICKET_ONCE is not True or error.ticket_raised is not True if send_notification: ErrorTracker._send_notification(request, message, frames[-1][:-1], error) if raise_ticket: @@ -102,10 +91,11 @@ def capture_exception(self, request=None, exception=None, additional_context=Non ty, frames, frame_str, traceback_str, rhash, request_data = \ get_context_detail(request, masking, context_builder, additional_context=additional_context) - error = model.create_or_update_entity(rhash, host, path, method, - str(request_data), - get_exception_name(ty), - traceback_str) + error = model.create_or_update_entity(rhash=rhash, host=host, path=path, method=method, + request_data=str(request_data), + exception_name=get_exception_name(ty), + exception_text=str(exception.args), + traceback=traceback_str) ErrorTracker._post_process(request, frame_str, frames, error) diff --git a/error_tracker/django/migrations/0001_initial.py b/error_tracker/django/migrations/0001_initial.py index d340815..79dfc19 100644 --- a/error_tracker/django/migrations/0001_initial.py +++ b/error_tracker/django/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.7 on 2019-11-29 07:29 +# Generated by Django 4.0.4 on 2022-09-09 05:47 from django.db import migrations, models import error_tracker.libs.mixins @@ -21,13 +21,17 @@ class Migration(migrations.Migration): ('method', models.CharField(max_length=64)), ('request_data', models.TextField()), ('exception_name', models.CharField(max_length=256)), + ('exception_text', models.CharField(max_length=1256)), ('traceback', models.TextField()), ('count', models.IntegerField(default=0)), ('created_on', models.DateTimeField(auto_now=True)), ('last_seen', models.DateTimeField(auto_now=True, db_index=True)), + ('notification_sent', models.BooleanField(default=False)), + ('ticket_raised', models.BooleanField(default=False)), ], options={ 'db_table': 'exceptions', + 'abstract': False, 'swappable': 'APP_ERROR_DB_MODEL', }, bases=(models.Model, error_tracker.libs.mixins.ModelMixin), diff --git a/error_tracker/django/migrations/0002_auto_20201018_1311.py b/error_tracker/django/migrations/0002_auto_20201018_1311.py deleted file mode 100644 index 3634abe..0000000 --- a/error_tracker/django/migrations/0002_auto_20201018_1311.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.2 on 2020-10-18 11:11 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('error_tracker', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='errormodel', - name='notification_sent', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='errormodel', - name='ticket_raised', - field=models.BooleanField(default=False), - ), - ] \ No newline at end of file diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 0c10b6b..71b0f1a 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -26,6 +26,7 @@ class AbstractErrorModel(models.Model, ModelMixin): method = models.CharField(max_length=64) request_data = models.TextField() exception_name = models.CharField(max_length=256) + exception_text = models.CharField(max_length=1256) traceback = models.TextField() count = models.IntegerField(default=0) created_on = models.DateTimeField(auto_now=True) @@ -39,21 +40,14 @@ def get_exceptions_per_page(cls, page_number=1, **query): if 'page' in query: page_number = query['page'] del query['page'] - query = {"{}__icontains".format(k): v for k, v in query.items()} - - if not query: - records = cls.objects.all().order_by('last_seen') - else: - records = cls.objects.filter(**query).order_by('last_seen') + query = {f"{k}__icontains": v for k, v in query.items()} + records = cls.objects.filter(**query).order_by('last_seen') if query else cls.objects.all().order_by('last_seen') paginator = Paginator(records, EXCEPTION_APP_DEFAULT_LIST_SIZE) try: page = paginator.page(page_number) - return Page(page.has_next(), - page.next_page_number() if page.has_next() else None, - page.has_previous(), - page.previous_page_number() if page.has_previous() else None, - page.object_list) + return Page(page.has_next(), page.next_page_number() if page.has_next() else None, page.has_previous(), page.previous_page_number() if page.has_previous() else None, page.object_list) + except EmptyPage: return Page(False, None, True, paginator.num_pages, []) @@ -62,12 +56,13 @@ def get_entity(cls, rhash): return cls.objects.get(hash=rhash) @classmethod - def create_or_update_entity(cls, rhash, host, path, method, request_data, exception_name, traceback): + def create_or_update_entity(cls, rhash, host, path, method, request_data, exception_name, exception_text, traceback): try: obj, created = cls.objects.get_or_create(hash=rhash) if created: obj.host, obj.path, obj.method, obj.request_data, obj.exception_name, obj.traceback = \ host, path, method, request_data, exception_name, traceback + obj.exception_text = exception_text obj.count = 1 obj.save() else: diff --git a/error_tracker/django/settings.py b/error_tracker/django/settings.py index 76d693d..cc3c4c8 100644 --- a/error_tracker/django/settings.py +++ b/error_tracker/django/settings.py @@ -50,3 +50,5 @@ def get(key, default): APP_ERROR_TICKET_ONCE = get('APP_ERROR_NOTIFICATION_ONCE', False) # Use django admin site APP_ERROR_USE_DJANGO_ADMIN_SITE = get('APP_ERROR_USE_DJANGO_ADMIN_SITE', False) +# suppress default warnings +APP_ERROR_SUPPRESS_DEFAULT_WARNING = get('APP_ERROR_SUPPRESS_DEFAULT_WARNING', False) diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html old mode 100755 new mode 100644 diff --git a/error_tracker/django/templates/error_tracker/detail.html b/error_tracker/django/templates/error_tracker/detail.html old mode 100755 new mode 100644 index 961873a..86ed674 --- a/error_tracker/django/templates/error_tracker/detail.html +++ b/error_tracker/django/templates/error_tracker/detail.html @@ -17,6 +17,17 @@

{{ error }}

+
+
{%trans 'Type' %}
+
{%trans 'Text' %}
+
+ {{ obj.exception_name }} +
+
+ {{ obj.exception_text }} +
+
+
{%trans 'First time seen' %}
diff --git a/error_tracker/django/templates/error_tracker/list.html b/error_tracker/django/templates/error_tracker/list.html old mode 100755 new mode 100644 diff --git a/error_tracker/django/urls.py b/error_tracker/django/urls.py old mode 100755 new mode 100644 diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 0b5afa6..fb06df6 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -6,6 +6,7 @@ # :license: BSD-3-Clause # +import contextlib import json import re @@ -28,21 +29,16 @@ def _get_form_data(request): return form post = request.POST if post is None or len(post) == 0: - body = None try: body = request.data except AttributeError: - try: + with contextlib.suppress(RawPostDataException): body = request.body - except RawPostDataException: - pass if body is not None: - try: + with contextlib.suppress(ImportError): from rest_framework.request import Request if isinstance(body, Request): form = body.data - except ImportError: - pass if len(form) == 0 and len(body) > 0: try: form = json.loads(body, encoding="UTF-8") diff --git a/error_tracker/flask/__init__.py b/error_tracker/flask/__init__.py old mode 100755 new mode 100644 diff --git a/error_tracker/flask/defaults.py b/error_tracker/flask/defaults.py old mode 100755 new mode 100644 diff --git a/error_tracker/flask/flask_error.py b/error_tracker/flask/flask_error.py old mode 100755 new mode 100644 diff --git a/error_tracker/flask/templates/error_tracker/base.html b/error_tracker/flask/templates/error_tracker/base.html old mode 100755 new mode 100644 diff --git a/error_tracker/flask/templates/error_tracker/detail.html b/error_tracker/flask/templates/error_tracker/detail.html old mode 100755 new mode 100644 diff --git a/error_tracker/flask/templates/error_tracker/list.html b/error_tracker/flask/templates/error_tracker/list.html old mode 100755 new mode 100644 diff --git a/error_tracker/libs/exception_formatter.py b/error_tracker/libs/exception_formatter.py old mode 100755 new mode 100644 diff --git a/error_tracker/libs/mixins.py b/error_tracker/libs/mixins.py old mode 100755 new mode 100644 diff --git a/error_tracker/libs/utils.py b/error_tracker/libs/utils.py index 3aceace..ef8bbb2 100644 --- a/error_tracker/libs/utils.py +++ b/error_tracker/libs/utils.py @@ -13,6 +13,7 @@ from error_tracker.libs.exception_formatter import format_exception from error_tracker.libs.mixins import MaskingMixin +from error_tracker.django.settings import APP_ERROR_SUPPRESS_DEFAULT_WARNING class Masking(MaskingMixin): @@ -110,6 +111,8 @@ def get_class_from_path(module_path, super_class, raise_exception=True, def get_class_instance(module_path, mixin, default, message_prefix, *args): + if module_path == 'UseDefault': + return default(*args) if module_path is not None: module = get_class_from_path(module_path, mixin, raise_exception=False, @@ -117,8 +120,9 @@ def get_class_instance(module_path, mixin, default, message_prefix, *args): if module is not None: return module(*args) if default is not None: - message = "Default " + message_prefix + " module will be used" - warnings.warn(message) + if not APP_ERROR_SUPPRESS_DEFAULT_WARNING: + message = "Default " + message_prefix + " module will be used" + warnings.warn(message) return default(*args) diff --git a/examples/DjangoSample/manage.py b/examples/DjangoSample/manage.py old mode 100755 new mode 100644 diff --git a/examples/flask-sample/app.py b/examples/flask-sample/app.py old mode 100755 new mode 100644 diff --git a/examples/flask-sample/settings.py b/examples/flask-sample/settings.py old mode 100755 new mode 100644 diff --git a/examples/flask-sample/templates/401.html b/examples/flask-sample/templates/401.html old mode 100755 new mode 100644 diff --git a/examples/flask-sample/templates/500.html b/examples/flask-sample/templates/500.html old mode 100755 new mode 100644 diff --git a/requirements-dev.txt b/requirements-dev.txt old mode 100755 new mode 100644 diff --git a/run-tests.sh b/run-tests.sh old mode 100755 new mode 100644 diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 diff --git a/tests/DjangoTest/tests/test_basic.py b/tests/DjangoTest/tests/test_basic.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/configs/__init__.py b/tests/FlaskTest/configs/__init__.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/configs/custom_mask_rule.py b/tests/FlaskTest/configs/custom_mask_rule.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/configs/masking_disabled.py b/tests/FlaskTest/configs/masking_disabled.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/configs/notification_config_disabled.py b/tests/FlaskTest/configs/notification_config_disabled.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/configs/notification_config_enabled.py b/tests/FlaskTest/configs/notification_config_enabled.py old mode 100755 new mode 100644 diff --git a/tests/FlaskTest/test_basic.py b/tests/FlaskTest/test_basic.py old mode 100755 new mode 100644 diff --git a/tests/__init__.py b/tests/__init__.py old mode 100755 new mode 100644