Skip to content

Commit

Permalink
feat: original copy of code_owner code
Browse files Browse the repository at this point in the history
This is the code_owner code as found in edx-django-utils.
  • Loading branch information
robrap committed Oct 23, 2024
1 parent 8d5b10c commit bd1cb6b
Show file tree
Hide file tree
Showing 9 changed files with 1,094 additions and 0 deletions.
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 edx_arch_experiments/datadog_monitoring/code_owner/middleware.py
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)
Loading

0 comments on commit bd1cb6b

Please sign in to comment.