Moves and rename common/djangoapps/newrelic_custom_metrics.
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
"""
|
||||
This is an interface to the newrelic_custom_metrics middleware. Functions
|
||||
defined in this module can be used to report custom metrics to New Relic. For
|
||||
example:
|
||||
|
||||
import newrelic_custom_metrics
|
||||
...
|
||||
newrelic_custom_metrics.accumulate('xb_user_state.get_many.num_items', 4)
|
||||
|
||||
There is no need to do anything else. The metrics are automatically cleared
|
||||
before the next request.
|
||||
|
||||
We try to keep track of our custom metrics at:
|
||||
|
||||
https://openedx.atlassian.net/wiki/display/PERF/Custom+Metrics+in+New+Relic
|
||||
|
||||
TODO: supply additional public functions for storing strings and booleans.
|
||||
"""
|
||||
|
||||
from newrelic_custom_metrics import middleware
|
||||
|
||||
|
||||
def accumulate(name, value):
|
||||
"""
|
||||
Accumulate custom New Relic metric for the current request.
|
||||
|
||||
The named metric is accumulated by a numerical amount using the sum. All
|
||||
metrics are queued up in the request_cache for this request. At the end of
|
||||
the request, the newrelic_custom_metrics middleware will batch report all
|
||||
queued accumulated metrics to NR.
|
||||
|
||||
Arguments:
|
||||
name (str): The metric name. It should be period-delimited, and
|
||||
increase in specificty from left to right. For example:
|
||||
'xb_user_state.get_many.num_items'.
|
||||
value (number): The amount to accumulate into the named metric. When
|
||||
accumulate() is called multiple times for a given metric name
|
||||
during a request, the sum of the values for each call is reported
|
||||
for that metric. For metrics which don't make sense to accumulate,
|
||||
make sure to only call this function once during a request.
|
||||
"""
|
||||
middleware.NewRelicCustomMetrics.accumulate_metric(name, value)
|
||||
|
||||
|
||||
def increment(name):
|
||||
"""
|
||||
Increment a custom New Relic metric representing a counter.
|
||||
|
||||
Here we simply accumulate a new custom metric with a value of 1, and the
|
||||
middleware should automatically aggregate this metric.
|
||||
"""
|
||||
accumulate(name, 1)
|
||||
@@ -1,71 +0,0 @@
|
||||
"""
|
||||
Middleware for handling the storage, aggregation, and reporing of custom New
|
||||
Relic metrics.
|
||||
|
||||
This middleware will only call on the newrelic agent if there are any metrics
|
||||
to report for this request, so it will not incur any processing overhead for
|
||||
request handlers which do not record custom metrics.
|
||||
"""
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
try:
|
||||
import newrelic.agent
|
||||
except ImportError:
|
||||
log.warning("Unable to load NewRelic agent module")
|
||||
newrelic = None # pylint: disable=invalid-name
|
||||
|
||||
import request_cache
|
||||
|
||||
REQUEST_CACHE_KEY = 'newrelic_custom_metrics'
|
||||
|
||||
|
||||
class NewRelicCustomMetrics(object):
|
||||
"""
|
||||
The middleware class. Make sure to add below the request cache in
|
||||
MIDDLEWARE_CLASSES.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _get_metrics_cache(cls):
|
||||
"""
|
||||
Get a reference to the part of the request cache wherein we store New
|
||||
Relic custom metrics related to the current request.
|
||||
"""
|
||||
return request_cache.get_cache(name=REQUEST_CACHE_KEY)
|
||||
|
||||
@classmethod
|
||||
def accumulate_metric(cls, name, value):
|
||||
"""
|
||||
Accumulate a custom metric (name and value) in the metrics cache.
|
||||
"""
|
||||
metrics_cache = cls._get_metrics_cache()
|
||||
metrics_cache.setdefault(name, 0)
|
||||
metrics_cache[name] += value
|
||||
|
||||
@classmethod
|
||||
def _batch_report(cls):
|
||||
"""
|
||||
Report the collected custom metrics to New Relic.
|
||||
"""
|
||||
if not newrelic:
|
||||
return
|
||||
metrics_cache = cls._get_metrics_cache()
|
||||
for metric_name, metric_value in metrics_cache.iteritems():
|
||||
newrelic.agent.add_custom_parameter(metric_name, metric_value)
|
||||
|
||||
# Whether or not there was an exception, report any custom NR metrics that
|
||||
# may have been collected.
|
||||
|
||||
def process_response(self, request, response): # pylint: disable=unused-argument
|
||||
"""
|
||||
Django middleware handler to process a response
|
||||
"""
|
||||
self._batch_report()
|
||||
return response
|
||||
|
||||
def process_exception(self, request, exception): # pylint: disable=unused-argument
|
||||
"""
|
||||
Django middleware handler to process an exception
|
||||
"""
|
||||
self._batch_report()
|
||||
return None
|
||||
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
Tests for newrelic custom metrics.
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from mock import patch, call
|
||||
|
||||
import newrelic_custom_metrics
|
||||
|
||||
|
||||
class TestNewRelicCustomMetrics(TestCase):
|
||||
"""
|
||||
Test the newrelic_custom_metrics middleware and helpers
|
||||
"""
|
||||
|
||||
@patch('newrelic.agent')
|
||||
def test_cache_normal_contents(self, mock_newrelic_agent):
|
||||
"""
|
||||
Test normal usage of collecting and reporting custom New Relic metrics
|
||||
"""
|
||||
newrelic_custom_metrics.accumulate('hello', 10)
|
||||
newrelic_custom_metrics.accumulate('world', 10)
|
||||
newrelic_custom_metrics.accumulate('world', 10)
|
||||
newrelic_custom_metrics.increment('foo')
|
||||
newrelic_custom_metrics.increment('foo')
|
||||
|
||||
# based on the metric data above, we expect the following calls to newrelic:
|
||||
nr_agent_calls_expected = [
|
||||
call('hello', 10),
|
||||
call('world', 20),
|
||||
call('foo', 2),
|
||||
]
|
||||
|
||||
# fake a response to trigger metrics reporting
|
||||
newrelic_custom_metrics.middleware.NewRelicCustomMetrics().process_response(
|
||||
'fake request',
|
||||
'fake response',
|
||||
)
|
||||
|
||||
# Assert call counts to newrelic.agent.add_custom_parameter()
|
||||
expected_call_count = len(nr_agent_calls_expected)
|
||||
measured_call_count = mock_newrelic_agent.add_custom_parameter.call_count
|
||||
self.assertEqual(expected_call_count, measured_call_count)
|
||||
|
||||
# Assert call args to newrelic.agent.add_custom_parameter(). Due to
|
||||
# the nature of python dicts, call order is undefined.
|
||||
mock_newrelic_agent.add_custom_parameter.has_calls(nr_agent_calls_expected, any_order=True)
|
||||
@@ -89,6 +89,7 @@ from openedx.core.djangoapps.external_auth.login_and_register import (
|
||||
login as external_auth_login,
|
||||
register as external_auth_register
|
||||
)
|
||||
from openedx.core.djangoapps import monitoring_utils
|
||||
|
||||
import track.views
|
||||
|
||||
@@ -120,8 +121,6 @@ from openedx.core.djangoapps.embargo import api as embargo_api
|
||||
import analytics
|
||||
from eventtracking import tracker
|
||||
|
||||
import newrelic_custom_metrics
|
||||
|
||||
# Note that this lives in LMS, so this dependency should be refactored.
|
||||
from notification_prefs.views import enable_notifications
|
||||
|
||||
@@ -654,7 +653,7 @@ def dashboard(request):
|
||||
|
||||
# Record how many courses there are so that we can get a better
|
||||
# understanding of usage patterns on prod.
|
||||
newrelic_custom_metrics.accumulate('num_courses', len(course_enrollments))
|
||||
monitoring_utils.accumulate('num_courses', len(course_enrollments))
|
||||
|
||||
# sort the enrollment pairs by the enrollment date
|
||||
course_enrollments.sort(key=lambda x: x.created, reverse=True)
|
||||
|
||||
Reference in New Issue
Block a user