Skip to content

Commit 38b736a

Browse files
authored
1. Protect Django and Flask views 2. Django REST Framework request body support. 3. Simplify error reporting using
#1 Protect Django and Flask views #2 Django REST Framework request body support. #3 Simplify error reporting using context manager capture_message capture_exception methods
2 parents 14250a9 + 98fa367 commit 38b736a

47 files changed

Lines changed: 773 additions & 178 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ install:
1414

1515
script:
1616
- coverage run tests/DjangoTest/runners/common_runner.py
17+
- coverage run -a tests/DjangoTest/runners/drf_test_runner.py
1718
- coverage run -a tests/DjangoTest/runners/custom_masking_test_runner.py
1819
- coverage run -a tests/DjangoTest/runners/masking_disabled_test_runner.py
1920
- coverage run -a tests/DjangoTest/runners/model_test_runner.py

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ app.py
9696
# Record exception when 404 error code is raised
9797
@app.errorhandler(403)
9898
def error_403(e):
99-
error_tracker.record_exception()
99+
error_tracker.capture_exception()
100100
# any custom logic
101101
102102
# Record error using decorator

docs/source/django-app.rst

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,33 +94,85 @@ Setting details
9494
.. note::
9595
Class must implements all abstract methods
9696

97+
- Exception Listing page permission
98+
By default exception listing is enabled for only admin users.
99+
100+
101+
.. code::
102+
103+
APP_ERROR_VIEW_PERMISSION = 'permission.ErrorViewPermission'
104+
105+
.. note::
106+
Class must not have any constructor parameters and should implement __call__ method.
107+
97108

98109
Manual Exception Tracking
99110
~~~~~~~~~~~~~~~~~~~~~~~~~
100111

101-
Error can be tracked programmatically using ErrorTracker's object available in middleware module. For tracking exception call
102-
error_tracker.record_exception method.
112+
Error can be tracked programmatically using `ErrorTracker`'s utility methods available in error_tracker module.
113+
For tracking exception call `capture_exception` method.
103114

104115
.. code::
105116
106-
from error_tracker.django.middleware import error_tracker
117+
from error_tracker import capture_exception
107118
108119
...
109120
try
110121
...
111122
catch Exception as e:
112-
error_tracker.record_exception(request, e)
123+
capture_exception(request=request, exception=e)
124+
125+
126+
A message can be captured using `capture_message` method.
127+
128+
.. code::
129+
130+
from error_tracker import capture_message
131+
132+
try
133+
...
134+
catch Exception as e:
135+
capture_message("Something went wrong", request=request, exception=e)
136+
137+
138+
113139
114140
Decorator based exception recording, record exception as it occurs in a method call.
115141

116142
.. note::
117143
Exception will be re-raised so it must be caught in the caller or ignored.
144+
Re-raising of exception can be disabled using `silent=True` parameter
118145

