Metacog Tutorial 01: Logging Instrumentation

Target Audience

This tutorial is aimed to front-end developers who want to instrument an existing Widget with the Metacog Client Library, in order to send custom events to the Metacog Platform.

This Use Case, known as "Logging", is the easier one to implement, in comparision with the "Playback and Training" Use Case, and therefore, it is recommended for a first contact with the Metacog Platform.

The Chernoff Face Widget

Overview

Chernoff Faces is a technique for multidimensional data visualization, that maps values to modifications in the features of a stylished face.

In our sample Widget, called "Chernoff Face", you can manipulate a set of sliders in order to play with the look of the face. Let's try at once:

A few things to notice about this Widget:

  1. The "Snapshot" button currently does nothing. We expect to use that button to send a special event to the Metacog Platform (more on this later).
  2. This is an example of an open-ended activity. There are many ways to achieve the goal: quite the opposite to other widgets that implements some way of multiple-choice as the underlaying assesment mechanism, making easy to implement assesment, but ignoring the rich source of information that is the continous interaction of the Learner.

Our goal is to capture all the rich interactions between Learner and Widget. Therefore, we are going to focus in notify not only the click on the buttons, but also the dragging of each slider as well.

Source code of the widget can be found at this location: //github.com/metacog-com/MetacogSamples/tree/master/chernoff

Code Review

A first glance to the index.html file reveals that it depends on d3.min.js and chernoff.js. D3.js is a javascript graphic library capable of manipulate SVG nodes, and chernoff.js is a small third-party plugin that implements Chernoff faces on top of D3.

1
2
3
4
5
6
7
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
  <script type="text/javascript" src="js/d3.min.js"></script>
  <script type="text/javascript" src="js/chernoff.js"></script>
 

At the bottom of the index.html file, there is a reference to main.js. That is where all the logic of the widget resides:

1
2
3
4
</footer>
<script src="js/main.js"></script>
</body>
</html>

The main.js file is implemented following the Javascript Module Pattern: it uses a autocallable function to encapsulate data structures and private functions, and expose a set of methods in the Main object.

This is a reduced view of the file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var Main = (function () {

  /** update model, refresh view */
  Main.onChange = function(sliderid, value){...};
  
  /* restore values in the model and view */
  Main.reset = function(){...}

  /** initialize model and view, attach event handlers  */
  Main.init = function(){...}

  Main.init();
  return Main;
})();

Instrumentation strategy

Once we have a good understanding of the structure of the Widget, it is time to plan our instrumentation strategy. The Instrumentation guide Suggest to create a separate file for all the instrumentation code, and even it is not a hard constraint, we are going to stick to that as a good practice:

Changes in index.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<head>
<meta charset="UTF-8">
  <script type="text/javascript" src="js/d3.min.js"></script>
  <script type="text/javascript" src="js/chernoff.js"></script>
  <script type="text/javascript" src="//cdn.metacog.com/metalogger-3.2-0.js"></script>
..
</head>
<body>
..
<script src="js/main.js"></script>
<script src="js/instrumentation.js"></script>
</body>
</html>

In the index.html file we just need to include the Metacog Client Library at the top, and a new file called instrumentation.js (name of the file is not important, is just a place to put all metacog-related code).

Initialize Metacog Client Library

In order to initialize the Client Library, we need to provide the application_id and publisher_id that were provided when setting the Metacog account for your organization. In this tutorial we are going to use a pair of values provided for testing purpouses:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Metacog.init({
    "session": {
        "application_id": '76e98b0fea0b86e2fa074e76bd606dc3', // Api credentials: Application key
        "publisher_id": '61580c99',  // Api credentials: Application ID
        "widget_id": "chernoff_tutorial"
    },
    mode: "production"
}, function(){
 //post-initialization code here.. 
});

Another important parameter is the name of the widget. You can name it as you like. Finally, the production flag indicates that we desire to configure the client library to send messages to the Metacog platform for storage and future retrival.

Intercepting methods

The goal of instrumentation is to log events everytime that something interesting happens in the widget. Let's start with the Main.onChange method: it is triggered by dragging the sliders on the UI. There are many ways to intercept methods, but here we are going to use a utility function provided with the Metacog library: the Metacog.Logger.logMethod.

This function creates a wrapper around an existing function on any object available at the public scope. The Main object is public, so we can use it for interception. Just put the following code in your instrumentation.js file and drag the sliders to see the message being printed in the console:

1
2
3
4
5
6
7
Metacog.Logger.logMethod({
  targetMethodName: "onChange",
  targetObject: Main,
  preCallback: function(sliderid, value){
    console.log("Main.onChange is going to be called with params: "+ sliderid + ", " + value); 
  }
}); 

Logging events

It is time to replace the implementation of the callback method with real code. We have to declare a object to hold the information about the event, and also provide a valid name for the event. In this case, we choose the name of "feature_change" and we are sending both the sliderid and its new value:

1
2
3
4
5
6
7
8
     Metacog.send({
	    event: "feature_changed",
	    data: {
        id: sliderid,
        value: value
      },
	    type: Metacog.EVENT_TYPE.MODEL
    });

The final step is to start the Metacog Logger, so the queing-and-polling mechanism starts to run in background, and your events will be safe even in the scenary of transient network problems. A good place to locate the following line is at the done callback of the Metacog.init method:

1
Metacog.start();

You can verify that the events are being send to the Metacog Platform by inspecting the Network Panel of your browser.

Aditionally, you can try to enable the Metacog Logger Panel to be able to inspect the event traffic on top of your widget.

How about to do this as a bonus? (tip: you will need to include JQuery as a dependency, and pass log_tab: true as an additional parameter to the Metacog.init method call.)

Summary

The strategy to instrument a widget depends on the complexity of the widget's implementation. A desirable (but not mandatory) goal is to be able to keep isolated the instrumentation code from the original implementation.

Next tutorial will cover the more complex scenario of Playback and Training Support. Meanwhile, don't forget to have a look to the final version of the instrumentation.js file and to the instrumented widget:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Metacog.init({
    "session": {
        "application_id": '76e98b0fea0b86e2fa074e76bd606dc3', // Api credentials: Application key
        "publisher_id": '61580c99',  // Api credentials: Application ID
        "widget_id": "chernoff_tutorial"
    },
    mode: "production"
}, function(){
Metacog.start();
});

Metacog.Logger.logMethod({
  targetMethodName: "onChange",
  targetObject: Main,
  preCallback: function(sliderid, value){
     Metacog.send({
	    event: "feature_changed",
	    data: {
        id: sliderid,
        value: value
      },
	    type: Metacog.EVENT_TYPE.MODEL
    });
  }
}); 

Metacog.Logger.logMethod({
  targetMethodName: "reset",
  targetObject: Main,
  preCallback: function(){
     Metacog.send({
	    event: "reset",
	    data: {},
	    type: Metacog.EVENT_TYPE.MODEL
    });
  }
}); 

Instrumented Widget