Merge pull request #11088 from edx/ormsbee/seq_new_relic_tagging
Add New Relic instrumentation to SequenceModule.
This commit is contained in:
@@ -3,7 +3,7 @@ xModule implementation of a learning sequence
|
||||
"""
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
import collections
|
||||
import json
|
||||
import logging
|
||||
from pkg_resources import resource_string
|
||||
@@ -13,6 +13,7 @@ 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
|
||||
@@ -191,6 +192,11 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
context["username"] = self.runtime.service(self, "user").get_current_user().opt_attrs['edx-platform.username']
|
||||
|
||||
display_names = [self.get_parent().display_name or '', self.display_name or '']
|
||||
|
||||
# We do this up here because proctored exam functionality could bypass
|
||||
# rendering after this section.
|
||||
self._capture_basic_metrics()
|
||||
|
||||
# Is this sequential part of a timed or proctored exam?
|
||||
if self.is_time_limited:
|
||||
view_html = self._time_limited_student_view(context)
|
||||
@@ -201,7 +207,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
fragment.add_content(view_html)
|
||||
return fragment
|
||||
|
||||
for child in self.get_display_items():
|
||||
display_items = self.get_display_items()
|
||||
for child in display_items:
|
||||
is_bookmarked = bookmarks_service.is_bookmarked(usage_key=child.scope_ids.usage_id)
|
||||
context["bookmarked"] = is_bookmarked
|
||||
|
||||
@@ -238,8 +245,78 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
|
||||
fragment.add_content(self.system.render_template("seq_module.html", params))
|
||||
|
||||
self._capture_full_seq_item_metrics(display_items)
|
||||
self._capture_current_unit_metrics(display_items)
|
||||
|
||||
# Get all descendant XBlock types and counts
|
||||
return fragment
|
||||
|
||||
def _locations_in_subtree(self, node):
|
||||
"""
|
||||
The usage keys for all descendants of an XBlock/XModule as a flat list.
|
||||
|
||||
Includes the location of the node passed in.
|
||||
"""
|
||||
stack = [node]
|
||||
locations = []
|
||||
|
||||
while stack:
|
||||
curr = stack.pop()
|
||||
locations.append(curr.location)
|
||||
if curr.has_children:
|
||||
stack.extend(curr.get_children())
|
||||
|
||||
return locations
|
||||
|
||||
def _capture_basic_metrics(self):
|
||||
"""
|
||||
Capture basic information about this sequence in New Relic.
|
||||
"""
|
||||
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)
|
||||
newrelic.agent.add_custom_parameter('seq.is_time_limited', self.is_time_limited)
|
||||
|
||||
def _capture_full_seq_item_metrics(self, display_items):
|
||||
"""
|
||||
Capture information about the number and types of XBlock content in
|
||||
the sequence as a whole. We send this information to New Relic so that
|
||||
we can do better performance analysis of courseware.
|
||||
"""
|
||||
# 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))
|
||||
|
||||
# Count of all modules (leaf nodes) in this sequence (e.g. videos,
|
||||
# problems, etc.) The units (verticals) themselves are not counted.
|
||||
all_item_keys = self._locations_in_subtree(self)
|
||||
newrelic.agent.add_custom_parameter('seq.num_items', len(all_item_keys))
|
||||
|
||||
# Count of all modules by block_type (e.g. "video": 2, "discussion": 4)
|
||||
block_counts = collections.Counter(usage_key.block_type for usage_key in all_item_keys)
|
||||
for block_type, count in block_counts.items():
|
||||
newrelic.agent.add_custom_parameter('seq.block_counts.{}'.format(block_type), count)
|
||||
|
||||
def _capture_current_unit_metrics(self, display_items):
|
||||
"""
|
||||
Capture information about the current selected Unit within the Sequence.
|
||||
"""
|
||||
# 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.
|
||||
if 1 <= self.position <= len(display_items):
|
||||
# Basic info about the Unit...
|
||||
current = display_items[self.position - 1]
|
||||
newrelic.agent.add_custom_parameter('seq.current.block_id', unicode(current.location))
|
||||
newrelic.agent.add_custom_parameter('seq.current.display_name', current.display_name or '')
|
||||
|
||||
# Examining all items inside the Unit (or split_test, conditional, etc.)
|
||||
child_locs = self._locations_in_subtree(current)
|
||||
newrelic.agent.add_custom_parameter('seq.current.num_items', len(child_locs))
|
||||
curr_block_counts = collections.Counter(usage_key.block_type for usage_key in child_locs)
|
||||
for block_type, count in curr_block_counts.items():
|
||||
newrelic.agent.add_custom_parameter('seq.current.block_counts.{}'.format(block_type), count)
|
||||
|
||||
def _time_limited_student_view(self, context):
|
||||
"""
|
||||
Delegated rendering of a student view when in a time
|
||||
|
||||
Reference in New Issue
Block a user