UNPKG

go-jsbox-metrics-helper

Version:
425 lines (348 loc) 15.7 kB
var vumigo = require('vumigo_v02'); var events = vumigo.events; var Eventable = events.Eventable; var Q = require('q'); var BaseError = vumigo.utils.BaseError; var InvalidStateActionError = BaseError.extend(function(self, action) { self.name = "InvalidStateActionError"; self.message = "Invalid state action: " + action; }); var MetricsHelper = Eventable.extend(function(self, im) { /**class:MetricsHelper A helper for common metrics tasks. :param InteractionMachine im: The interaction machine that the metrics should be run on. */ self.im = im; self.add = {}; self.add.total_unique_users = function(label) { /**:MetricsHelper.add.total_unique_users(label) Adds inc and sum metrics that fires every time a new session is started with a new user. :param string label: The label for the metric. Defaults to ``unique_users`` */ label = typeof label !== 'undefined' ? label: 'unique_users'; self.im.on('user user:new', function(e) { return Q.all([ e.user.im.metrics.fire.inc(label, 1), e.user.im.metrics.fire.sum(label + '.transient', 1) ]); }); return self; }; self.add.total_sessions = function(label) { /**:MetricsHelper.add.total_sessions(label) Adds inc and sum metrics that fires every time a new session is started. :param string label: The label for the metric. Defaults to ``total_sessions`` */ label = typeof label !== 'undefined' ? label: 'total_sessions'; self.im.on('session:new', function(e) { return Q.all([ e.im.metrics.fire.inc(label, 1), e.im.metrics.fire.sum(label + '.transient', 1) ]); }); return self; }; self._validate_state_action = function(action) { /* Validates that ``action`` is a valid state action. Throws an exception if it is not a valid state action. */ formatted_action = (typeof action === 'undefined'?'enter':action) .match(/(enter|exit|input|resume|show)/i); try { return formatted_action[0].toLowerCase(); } catch(e) { throw new InvalidStateActionError(action); } }; self._create_metric_label = function(pre, action, state) { /* Creates a label ``$pre_$action_$state`` that is valid for a metric. */ return [pre, action, state.replace(/[^a-zA-Z0-9._]/g, '_')] .join('_').substring(0,100); }; self.add.total_state_actions = function(state, label) { /**:MetricsHelper.add.total_state_actions(state, label) Adds inc and sum metrics that fires every time the specified state action is triggered. :param object state: The name and action of the state to count actions for, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param string state.state: The name of the state to count the action for. Required. :param string state.action: The state action that should increment the count. Can be one of ``enter``, ``exit``, ``input``, ``resume``, and ``show``. Defaults to ``enter``. :param string label: The label for the metric. Defaults to ``total_action_$ACTION_$STATE`` where ``$ACTION`` is the specified action, and ``$STATE`` is the name of the state with all characters that are not ``a-zA-Z._`` replaced with ``_``. */ var action = self._validate_state_action(state.action); label = typeof label !== 'undefined' ? label : self._create_metric_label('total_action', action, state.state); self.im.on('state:' + action, function(e) { if(e.state.name === state.state) { return Q.all([ e.state.im.metrics.fire.inc(label, 1), e.state.im.metrics.fire.sum(label + '.transient', 1) ]); } }); return self; }; self._increment_metadata = function(user, label) { // Increments the specified metadata value var metadata = user.metadata; if (typeof metadata[label] === 'undefined'){ metadata[label] = 0; } user.metadata[label] += 1; }; self._reset_metadata = function(user, label) { // Gets the specified metadata value, resets to zero, and returns value. // If the value does not exist, sets the value to 0 and returns 0. if (typeof user.metadata[label] === 'undefined'){ user.metadata[label] = 0; return 0; } var metadata_value = user.metadata[label]; user.metadata[label] = 0; return metadata_value; }; self._set_metadata = function(user, label, value) { user.metadata[label] = value; }; self.add.sessions_until_state = function(state, label) { /**:MetricsHelper.add.sessions_until_state(state, label) Adds an average metric that counts the amount of sessions taken to reach the specified state action. :param object state: The name and action of the state to count actions for, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param string state.state: The name of the state to count the action for. Required. :param string state.action: The state action that should increment the count. Can be one of ``enter``, ``exit``, ``input``, ``resume``, and ``show``. Defaults to ``enter``. :param string label: The label for the metric. Defaults to ``sessions_until_$ACTION_$STATE`` where ``$ACTION`` is the specified action, and ``$STATE`` is the name of the state with all characters that are not ``a-zA-Z._`` replaced with ``_``. */ var action = self._validate_state_action(state.action); label = typeof label !== 'undefined' ? label : self._create_metric_label('sessions_until', action, state.state); var metadata_label = 'sessions_until_state_metric_' + label; self.im.on('session:new', function(e) { /* Increment sessions counter */ self._increment_metadata(e.im.user, metadata_label); }); self.im.on('state:' + action, function(e) { /* Reset sessions counter and fire metric */ if(e.state.name === state.state) { var metadata_value = self._reset_metadata( e.state.im.user, metadata_label); return e.state.im.metrics.fire.avg(label, metadata_value); } }); return self; }; self.add.time_between_states = function(state_from, state_to, label) { /**:MetricsHelper.add.time_between_states(state_from, state_to, label) Adds an average metric that measures the amount of time taken between two state events. Metric stored as milliseconds. :param object state_from: The state where timing should start. :param object state_to: The state where timing should end. :param object state_*: The name and action of the state, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param string state_*.state: The name of the state. Required. :param string state_*.action: The state action. Can be one of ``enter``, ``exit``, ``input``, ``resume``, and ``show``. Defaults to ``enter``. :param string label: The label for the metric. Defaults to ``time_between_$ACTIONFROM_$STATEFROM_$ACTIONTO_$STATETO`` where ``$ACTION_*`` is the specified action, and ``$STATE_*`` is the name of the state with all characters that are not ``a-zA-Z._`` replaced with ``_``. */ var from_action = self._validate_state_action(state_from.action); var to_action = self._validate_state_action(state_to.action); label = typeof label !== 'undefined' ? label : self._create_metric_label( 'time_between', from_action, state_from.state) + self._create_metric_label('', to_action, state_to.state); var metadata_label = 'time_between_states_metric_' + label; self.im.on('state:' + from_action, function(e) { // Set timestamp if(e.state.name === state_from.state) { self._set_metadata( e.state.im.user, metadata_label, Date.now()); } }); self.im.on('state:' + to_action, function(e) { // Fire metric with time difference if(e.state.name === state_to.state) { var time_from = self._reset_metadata( e.state.im.user, metadata_label); return e.state.im.metrics.fire.avg( label, Date.now() - time_from); } }); return self; }; self.add.sessions_between_states = function(state_from, state_to, label) { /**:MetricsHelper.add.sessions_between_states(state_from, state_to, label) Adds an average metric that measures the amount of sessions between two state events. The metric will fire with a value of 1 plus the amount of new sessions between the two state events. :param object state_from: The state where session counting should start. :param object state_to: The state where session counting should end. :param object state_*: The name and action of the state, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param string state_*.state: The name of the state. Required. :param string state_*.action: The state action. Can be one of ``enter``, ``exit``, ``input``, ``resume``, and ``show``. Defaults to ``enter``. :param string label: The label for the metric. Defaults to ``sessions_between_$ACTIONFROM_$STATEFROM_$ACTIONTO_$STATETO`` where ``$ACTION_*`` is the specified action, and ``$STATE_*`` is the name of the state with all characters that are not ``a-zA-Z._`` replaced with ``_``. */ var from_action = self._validate_state_action(state_from.action); var to_action = self._validate_state_action(state_to.action); label = typeof label !== 'undefined' ? label : self._create_metric_label( 'sessions_between', from_action, state_from.state) + self._create_metric_label('', to_action, state_to.state); var metadata_label = 'sessions_between_states_metric_' + label; self.im.on('state:' + from_action, function(e) { /* Set counter to one on state_from */ if(e.state.name === state_from.state) { self._set_metadata( e.state.im.user, metadata_label, 1); } }); self.im.on('session:new', function(e) { /* Increment sessions counter on new sessions */ self._increment_metadata(e.im.user, metadata_label); }); self.im.on('state:' + to_action, function(e) { /* Fire metric with number of sessions on state_to */ if(e.state.name === state_to.state) { var sessions = self._reset_metadata( e.state.im.user, metadata_label); return e.state.im.metrics.fire.avg(label, sessions); } }); return self; }; self.add.tracker = function(start_trigger, end_trigger, metrics) { /**:MetricsHelper.add.tracker(start_trigger, end_trigger, metrics) Allows the addition of multiple start/end metrics with one function. :param object start_trigger: The action that should trigger the start of the metric. :param object end_trigger: The action that should trigger the end of the metric. :param object \*_trigger: The name and action of the state, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param object metrics: The metrics and their labels that should be attached to the given triggers. Of the format ``metric_type: metric_label``. ``metric_type`` can be one of ``time_between_states``, ``sessions_between_states``. e.g. .. code-block:: javascript { time_between_states: 'time_between_a_and_b' } */ if(metrics.time_between_states) { self.add.time_between_states( start_trigger, end_trigger, metrics.time_between_states); } if(metrics.sessions_between_states) { self.add.sessions_between_states( start_trigger, end_trigger, metrics.sessions_between_states); } return self; }; self.add.trigger = function(trigger, metrics) { /**:MetricsHelper.add.trigger(trigger, metrics) Allows the addition of multiple metrics on one trigger event. :param object trigger: The name and action of the state to trigger on, e.g. .. code-block:: javascript { state: 'states:foo', action: 'exit' } :param object metrics: The metrics and their labels that should be attached to the given trigger. Of the format ``metric_type: metric_label``. ``metric_type`` can be one of ``sessions_until_state``, ``total_state_actions``. e.g. .. code-block:: javascript { sessions_until_state: 'sessions_until_foo', total_state_actions: 'total_bar_completions' } */ if(metrics.sessions_until_state) { self.add.sessions_until_state( trigger, metrics.sessions_until_state); } if(metrics.total_state_actions) { self.add.total_state_actions( trigger, metrics.total_state_actions); } return self; }; }); this.MetricsHelper = MetricsHelper;