119146
.. code::
120147
121-
from error_tracker.django.middleware import track_exception
148+
from error_tracker import track_exception
122149
123150
@track_exception
124151
def do_something():
125152
...
126153
154+
So far, you have seen only uses where context is provided upfront using default context builder or some other means.
155+
Sometimes, we need to put context based on the current code path, like add user_id and email in login flow.
156+
ErrorTracker comes with context manager that can be used for such use cases.
157+
158+
.. code::
159+
160+
from error_tracker import configure_scope
161+
162+
with configure_scope(request=request) as scope:
163+
scope.set_extra("user_id", 1234)
164+
scope.set_extra("email", "example@example.com"
165+
166+
167+
In this case whenever exception would be raised, it will capture the exception automatically and these context details would be stored as well.
168+
169+
170+
.. code::
171+
172+
{
173+
...
174+
"context" : {
175+
"id" : 1234,
176+
"email" : "example@example.com"
177+
}
178+
}

docs/source/flask-app.rst

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ Config details
6767
6868
Manual Exception Tracking
6969
~~~~~~~~~~~~~~~~~~~~~~~~~
70-
Error can be tracked programmatically using AppErrorTracker's record_exception method.
70+
Error can be tracked programmatically using AppErrorTracker's capture_exception method.
71+
ErrorTracker provides many ways to capture error.
72+
73+
Capture Error using `capture_exception` method
74+
`capture_exception` takes another parameter for `additional_context` (dictionary of key value pairs).
75+
This parameter can be used to provide additional details about the failure.
76+
7177

7278
.. code::
7379
@@ -76,19 +82,56 @@ Error can be tracked programmatically using AppErrorTracker's record_exception m
7682
try
7783
...
7884
catch Exception as e:
79-
error_tracker.record_exception()
85+
error_tracker.capture_exception()
86+
8087
88+
A simple Message can be captured using `capture_message` method.
89+
90+
91+
.. code::
92+
93+
try
94+
...
95+
catch Exception as e:
96+
error_tracker.capture_message("Something went wrong!")
8197
8298
8399
Decorator based exception recording, record exception as it occurs in a method call.
84100

85101
.. note::
86102
Exception will be re-raised so it must be caught in the caller or ignored.
103+
Raised exception can be ignored by passing `silent=True`.
104+
Also more context detail can be provided using `additional_context` parameter.
87105

88106

89107
.. code::
90108
91-
error_tracker = AppErrorTracker(...)
92109
@error_tracker.auto_track_exception
93110
def fun():
94111
pass
112+
113+
114+
So far, you have seen only uses where context is provided upfront using default context builder or some other means.
115+
Sometimes, we need to put context based on the current code path, like add user_id and email in login flow.
116+
ErrorTracker comes with context manager that can be used for such use cases.
117+
118+
.. code::
119+
120+
from error_tracker import flask_scope
121+
122+
with flask_scope() as scope:
123+
scope.set_extra("user_id", 1234)
124+
scope.set_extra("email", "example@example.com" )
125+
126+
127+
Now error_tracker will automatically capture exception as it will occur. This data will be stored in request_data detail as
128+
129+
.. code::
130+
131+
{
132+
...
133+
"context" : {
134+
"id" : 1234,
135+
"email" : "example@example.com"
136+
}
137+
}

docs/source/index.rst

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Recording exception/error
5252
An error/exception can be recorded using decorator or function call.
5353

5454
- To record the error using decorator, decorate a function with :code:`track_exception` or :code:`auto_track_exception`
55-
- Where as to record error using function call use :code:`record_exception` function.
55+
- Where as to record error using function call use :code:`capture_exception` function.
5656
- Exception detail can be written to a file, console or logger etc call method :code:`print_exception`
5757

5858
All the data will be stored in the configured data store and these data will be available at configure URL path.
@@ -64,6 +64,12 @@ An instance of :code:`AppErrorTracker` needs to be created and have to be config
6464
Monitoring feature can be configured either using object based configuration or app-based configuration,
6565
the only important thing here is we should have all the required key configs in the app.config otherwise it will fail.
6666

67+
.. note::
68+
Exception listing page is disabled by default. You need to enable that using view_permission parameter.
69+
view_permission function/callable class must return True/False based on the current request detail.
70+
This method would be called as view_permission(request).
71+
72+
6773
For object based configuration add
6874
**settings.py**
6975

@@ -97,15 +103,22 @@ For object based configuration add
97103
message = Message(email_subject, recipient_list, email_body, sender=from_email)
98104
self.send(message)
99105
mailer = Notifier(app=app)
100-
error_tracker = AppErrorTracker(app=app, db=db, notifier=mailer)
106+
107+
# enable for all users
108+
class ViewPermission(ViewPermissionMixin):
109+
def __call__(self, request):
110+
return True
111+
112+
error_tracker = AppErrorTracker(app=app, db=db, notifier=mailer, view_permission=ViewPermission())
113+
101114
102115
....
103116
104117
....
105118
# Record exception when 404 error code is raised
106119
@app.errorhandler(403)
107120
def error_403(e):
108-
error_tracker.record_exception()
121+
error_tracker.capture_exception()
109122
# any custom logic
110123
111124
# Record error using decorator
@@ -131,12 +144,19 @@ We need to update settings.py file as
131144

132145
.. [1] This should be added at the end so that it can process exception 1st in the middleware call stack.
133146
147+
.. note::
148+
Exception listing page is only enable for admin by default.
149+
You can enable for others by providing a custom implementation of ViewPermissionMixin.
150+
This class must return True/False based on the current request, False means not authorized, True means authorized.
151+
134152
.. code::
135153
136154
...
137155
APP_ERROR_RECIPIENT_EMAIL = ('example@example.com',)
138156
APP_ERROR_SUBJECT_PREFIX = "Server Error"
139157
APP_ERROR_EMAIL_SENDER = 'user@example.com'
158+
# optional setting otherwise it's enabled for admin only
159+
APP_ERROR_VIEW_PERMISSION = 'permission.ErrorViewPermission'
140160
141161
INSTALLED_APPS = [
142162
...
@@ -168,14 +188,14 @@ For example, if we want to use Flask then do
168188
* Create Flask App instance
169189
* Create Error Tracker app instance
170190
* DO NOT call run method of Flask app instance
171-
* To track exception call :code:`error_tracker.record_exception` method
191+
* To track exception call :code:`capture_exception` method
172192

173193
- Django App
174194
* Create Django App with settings and all configuration
175195
* Set environment variable **DJANGO_SETTINGS_MODULE**
176196
* call :code:`django.setup()`
177-
* :code:`from error_tracker.django.middleware import error_tracker`
178-
* To track exception do :code:`error_tracker.record_exception(None, exception)`
197+
* :code:`from error_tracker import error_tracker`
198+
* To track exception do :code:`capture_exception(None, exception)`
179199

180200

181201

error_tracker/__init__.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,34 @@
22
#
33
# Error Tracking app
44
#
5-
# :copyright: 2019 Sonu Kumar
5+
# :copyright: 2020 Sonu Kumar
66
# :license: BSD-3-Clause
77
#
88

9-
__version__ = '1.1.2'
9+
__version__ = '1.1.3'
1010
__author__ = 'Sonu Kumar'
1111
__email__ = 'sonunitw12@gmail.com'
1212

1313
from error_tracker.libs.mixins import *
1414
from error_tracker.flask import *
1515
from error_tracker.django import *
16+
from error_tracker.flask.utils import configure_scope as flask_scope
1617
from error_tracker.django.apps import DjangoErrorTracker
18+
from error_tracker.django.utils import capture_message, track_exception, configure_scope, capture_exception
1719
from error_tracker.libs.exception_formatter import *
1820

1921
__all__ = [
20-
"AppErrorTracker", "DefaultFlaskContextBuilder",
22+
# flask modules
23+
"AppErrorTracker", "DefaultFlaskContextBuilder", "flask_scope",
24+
25+
# mixin classes
2126
"NotificationMixin", "ModelMixin", "MaskingMixin",
22-
"ContextBuilderMixin", "TicketingMixin",
23-
"DefaultDjangoContextBuilder", "DjangoErrorTracker",
27+
"ContextBuilderMixin", "TicketingMixin", "ViewPermissionMixin",
28+
29+
# Django modules
30+
"DefaultDjangoContextBuilder", "DjangoErrorTracker", "DefaultDjangoViewPermission",
31+
"capture_message", "track_exception", "configure_scope", "capture_exception",
32+
33+
# lower level methods
2434
"format_exception", "print_exception"
2535
]

error_tracker/django/__init__.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,20 @@
22
#
33
# Django components
44
#
5-
# :copyright: 2019 Sonu Kumar
5+
# :copyright: 2020 Sonu Kumar
66
# :license: BSD-3-Clause
77
#
88

99

10-
from .utils import DefaultDjangoContextBuilder, DjangoNotification
11-
from .settings import APP_ERROR_MASKED_KEY_HAS, APP_ERROR_MASK_WITH, APP_ERROR_RECIPIENT_EMAIL, \
12-
APP_ERROR_EMAIL_SENDER, get
10+
from .utils import DefaultDjangoContextBuilder, DjangoNotification, DefaultDjangoViewPermission
11+
from .settings import *
1312

1413
from .utils import DjangoNotification, DefaultDjangoContextBuilder
1514
from error_tracker.libs.utils import Masking, get_class_from_path, get_class_instance
16-
from error_tracker import ModelMixin, MaskingMixin, TicketingMixin, NotificationMixin, ContextBuilderMixin
15+
from error_tracker import ModelMixin, MaskingMixin, TicketingMixin, NotificationMixin, ContextBuilderMixin, \
16+
ViewPermissionMixin
1717
from django.apps import apps as django_apps
1818
import warnings
19-
from .settings import * # noqa
2019

2120

2221
def get_exception_model():
@@ -68,4 +67,6 @@ def get_context_builder():
6867
DefaultDjangoContextBuilder, "ContextBuilder")
6968

7069

71-
70+
def get_view_permission():
71+
return get_class_instance(APP_ERROR_VIEW_PERMISSION, ViewPermissionMixin, DefaultDjangoViewPermission,
72+
"ViewPermission")

0 commit comments

Comments
 (0)