Commit 6a55ef79 by Joel Barciauskas Committed by GitHub

Merge pull request #14633 from edx/jbarciauskas/remove-newrelic-dep

Wrap all newrelic dependencies in a check to see if the module is loaded
parents b71f7d98 630985b3
......@@ -6,7 +6,14 @@ 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 newrelic.agent
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'
......@@ -40,6 +47,8 @@ class NewRelicCustomMetrics(object):
"""
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)
......
......@@ -14,7 +14,6 @@ from lxml import etree
from xblock.core import XBlock
from xblock.fields import Integer, Scope, Boolean, String
from xblock.fragment import Fragment
import newrelic.agent
from .exceptions import NotFoundError
from .fields import Date
......@@ -25,6 +24,11 @@ from .xml_module import XmlDescriptor
log = logging.getLogger(__name__)
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
# HACK: This shouldn't be hard-coded to two types
# OBSOLETE: This obsoletes 'type'
class_priority = ['video', 'problem']
......@@ -385,6 +389,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
"""
Capture basic information about this sequence in New Relic.
"""
if not newrelic:
return
newrelic.agent.add_custom_parameter('seq.block_id', unicode(self.location))
newrelic.agent.add_custom_parameter('seq.display_name', self.display_name or '')
newrelic.agent.add_custom_parameter('seq.position', self.position)
......@@ -396,6 +402,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
the sequence as a whole. We send this information to New Relic so that
we can do better performance analysis of courseware.
"""
if not newrelic:
return
# Basic count of the number of Units (a.k.a. VerticalBlocks) we have in
# this learning sequence
newrelic.agent.add_custom_parameter('seq.num_units', len(display_items))
......@@ -414,6 +422,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
"""
Capture information about the current selected Unit within the Sequence.
"""
if not newrelic:
return
# Positions are stored with indexing starting at 1. If we get into a
# weird state where the saved position is out of bounds (e.g. the
# content was changed), avoid going into any details about this unit.
......
......@@ -8,7 +8,6 @@ import logging
from collections import OrderedDict
from functools import partial
import newrelic.agent
from capa.xqueue_interface import XQueueInterface
from django.conf import settings
from django.contrib.auth.models import User
......@@ -82,6 +81,10 @@ from .field_overrides import OverrideFieldData
log = logging.getLogger(__name__)
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
if settings.XQUEUE_INTERFACE.get('basic_auth') is not None:
REQUESTS_AUTH = HTTPBasicAuth(*settings.XQUEUE_INTERFACE['basic_auth'])
......@@ -967,6 +970,7 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course
except InvalidKeyError:
raise Http404
if newrelic:
# Gather metrics for New Relic so we can slice data in New Relic Insights
newrelic.agent.add_custom_parameter('course_id', unicode(course_key))
newrelic.agent.add_custom_parameter('org', unicode(course_key.org))
......@@ -979,6 +983,7 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course
# "handler" in those cases is always just "xmodule_handler".
nr_tx_name = "{}.{}".format(instance.__class__.__name__, handler)
nr_tx_name += "/{}".format(suffix) if (suffix and handler == "xmodule_handler") else ""
if newrelic:
newrelic.agent.set_transaction_name(nr_tx_name, group="Python/XBlock/Handler")
tracking_context_name = 'module_callback_handler'
......
......@@ -19,7 +19,14 @@ from django.shortcuts import redirect
from courseware.url_helpers import get_redirect_url_for_global_staff
from edxmako.shortcuts import render_to_response, render_to_string
import logging
import newrelic.agent
log = logging.getLogger("edx.courseware.views.index")
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
import urllib
from lms.djangoapps.gating.api import get_entrance_exam_score_ratio, get_entrance_exam_usage_key
......@@ -55,7 +62,6 @@ from ..module_render import toc_for_course, get_module_for_descriptor
from .views import get_current_child, registered_for_course
log = logging.getLogger("edx.courseware.views.index")
TEMPLATE_IMPORTS = {'urllib': urllib}
CONTENT_DEPTH = 2
......@@ -175,6 +181,8 @@ class CoursewareIndex(View):
"""
Initialize metrics for New Relic so we can slice data in New Relic Insights
"""
if not newrelic:
return
newrelic.agent.add_custom_parameter('course_id', unicode(self.course_key))
newrelic.agent.add_custom_parameter('org', unicode(self.course_key.org))
......
......@@ -17,7 +17,13 @@ from django.shortcuts import render_to_response
from django.template.loader import render_to_string
from django.utils.translation import get_language_bidi
from django.views.decorators.http import require_GET
import newrelic.agent
log = logging.getLogger("edx.discussions")
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
from rest_framework import status
from web_fragments.fragment import Fragment
......@@ -51,13 +57,27 @@ from util.enterprise_helpers import data_sharing_consent_required
from opaque_keys.edx.keys import CourseKey
from contextlib import contextmanager
THREADS_PER_PAGE = 20
INLINE_THREADS_PER_PAGE = 20
PAGES_NEARBY_DELTA = 2
log = logging.getLogger("edx.discussions")
@newrelic.agent.function_trace()
@contextmanager
def newrelic_function_trace(function_name):
"""
A wrapper context manager newrelic.agent.FunctionTrace to no-op if the
newrelic package is not installed
"""
if newrelic:
nr_transaction = newrelic.agent.current_transaction()
with newrelic.agent.FunctionTrace(nr_transaction, function_name):
yield
else:
yield
def make_course_settings(course, user):
"""
Generate a JSON-serializable model for course settings, which will be used to initialize a
......@@ -72,7 +92,6 @@ def make_course_settings(course, user):
}
@newrelic.agent.function_trace()
def get_threads(request, course, user_info, discussion_id=None, per_page=THREADS_PER_PAGE):
"""
This may raise an appropriate subclass of cc.utils.CommentClientError
......@@ -186,7 +205,6 @@ def inline_discussion(request, course_key, discussion_id):
"""
Renders JSON for DiscussionModules
"""
nr_transaction = newrelic.agent.current_transaction()
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
cc_user = cc.User.from_django_user(request.user)
......@@ -197,12 +215,14 @@ def inline_discussion(request, course_key, discussion_id):
except ValueError:
return HttpResponseServerError("Invalid group_id")
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
with newrelic_function_trace("get_metadata_for_threads"):
annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)
is_staff = has_permission(request.user, 'openclose_thread', course.id)
threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
with newrelic_function_trace("add_courseware_context"):
add_courseware_context(threads, course, request.user)
return utils.JsonResponse({
'is_commentable_cohorted': is_commentable_cohorted(course_key, discussion_id),
'discussion_data': threads,
......@@ -222,8 +242,6 @@ def forum_form_discussion(request, course_key):
"""
Renders the main Discussion page, potentially filtered by a search query
"""
nr_transaction = newrelic.agent.current_transaction()
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
if request.is_ajax():
user = cc.User.from_django_user(request.user)
......@@ -238,10 +256,10 @@ def forum_form_discussion(request, course_key):
except ValueError:
return HttpResponseServerError("Invalid group_id")
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
with newrelic_function_trace("get_metadata_for_threads"):
annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
with newrelic_function_trace("add_courseware_context"):
add_courseware_context(threads, course, request.user)
return utils.JsonResponse({
......@@ -269,8 +287,6 @@ def single_thread(request, course_key, discussion_id, thread_id):
Depending on the HTTP headers, we'll adjust our response accordingly.
"""
nr_transaction = newrelic.agent.current_transaction()
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
if request.is_ajax():
......@@ -282,7 +298,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
if not thread:
raise Http404
with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"):
with newrelic_function_trace("get_annotated_content_infos"):
annotated_content_info = utils.get_annotated_content_infos(
course_key,
thread,
......@@ -291,7 +307,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
)
content = utils.prepare_content(thread.to_dict(), course_key, is_staff)
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
with newrelic_function_trace("add_courseware_context"):
add_courseware_context([content], course, request.user)
return utils.JsonResponse({
......@@ -376,7 +392,6 @@ def _create_discussion_board_context(request, course_key, discussion_id=None, th
"""
Returns the template context for rendering the discussion board.
"""
nr_transaction = newrelic.agent.current_transaction()
context = _create_base_discussion_view_context(request, course_key)
course = context['course']
course_settings = context['course_settings']
......@@ -405,13 +420,13 @@ def _create_discussion_board_context(request, course_key, discussion_id=None, th
is_staff = has_permission(user, 'openclose_thread', course.id)
threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
with newrelic_function_trace("get_metadata_for_threads"):
annotated_content_info = utils.get_metadata_for_threads(course_key, threads, user, user_info)
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
with newrelic_function_trace("add_courseware_context"):
add_courseware_context(threads, course, user)
with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
with newrelic_function_trace("get_cohort_info"):
user_cohort_id = get_cohort_id(user, course_key)
context.update({
......@@ -439,9 +454,6 @@ def user_profile(request, course_key, user_id):
Renders a response to display the user profile page (shown after clicking
on a post author's username).
"""
nr_transaction = newrelic.agent.current_transaction()
user = cc.User.from_django_user(request.user)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
......@@ -470,13 +482,13 @@ def user_profile(request, course_key, user_id):
query_params['page'] = page
query_params['num_pages'] = num_pages
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
with newrelic_function_trace("get_metadata_for_threads"):
user_info = cc.User.from_django_user(request.user).to_dict()
annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)
is_staff = has_permission(request.user, 'openclose_thread', course.id)
threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"):
with newrelic_function_trace("add_courseware_context"):
add_courseware_context(threads, course, request.user)
if request.is_ajax():
return utils.JsonResponse({
......@@ -490,7 +502,7 @@ def user_profile(request, course_key, user_id):
course_id=course.id
).order_by("name").values_list("name", flat=True).distinct()
with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"):
with newrelic_function_trace("get_cohort_info"):
user_cohort_id = get_cohort_id(request.user, course_key)
context = _create_base_discussion_view_context(request, course_key)
......@@ -518,9 +530,6 @@ def followed_threads(request, course_key, user_id):
"""
Ajax-only endpoint retrieving the threads followed by a specific user.
"""
nr_transaction = newrelic.agent.current_transaction()
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
try:
profiled_user = cc.User(id=user_id, course_id=course_key)
......@@ -561,7 +570,7 @@ def followed_threads(request, course_key, user_id):
query_params['num_pages'] = paginated_results.num_pages
user_info = cc.User.from_django_user(request.user).to_dict()
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
with newrelic_function_trace("get_metadata_for_threads"):
annotated_content_info = utils.get_metadata_for_threads(
course_key,
paginated_results.collection,
......
......@@ -8,7 +8,12 @@ from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db.utils import DatabaseError
from logging import getLogger
import newrelic.agent
log = getLogger(__name__)
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
from celery_utils.logged_task import LoggedTask
from celery_utils.persist_on_failure import PersistOnFailureTask
......@@ -29,8 +34,6 @@ from .new.subsection_grade import SubsectionGradeFactory
from .signals.signals import SUBSECTION_SCORE_CHANGED
from .transformer import GradesTransformer
log = getLogger(__name__)
class DatabaseNotReadyError(IOError):
"""
......@@ -99,6 +102,7 @@ def _recalculate_subsection_grade(self, **kwargs):
course_key = CourseLocator.from_string(kwargs['course_id'])
scored_block_usage_key = UsageKey.from_string(kwargs['usage_id']).replace(course_key=course_key)
if newrelic:
newrelic.agent.add_custom_parameter('course_id', unicode(course_key))
newrelic.agent.add_custom_parameter('usage_id', unicode(scored_block_usage_key))
......
......@@ -4,7 +4,11 @@ Middleware to serve assets.
import logging
import datetime
import newrelic.agent
log = logging.getLogger(__name__)
try:
import newrelic.agent
except ImportError:
newrelic = None # pylint: disable=invalid-name
from django.http import (
HttpResponse, HttpResponseNotModified, HttpResponseForbidden,
HttpResponseBadRequest, HttpResponseNotFound, HttpResponsePermanentRedirect)
......@@ -25,7 +29,6 @@ from .models import CourseAssetCacheTtlConfig, CdnUserAgentsConfig
# TODO: Soon as we have a reasonable way to serialize/deserialize AssetKeys, we need
# to change this file so instead of using course_id_partial, we're just using asset keys
log = logging.getLogger(__name__)
HTTP_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
......@@ -86,6 +89,7 @@ class StaticContentServer(object):
if safe_course_key.run is None:
safe_course_key = safe_course_key.replace(run='only')
if newrelic:
newrelic.agent.add_custom_parameter('course_id', safe_course_key)
newrelic.agent.add_custom_parameter('org', loc.org)
newrelic.agent.add_custom_parameter('contentserver.path', loc.path)
......@@ -153,6 +157,7 @@ class StaticContentServer(object):
response['Content-Length'] = str(last - first + 1)
response.status_code = 206 # Partial Content
if newrelic:
newrelic.agent.add_custom_parameter('contentserver.ranged', True)
else:
log.warning(
......@@ -165,6 +170,7 @@ class StaticContentServer(object):
response = HttpResponse(content.stream_data())
response['Content-Length'] = content.length
if newrelic:
newrelic.agent.add_custom_parameter('contentserver.content_len', content.length)
newrelic.agent.add_custom_parameter('contentserver.content_type', content.content_type)
......@@ -194,11 +200,13 @@ class StaticContentServer(object):
# indicate there should be no caching whatsoever.
cache_ttl = CourseAssetCacheTtlConfig.get_cache_ttl()
if cache_ttl > 0 and not is_locked:
if newrelic:
newrelic.agent.add_custom_parameter('contentserver.cacheable', True)
response['Expires'] = StaticContentServer.get_expiration_value(datetime.datetime.utcnow(), cache_ttl)
response['Cache-Control'] = "public, max-age={ttl}, s-maxage={ttl}".format(ttl=cache_ttl)
elif is_locked:
if newrelic:
newrelic.agent.add_custom_parameter('contentserver.cacheable', False)
response['Cache-Control'] = "private, no-cache, no-store"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment