Commit dba12c09 by David Ormsbee

Merge pull request #11088 from edx/ormsbee/seq_new_relic_tagging

Add New Relic instrumentation to SequenceModule.
parents 4f185abc 02190715
......@@ -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
......
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