-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: original copy of code_owner code
This is the code_owner code as found in edx-django-utils.
- Loading branch information
Showing
9 changed files
with
1,094 additions
and
0 deletions.
There are no files selected for viewing
6 changes: 6 additions & 0 deletions
6
edx_arch_experiments/datadog_monitoring/code_owner/__init__.py
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,6 @@ | ||
""" | ||
This directory should only be used internally. | ||
Its public API is exposed in the top-level monitoring __init__.py. | ||
See its README.rst for details. | ||
""" |
175 changes: 175 additions & 0 deletions
175
edx_arch_experiments/datadog_monitoring/code_owner/middleware.py
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,175 @@ | ||
""" | ||
Middleware for code_owner custom attribute | ||
""" | ||
import logging | ||
|
||
from django.urls import resolve | ||
from django.urls.exceptions import Resolver404 | ||
|
||
from ..utils import set_custom_attribute | ||
from .utils import ( | ||
_get_catch_all_code_owner, | ||
get_code_owner_from_module, | ||
is_code_owner_mappings_configured, | ||
set_code_owner_custom_attributes | ||
) | ||
|
||
try: | ||
import newrelic.agent | ||
except ImportError: | ||
newrelic = None # pylint: disable=invalid-name | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class MonitoringTransaction(): | ||
""" | ||
Represents a monitoring transaction (likely the current transaction). | ||
""" | ||
def __init__(self, transaction): | ||
self.transaction = transaction | ||
|
||
@property | ||
def name(self): | ||
""" | ||
The name of the transaction. | ||
For NewRelic, the name may look like: | ||
openedx.core.djangoapps.contentserver.middleware:StaticContentServer | ||
""" | ||
if self.transaction and hasattr(self.transaction, 'name'): | ||
return self.transaction.name | ||
return None | ||
|
||
|
||
def get_current_transaction(): | ||
""" | ||
Returns the current transaction. This is only used internally and won't | ||
be ported over to the backends framework, because transactions will be | ||
very different based on the backend. | ||
""" | ||
current_transaction = None | ||
if newrelic: | ||
current_transaction = newrelic.agent.current_transaction() | ||
|
||
return MonitoringTransaction(current_transaction) | ||
|
||
|
||
class CodeOwnerMonitoringMiddleware: | ||
""" | ||
Django middleware object to set custom attributes for the owner of each view. | ||
For instructions on usage, see: | ||
https://github.com/openedx/edx-django-utils/blob/master/edx_django_utils/monitoring/docs/how_tos/add_code_owner_custom_attribute_to_an_ida.rst | ||
Custom attributes set: | ||
- code_owner: The owning team mapped to the current view. | ||
- code_owner_module: The module found from the request or current transaction. | ||
- code_owner_path_error: The error mapping by path, if code_owner isn't found in other ways. | ||
- code_owner_transaction_error: The error mapping by transaction, if code_owner isn't found in other ways. | ||
- code_owner_transaction_name: The current transaction name used to try to map to code_owner. | ||
This can be used to find missing mappings. | ||
""" | ||
def __init__(self, get_response): | ||
self.get_response = get_response | ||
|
||
def __call__(self, request): | ||
response = self.get_response(request) | ||
self._set_code_owner_attribute(request) | ||
return response | ||
|
||
def process_exception(self, request, exception): # pylint: disable=W0613 | ||
self._set_code_owner_attribute(request) | ||
|
||
def _set_code_owner_attribute(self, request): | ||
""" | ||
Sets the code_owner custom attribute for the request. | ||
""" | ||
code_owner = None | ||
module = self._get_module_from_request(request) | ||
if module: | ||
code_owner = get_code_owner_from_module(module) | ||
if not code_owner: | ||
code_owner = _get_catch_all_code_owner() | ||
|
||
if code_owner: | ||
set_code_owner_custom_attributes(code_owner) | ||
|
||
def _get_module_from_request(self, request): | ||
""" | ||
Get the module from the request path or the current transaction. | ||
Side-effects: | ||
Sets code_owner_module custom attribute, used to determine code_owner. | ||
If module was not found, may set code_owner_path_error and/or | ||
code_owner_transaction_error custom attributes if applicable. | ||
Returns: | ||
str: module name or None if not found | ||
""" | ||
if not is_code_owner_mappings_configured(): | ||
return None | ||
|
||
module, path_error = self._get_module_from_request_path(request) | ||
if module: | ||
set_custom_attribute('code_owner_module', module) | ||
return module | ||
|
||
module, transaction_error = self._get_module_from_current_transaction() | ||
if module: | ||
set_custom_attribute('code_owner_module', module) | ||
return module | ||
|
||
# monitor errors if module was not found | ||
if path_error: | ||
set_custom_attribute('code_owner_path_error', path_error) | ||
if transaction_error: | ||
set_custom_attribute('code_owner_transaction_error', transaction_error) | ||
return None | ||
|
||
def _get_module_from_request_path(self, request): | ||
""" | ||
Uses the request path to get the view_func module. | ||
Returns: | ||
(str, str): (module, error_message), where at least one of these should be None | ||
""" | ||
try: | ||
view_func, _, _ = resolve(request.path) | ||
module = view_func.__module__ | ||
return module, None | ||
# TODO: Replace ImportError with ModuleNotFoundError when Python 3.5 support is dropped. | ||
except (ImportError, Resolver404) as e: | ||
return None, str(e) | ||
except Exception as e: # pragma: no cover | ||
# will remove broad exceptions after ensuring all proper cases are covered | ||
set_custom_attribute('deprecated_broad_except__get_module_from_request_path', e.__class__) | ||
return None, str(e) | ||
|
||
def _get_module_from_current_transaction(self): | ||
""" | ||
Uses the current transaction to get the module. | ||
Side-effects: | ||
Sets code_owner_transaction_name custom attribute, used to determine code_owner | ||
Returns: | ||
(str, str): (module, error_message), where at least one of these should be None | ||
""" | ||
try: | ||
# Example: openedx.core.djangoapps.contentserver.middleware:StaticContentServer | ||
transaction_name = get_current_transaction().name | ||
if not transaction_name: | ||
return None, 'No current transaction name found.' | ||
module = transaction_name.split(':')[0] | ||
set_custom_attribute('code_owner_transaction_name', transaction_name) | ||
return module, None | ||
except Exception as e: | ||
# will remove broad exceptions after ensuring all proper cases are covered | ||
set_custom_attribute('deprecated_broad_except___get_module_from_current_transaction', e.__class__) | ||
return None, str(e) |
Oops, something went wrong.