diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6b1bba3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: sonus21 diff --git a/LICENSE.txt b/LICENSE.txt index f7ce0a1..8d53443 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2019-2020 Sonu Kumar +Copyright 2019-2023 Sonu Kumar Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/error_tracker/__init__.py b/error_tracker/__init__.py index bc3b36f..5a72780 100644 --- a/error_tracker/__init__.py +++ b/error_tracker/__init__.py @@ -2,11 +2,11 @@ # # Error Tracking app # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # -__version__ = '2.1.0' +__version__ = '3.1.1' __author__ = 'Sonu Kumar' __email__ = 'sonunitw12@gmail.com' diff --git a/error_tracker/django/__init__.py b/error_tracker/django/__init__.py index 0d6c946..0addf62 100644 --- a/error_tracker/django/__init__.py +++ b/error_tracker/django/__init__.py @@ -2,7 +2,7 @@ # # Django components # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/error_tracker/django/middleware.py b/error_tracker/django/middleware.py index 63d9e2f..f58b81d 100644 --- a/error_tracker/django/middleware.py +++ b/error_tracker/django/middleware.py @@ -2,7 +2,7 @@ # # Django error tracker middleware responsible for recording exception # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/error_tracker/django/models.py b/error_tracker/django/models.py index 0c10b6b..04f3ef7 100644 --- a/error_tracker/django/models.py +++ b/error_tracker/django/models.py @@ -2,7 +2,7 @@ # # Django error tracker default model # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # from django.core.paginator import Paginator, EmptyPage diff --git a/error_tracker/django/templates/error_tracker/base.html b/error_tracker/django/templates/error_tracker/base.html index b8186e5..c8c9d99 100755 --- a/error_tracker/django/templates/error_tracker/base.html +++ b/error_tracker/django/templates/error_tracker/base.html @@ -38,7 +38,7 @@
{% block header_block %}

- {% trans 'Errors Seen' %} + {% trans 'Errors Seen' %}

{% endblock %} {% block content_block %} diff --git a/error_tracker/django/templates/error_tracker/partials/partial_table.html b/error_tracker/django/templates/error_tracker/partials/partial_table.html index 720c29a..3388f9b 100644 --- a/error_tracker/django/templates/error_tracker/partials/partial_table.html +++ b/error_tracker/django/templates/error_tracker/partials/partial_table.html @@ -4,7 +4,7 @@ {{ error.host }} {{ error.method }} - + {{ error.path|truncatechars:30 }} @@ -12,7 +12,7 @@ {{ error.last_seen }} {{ error.count }} - {%trans 'Delete' %} diff --git a/error_tracker/django/urls.py b/error_tracker/django/urls.py index 85bc7be..e1b3047 100755 --- a/error_tracker/django/urls.py +++ b/error_tracker/django/urls.py @@ -6,11 +6,12 @@ # :license: BSD-3-Clause # -from django.conf.urls import url +from django.urls import path from .views import detail, view_list, delete_exception +app_name = 'error_tracker' urlpatterns = [ - url(r'^$', view_list, name="view_errors"), - url(r'^(?P[\w-]+)/delete$', delete_exception, name='delete_error'), - url(r'^(?P[\w-]+)$', detail, name='view_error'), + path('', view_list, name="view_errors"), + path('/delete', delete_exception, name='delete_error'), + path('', detail, name='view_error'), ] diff --git a/error_tracker/django/utils.py b/error_tracker/django/utils.py index 0b5afa6..99d54cf 100644 --- a/error_tracker/django/utils.py +++ b/error_tracker/django/utils.py @@ -2,7 +2,7 @@ # # Django error tracker utils classes # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/error_tracker/django/views.py b/error_tracker/django/views.py index e83076a..97a6f7c 100644 --- a/error_tracker/django/views.py +++ b/error_tracker/django/views.py @@ -2,7 +2,7 @@ # # Django error tracker default value # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # from django.http import Http404, HttpResponse, JsonResponse @@ -42,13 +42,14 @@ def view_list(request): error = False errors = model.get_exceptions_per_page(**query) - next_url = reverse('view_errors') + "?page=" + str(errors.next_num) \ + next_url = reverse('error_tracker:view_errors') + "?page=" + str(errors.next_num) \ if errors.has_next else None - prev_url = reverse('view_errors') + "?page=" + str(errors.prev_num) \ + prev_url = reverse('error_tracker:view_errors') + "?page=" + str(errors.prev_num) \ if errors.has_prev else None - if request.is_ajax() or request.GET.get('ajax_partial'): + is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' + if is_ajax or request.GET.get('ajax_partial'): table = render_to_string('error_tracker/partials/partial_table.html', { 'errors': errors, }) @@ -75,7 +76,7 @@ def delete_exception(request, rhash): :return: redirect back to home page """ model.delete_entity(rhash) - return redirect(reverse('view_errors')) + return redirect(reverse('error_tracker:view_errors')) @require_GET diff --git a/error_tracker/flask/flask_error.py b/error_tracker/flask/flask_error.py index 9d7f5c7..805a651 100755 --- a/error_tracker/flask/flask_error.py +++ b/error_tracker/flask/flask_error.py @@ -2,7 +2,7 @@ # # Error tracker's flask plugin, this class initialize it's internal state # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # @@ -215,7 +215,7 @@ def create_or_update_entity(cls, rhash, host, path, method, request_data, @classmethod def get_exceptions_per_page(cls, page_number=1): return cls.query.order_by(desc(cls.last_seen)).paginate( - page_number, page_size, False) + page=page_number, per_page=page_size, error_out=False) @classmethod def get_entity(cls, rhash): @@ -285,10 +285,11 @@ def capture_exception(self, additional_context=None): ty, frames, frame_str, traceback_str, rhash, request_data = \ get_context_detail(rq, self.masking, self.context_builder, additional_context) - error = self.model.create_or_update_entity(rhash, host, path, method, - str(request_data), - get_exception_name(ty), - traceback_str) + with self.app.app_context(): + error = self.model.create_or_update_entity(rhash, host, path, method, + str(request_data), + get_exception_name(ty), + traceback_str) self._post_process(rq, frame_str, frames, error) def auto_track_exception(self, func, additional_context=None, silent=False): @@ -329,7 +330,8 @@ def get_exceptions(self, page_number=1): :return: list of exception objects """ if self.model: - return self.model.get_exceptions_per_page(page_number=page_number).items + with self.app.app_context(): + return self.model.get_exceptions_per_page(page_number=page_number).items raise ConfigError def get_exception(self, rhash): @@ -339,7 +341,8 @@ def get_exception(self, rhash): :return: exception object """ if self.model: - return self.model.get_entity(rhash) + with self.app.app_context(): + return self.model.get_entity(rhash) raise ConfigError def delete_exception(self, rhash): @@ -349,12 +352,14 @@ def delete_exception(self, rhash): :return: whatever model returns """ if self.model: - return self.model.delete_entity(rhash) + with self.app.app_context(): + return self.model.delete_entity(rhash) raise ConfigError def create_or_update_exception(self, rhash, host, path, method, request_data, exception_name, traceback): if self.model: - return self.model.create_or_update_entity(rhash, host, path, method, request_data, - exception_name, traceback) + with self.app.app_context(): + return self.model.create_or_update_entity(rhash, host, path, method, request_data, + exception_name, traceback) raise ConfigError diff --git a/error_tracker/flask/utils.py b/error_tracker/flask/utils.py index ffc5e74..d9a9ada 100644 --- a/error_tracker/flask/utils.py +++ b/error_tracker/flask/utils.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Utils modules for flask plugin # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # from error_tracker import ContextBuilderMixin, ViewPermissionMixin diff --git a/error_tracker/flask/view.py b/error_tracker/flask/view.py index 2bcb3c2..e898dfc 100644 --- a/error_tracker/flask/view.py +++ b/error_tracker/flask/view.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Exception formatter defaults view for flask app # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/error_tracker/libs/exception_formatter.py b/error_tracker/libs/exception_formatter.py index 4c54416..76580c6 100755 --- a/error_tracker/libs/exception_formatter.py +++ b/error_tracker/libs/exception_formatter.py @@ -2,7 +2,7 @@ # # Exception formatter that captures frame details in string format. # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # @@ -37,7 +37,7 @@ def convert_if_possible(x): return "QueryDict({%s})", x.dict() except ImportError: pass - return None, None + return None, x def format_frame(x, max_elements, max_string, max_recursion, masking=None): @@ -168,8 +168,9 @@ def format_exception(tb, max_elements=1000, w(val) if not masked: try: - w(format_frame(value, max_elements, max_string, max_recursion, - masking=masking)) + formatted_val = format_frame(value, max_elements, max_string, max_recursion, + masking=masking) + w(formatted_val) except Exception: exc_class = sys.exc_info()[0] w("<%s raised while printing value>" % exc_class) diff --git a/error_tracker/libs/mixins.py b/error_tracker/libs/mixins.py index 38779ab..dfaef03 100755 --- a/error_tracker/libs/mixins.py +++ b/error_tracker/libs/mixins.py @@ -2,7 +2,7 @@ # # Exception formatter mixin classes # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/error_tracker/libs/utils.py b/error_tracker/libs/utils.py index 3aceace..d1447f7 100644 --- a/error_tracker/libs/utils.py +++ b/error_tracker/libs/utils.py @@ -2,7 +2,7 @@ # # Exception formatter utils module # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # import sys diff --git a/setup.py b/setup.py index 4b290ab..42744ab 100755 --- a/setup.py +++ b/setup.py @@ -69,6 +69,8 @@ def grep(attrname): 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) diff --git a/tests/DjangoTest/DjangoTest/drf_urls.py b/tests/DjangoTest/DjangoTest/drf_urls.py index 939c976..2542732 100644 --- a/tests/DjangoTest/DjangoTest/drf_urls.py +++ b/tests/DjangoTest/DjangoTest/drf_urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url, include +from django.urls import path, include from django.contrib import admin from rest_framework import routers from error_tracker.django import urls @@ -9,11 +9,11 @@ router.register(r'users', UserViewSet) urlpatterns = [ - url('admin/', admin.site.urls), - url("dev/", include(urls)), - url(r'^$', views.index), - url(r'^value-error$', views.value_error), - url(r'^post-view$', views.post_view), - url(r'^', include(router.urls)), - url(r'^api-auth/', include('rest_framework.urls')) + path('admin/', admin.site.urls), + path('dev/', include(urls)), + path('', views.index), + path('value-error', views.value_error), + path('post-view', views.post_view), + path('', include(router.urls)), + path('api-auth/', include('rest_framework.urls')) ] diff --git a/tests/DjangoTest/DjangoTest/urls.py b/tests/DjangoTest/DjangoTest/urls.py index e22991c..b85bf2a 100644 --- a/tests/DjangoTest/DjangoTest/urls.py +++ b/tests/DjangoTest/DjangoTest/urls.py @@ -13,15 +13,15 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.conf.urls import url, include +from django.urls import path, include from django.contrib import admin from error_tracker.django import urls from core import views urlpatterns = [ - url('admin/', admin.site.urls), - url("dev/", include(urls)), - url(r'^$', views.index), - url(r'^value-error$', views.value_error), - url(r'^post-view$', views.post_view), + path('admin/', admin.site.urls), + path('dev/', include(urls)), + path('', views.index), + path('value-error', views.value_error), + path('post-view', views.post_view), ] diff --git a/tests/DjangoTest/tests/test_401_end_point.py b/tests/DjangoTest/tests/test_401_end_point.py index 2c2184e..077e253 100644 --- a/tests/DjangoTest/tests/test_401_end_point.py +++ b/tests/DjangoTest/tests/test_401_end_point.py @@ -7,7 +7,7 @@ # # Test view permission feature # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/DjangoTest/tests/test_drf.py b/tests/DjangoTest/tests/test_drf.py index 6514249..17357ec 100644 --- a/tests/DjangoTest/tests/test_drf.py +++ b/tests/DjangoTest/tests/test_drf.py @@ -2,7 +2,7 @@ # # Test Django Rest framework related changes # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/DjangoTest/tests/test_end_point.py b/tests/DjangoTest/tests/test_end_point.py index 63017b9..8cf9a11 100644 --- a/tests/DjangoTest/tests/test_end_point.py +++ b/tests/DjangoTest/tests/test_end_point.py @@ -2,7 +2,7 @@ # # Test all end points are working as expected # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/DjangoTest/tests/test_manual_error_tracking.py b/tests/DjangoTest/tests/test_manual_error_tracking.py index 8b5d07c..050bee2 100644 --- a/tests/DjangoTest/tests/test_manual_error_tracking.py +++ b/tests/DjangoTest/tests/test_manual_error_tracking.py @@ -2,7 +2,7 @@ # # Basic test case test, this tests basic part of application # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # import unittest diff --git a/tests/DjangoTest/tests/test_no_masking.py b/tests/DjangoTest/tests/test_no_masking.py index 7a6f2be..1d617cb 100644 --- a/tests/DjangoTest/tests/test_no_masking.py +++ b/tests/DjangoTest/tests/test_no_masking.py @@ -2,7 +2,7 @@ # # Test no masking feature # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # @@ -15,15 +15,18 @@ class NoMasking(TestBase, TestCase): def test_no_mask(self): - self.post('/post-view', data=dict( - username="username", - password="password")) + self.post('/post-view', data=dict(username="username", password="password")) errors = self.get_exceptions() error = errors[0] re1 = r".*password.* = .*" re2 = r".*secret.* = .*" + re3 = r".*form.* = .*" + re4 = r'.*l.* = \[.*\]' re1 = re.compile(re1, re.IGNORECASE) re2 = re.compile(re2, re.IGNORECASE) + re3 = re.compile(re3, re.IGNORECASE) + re4 = re.compile(re4, re.IGNORECASE) + exception = error.traceback matches1 = re1.findall(exception) matches2 = re2.findall(exception) @@ -33,6 +36,13 @@ def test_no_mask(self): key, value = match.split(" = ") self.assertNotEqual(value, "%r" % APP_ERROR_MASK_WITH) + matches3 = re3.findall(exception) + matches4 = re4.findall(exception) + self.assertEqual(1, len(matches4)) + self.assertEqual(1, len(matches3)) + self.assertEqual("[1, 2, 3, 4]", matches4[0].strip().split("l = ")[1]) + data = matches3[0].strip().split("form = ")[1].split("QueryDict(")[1].split(')')[0] + self.assertEqual(True, data == "{'password' : 'password', 'username' : 'username'}") re3 = r".*password.* : .*" re3 = re.compile(re3, re.IGNORECASE) matches3 = re3.findall(exception) diff --git a/tests/DjangoTest/tests/util.py b/tests/DjangoTest/tests/util.py index c437a0a..b191c86 100644 --- a/tests/DjangoTest/tests/util.py +++ b/tests/DjangoTest/tests/util.py @@ -2,7 +2,7 @@ # # Test utils # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/FlaskTest/test_401_views.py b/tests/FlaskTest/test_401_views.py index 96aa940..8ba4a49 100644 --- a/tests/FlaskTest/test_401_views.py +++ b/tests/FlaskTest/test_401_views.py @@ -2,7 +2,7 @@ # # Test view permission feature # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/FlaskTest/test_db_model.py b/tests/FlaskTest/test_db_model.py index 6cfa064..996eeeb 100644 --- a/tests/FlaskTest/test_db_model.py +++ b/tests/FlaskTest/test_db_model.py @@ -2,7 +2,7 @@ # # Test custom model features # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # diff --git a/tests/FlaskTest/test_end_point.py b/tests/FlaskTest/test_end_point.py index 09c3224..42652aa 100644 --- a/tests/FlaskTest/test_end_point.py +++ b/tests/FlaskTest/test_end_point.py @@ -69,18 +69,19 @@ def test_pagination(self): exception = error_tracker.get_exceptions()[0] hashx = exception.hash inserted = 0 - i = 0 while inserted < 20: - i += 1 - idx = str(i) + hashx[2:] inserted += 1 + idx = str(inserted) + hashx[2:] + print("Inserting ", idx) error_tracker.create_or_update_exception(idx, exception.host, exception.path, exception.method, exception.request_data, exception.exception_name, exception.traceback) + response = c.get('/dev/error', follow_redirects=True) - urls = [node.attrib['href'] for node in pyquery.PyQuery(response.data)('a')] + data = response.data + urls = [node.attrib['href'] for node in pyquery.PyQuery(data)('a')] self.assertEqual(len(urls), pagination_config.APP_DEFAULT_LIST_SIZE * 2 + 1) self.assertTrue('/dev/error/?page=2' in urls) diff --git a/tests/FlaskTest/test_init_later.py b/tests/FlaskTest/test_init_later.py index 3390646..d01fb4d 100644 --- a/tests/FlaskTest/test_init_later.py +++ b/tests/FlaskTest/test_init_later.py @@ -2,7 +2,7 @@ # # Test app initialization post constructions # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # @@ -24,8 +24,9 @@ def _setup(self, db_file): db = SQLAlchemy(app) error_tracker = AppErrorTracker() error_tracker.init_app(app, db, view_permission=ViewPermission()) - db.drop_all() - db.create_all() + with app.app_context(): + db.drop_all() + db.create_all() return app, db, error_tracker diff --git a/tests/FlaskTest/test_manual_error_tracking.py b/tests/FlaskTest/test_manual_error_tracking.py index 988a2d9..668bebd 100644 --- a/tests/FlaskTest/test_manual_error_tracking.py +++ b/tests/FlaskTest/test_manual_error_tracking.py @@ -2,7 +2,7 @@ # # Test manual error tracking is working or not # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # import unittest diff --git a/tests/FlaskTest/utils.py b/tests/FlaskTest/utils.py index da81d20..9921bdb 100644 --- a/tests/FlaskTest/utils.py +++ b/tests/FlaskTest/utils.py @@ -2,7 +2,7 @@ # # Test's util class # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause # @@ -97,6 +97,7 @@ def _setup(self, db_file): app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///%s" % db_file db = SQLAlchemy(app) error_tracker = AppErrorTracker(app=app, db=db, view_permission=ViewPermission(), **self.kwargs) - db.drop_all() - db.create_all() + with app.app_context(): + db.drop_all() + db.create_all() return app, db, error_tracker diff --git a/tests/utils.py b/tests/utils.py index 7979849..7e75884 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,7 +2,7 @@ # # Test utility # -# :copyright: 2020 Sonu Kumar +# :copyright: 2023 Sonu Kumar # :license: BSD-3-Clause #