-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add decorator that can be used to log incoming REST request data (
#2364) [APER-3122] Adds a decorator that can be used to log incoming REST request data. This will be used to get more visibility into the grade updates flowing from the LMS to Credentials. This functionality will be gated using a WaffleSwitch so that we don't cause a massive spike in log size. This is intended to be used for debugging when needed.
- Loading branch information
1 parent
3e39f1f
commit b0ab6a0
Showing
5 changed files
with
166 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
""" | ||
Decorators for the API app of Credentials. | ||
""" | ||
|
||
import logging | ||
|
||
from django.conf import settings | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def log_incoming_request(func): | ||
""" | ||
A decorator that can be used to log information from an incoming REST request. Please take care if using this | ||
decorator, we don't want to accidentally log any secrets or sensitive PII. | ||
Use of this decorator is also gated by the `LOG_INCOMING_REQUESTS` waffle switch. This is to ensure that we can | ||
toggle this functionality as needed and keep log sizes reasonable. | ||
""" | ||
|
||
def wrapper(*args, **kwargs): | ||
if settings.LOG_INCOMING_REQUESTS.is_enabled(): | ||
try: | ||
request = args[1] | ||
data = request.body | ||
logger.info( | ||
f"{request.method} request received to endpoint [{request.get_full_path()}] from user " | ||
f"[{request.user.username}] originating from [{request.META.get('HTTP_HOST', 'Unknown')}] " | ||
f"with data: [{data}]" | ||
) | ||
except Exception as exc: | ||
# catch overly broad exception to try to ensure if logging doesn't work the request will still be | ||
# processed | ||
logger.error(f"Error logging incoming request: {exc}. Continuing...") | ||
|
||
return func(*args, **kwargs) | ||
|
||
return wrapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
""" | ||
Tests for the API app's `decorators.py` file. | ||
""" | ||
|
||
from django.conf import settings | ||
from django.http import HttpResponse | ||
from django.test import TestCase | ||
from django.test.client import RequestFactory | ||
from edx_toggles.toggles.testutils import override_waffle_switch | ||
from testfixtures import LogCapture | ||
|
||
from credentials.apps.api.v2.decorators import log_incoming_request | ||
from credentials.apps.core.tests.factories import UserFactory | ||
|
||
|
||
class CredentialsApiDecoratorTests(TestCase): | ||
""" | ||
Unit tests for the API app's `decorators.py` file. | ||
""" | ||
|
||
def setUp(self): | ||
super().setUp() | ||
self.request = RequestFactory().post("/api/v2/grades/") | ||
self.user = UserFactory() | ||
self.request.user = self.user | ||
|
||
def _test_view(self, request, *args, **kwargs): | ||
return HttpResponse("YOLO") | ||
|
||
@override_waffle_switch(settings.LOG_INCOMING_REQUESTS, active=True) | ||
def test_log_incoming_requests_decorator_waffle_enabled(self): | ||
""" | ||
Test that ensures when a function decorated with the `log_incoming_requests` decorator, it emits the expected | ||
log messages if the setting gating the functionality is enabled. | ||
""" | ||
expected_msg = ( | ||
f"{self.request.method} request received to endpoint [{self.request.get_full_path()}] from user " | ||
f"[{self.request.user.username}] originating from [Unknown] with data: [{self.request.body}]" | ||
) | ||
decorated_view = log_incoming_request(self._test_view) | ||
|
||
args = (None, self.request) | ||
kwargs = {} | ||
with LogCapture() as log: | ||
decorated_view(*args, **kwargs) | ||
|
||
assert log.records[0].msg == expected_msg | ||
|
||
@override_waffle_switch(settings.LOG_INCOMING_REQUESTS, active=False) | ||
def test_log_incoming_requests_decorator_waffle_disabled(self): | ||
""" | ||
Test that ensures when a function decorated with the `log_incoming_requests` decorator, it does not emit log | ||
messges if the setting gating the functionality is disabled. | ||
""" | ||
decorated_view = log_incoming_request(self._test_view) | ||
|
||
args = (None, self.request) | ||
kwargs = {} | ||
with LogCapture() as log: | ||
decorated_view(*args, **kwargs) | ||
|
||
assert log.records == [] | ||
|
||
@override_waffle_switch(settings.LOG_INCOMING_REQUESTS, active=True) | ||
def test_log_incoming_requests_decorator_with_exception(self): | ||
""" | ||
Test that verifies an expected error message in our logs if an exception occurs when trying to log request data | ||
while using the `log_incoming_requests` decorator. | ||
""" | ||
expected_msg = "Error logging incoming request: 'NoneType' object has no attribute 'body'. Continuing..." | ||
|
||
decorated_view = log_incoming_request(self._test_view) | ||
|
||
with LogCapture() as log: | ||
response = decorated_view(None, None) | ||
|
||
assert log.records[0].msg == expected_msg | ||
assert response.status_code == 200 | ||
assert response.content == b"YOLO" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters