diff --git a/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py b/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py index 33fc264ff9..0521b32c94 100644 --- a/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py +++ b/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py @@ -11,6 +11,7 @@ from xmodule.vertical_module import VerticalModule, VerticalDescriptor from xblock.field_data import DictFieldData from xblock.fragment import Fragment from xblock.core import XBlock +from xblock.fields import ScopeIds from . import get_test_system @@ -216,6 +217,7 @@ class FakeChild(XBlock): self.student_view = Mock(return_value=Fragment(self.get_html())) self.save = Mock() self.id = 'i4x://this/is/a/fake/id' + self.scope_ids = ScopeIds('fake_user_id', 'fake_block_type', 'fake_definition_id', 'fake_usage_id') def get_html(self): """ diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 3845422e80..49d4789260 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -27,10 +27,13 @@ from xmodule.modulestore import Location from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError from xmodule.modulestore.locator import BlockUsageLocator from xmodule.exceptions import UndefinedContext +from dogapi import dog_stats_api log = logging.getLogger(__name__) +XMODULE_METRIC_NAME = 'edxapp.xmodule' + def dummy_track(_event_type, _event): pass @@ -926,7 +929,52 @@ def descriptor_global_local_resource_url(block, uri): # pylint: disable=invalid raise NotImplementedError("Applications must monkey-patch this function before using local_resource_url for studio_view") -class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method +class MetricsMixin(object): + """ + Mixin for adding metric logging for render and handle methods in the DescriptorSystem and ModuleSystem. + """ + + def render(self, block, view_name, context=None): + try: + status = "success" + return super(MetricsMixin, self).render(block, view_name, context=context) + + except: + status = "failure" + raise + + finally: + course_id = getattr(self, 'course_id', '') + dog_stats_api.increment(XMODULE_METRIC_NAME, tags=[ + u'view_name:{}'.format(view_name), + u'action:render', + u'action_status:{}'.format(status), + u'course_id:{}'.format(course_id), + u'block_type:{}'.format(block.scope_ids.block_type) + ]) + + def handle(self, block, handler_name, request, suffix=''): + handle = None + try: + status = "success" + return super(MetricsMixin, self).handle(block, handler_name, request, suffix=suffix) + + except: + status = "failure" + raise + + finally: + course_id = getattr(self, 'course_id', '') + dog_stats_api.increment(XMODULE_METRIC_NAME, tags=[ + u'handler_name:{}'.format(handler_name), + u'action:handle', + u'action_status:{}'.format(status), + u'course_id:{}'.format(course_id), + u'block_type:{}'.format(block.scope_ids.block_type) + ]) + + +class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method """ Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s """ @@ -1086,7 +1134,7 @@ class XMLParsingSystem(DescriptorSystem): self.process_xml = process_xml -class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method +class ModuleSystem(MetricsMixin,ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method """ This is an abstraction such that x_modules can function independent of the courseware (e.g. import into other types of courseware, LMS,