6. Add a third-party visualization

Now that we've covered the basics, it's time for the real fun. In this example, we'll create a view that uses a third-party visualization (a chart from Chart.js) to view our Splunk data.

We'll start by creating a Simple XML dashboard with an extension that implements a standard Splunk bar chart. Then, we'll replace the Splunk chart with the third-party Chart.js visualization. Finally, we'll convert that extension into a reusable custom view.

Before we start, you'll need to download a couple of things:

  • We'll use sample music data from a static lookup table in CSV format: create a /lookups folder under the customviewtutorial app folder, then download the musicdata.csv file to $SPLUNK_HOME/etc/apps/customviewtutorial/lookups.
  • Download the Chart.js library to $SPLUNK_HOME/etc/apps/customviewtutorial/appserver/static.
    Note  The version of Chart.js used for this tutorial is 1.0.2. Later versions of this library will not work.
  • Restart Splunk Enterprise to update the customviewtutorial app with the new files.

Let's start by using an extension to create a Simple XML dashboard with a search manager, a text input, and a chart.

  1. Update the HTML panel in the Simple XML dashboard. In Splunk Web, navigate to your dashboard, click Edit, and then click Source, then replace the Simple XML code with the following:
  2. <dashboard script="customview.js">
    
      <label>Custom View</label>
    
      <row>
        <html>
          <h4>How many artists do you want to show? (1-21)</h4>
          <div id="txtNum"></div>
    
          <h2>Downloads per Artist</h2>
          <div id="mychart"></div>
        </html>
      </row>
    
    </dashboard>
    
  3. Update the extension to instantiate these SplunkJS Stack components. Open customview.js and replace everything with the following code:
  4. /* customview.js */
    
    require([
        "splunkjs/mvc",
        "splunkjs/mvc/searchmanager",
        "splunkjs/mvc/chartview",
        "splunkjs/mvc/textinputview",
        "splunkjs/mvc/simplexml/ready!"
    ], function( 
        // Keep these variables in the same order as the libraries above:
        mvc, 
        SearchManager, 
        ChartView, 
        TextInputView
    ) {
    
        // Create a text input for the number of artists to show
        var myTextBox = new TextInputView({
            id: "txtNum",
            value: mvc.tokenSafe("$headNum$"),
            default: "10",
            el: $("#txtNum")
        }).render();
    
        // Downloads per artist
        var mySearch = new SearchManager({
            id: "mysearch",
            preview: true,
            cache: true,
            search: mvc.tokenSafe("| inputlookup musicdata.csv | search bc_uri=/sync/addtolibrary* | stats count by artist_name | table artist_name, count | head $headNum$"),
        });
    
        // Display the data in a Splunk chart
        var myChart = new ChartView ({
            id: "mychart",
            managerid: "mysearch",
            type: "bar",
            el: $("#mychart")
        }).render();
    
    });

    Here's what this code does:

    • The SplunkJS Stack components are imported. The MVC library is required because we want to work with tokens.
    • A new search displays the number of downloads per artist from the musicdata.csv lookup. The number of results (artists) to display is determined by the value in the text box. There are 21 artists in this sample data set, so the UI provides a range.
    • The text box input determines how many results to display, with a default of 10. This text box is bound to the search using a token variable. Whenever this value changes, the search is run again.
    • The Splunk chart is bound to the search.
  5. Save your changes, refresh Splunk Web (http://<localhost:port>/debug/refresh), then navigate to the dashboard (http://<localhost:port>/app/customviewtutorial/custom_view). Try entering different numbers in the text box to change the search.
  6. Splunk Chart view

     

    Now we'll replace the Splunk chart with a radar chart from Chart.js, using the artist names for our chart labels, and the download numbers as the chart data. Take a look at the Chart.js docs to get an idea of how to implement this chart.

    1. Update the HTML panel in the Simple XML dashboard to replace the Splunk chart div tag with a tag for the radar chart. In Splunk Web, navigate to your dashboard, click Edit > Edit Source, then replace the Simple XML code with the following:
    2.  
      <dashboard script="customview.js">
      
        <label>Custom View</label>
      
        <row>
          <html>
            <h4>How many artists do you want to show? (1-21)</h4>
            <div id="txtNum"></div>
      
            <h2>Downloads per Artist</h2>
            <canvas id="mychart" width="700" height="700"></canvas>
          </html>
        </row>
      
      </dashboard>
      
    3. Open customview.js. Replace the code with the following:
    4. /* customview.js */
      
      require([
          "/static/app/customviewtutorial/chart.js",
          "splunkjs/mvc",
          "underscore",
          "splunkjs/mvc/searchmanager",
          "splunkjs/mvc/textinputview",
          "splunkjs/mvc/simplexml/ready!"
      ], function( 
          // Keep these variables in the same order as the libraries above:
          chart,
          mvc,
          _,
          SearchManager, 
          TextInputView
      ) {
      
          // Create a text input for the number of artists to show
          var myTextBox = new TextInputView({
              id: "txtNum",
              value: mvc.tokenSafe("$headNum$"),
              default: "10",
              el: $("#txtNum")
          }).render();
      
          // Downloads per artist
          var mySearch = new SearchManager({
              id: "mysearch",
              preview: true,
              cache: true,
              search: mvc.tokenSafe("| inputlookup musicdata.csv | search bc_uri=/sync/addtolibrary* | stats count by artist_name | table artist_name, count | head $headNum$")
          });
      
          // Get the result data
          var myResults = mySearch.data("results");
          
          // When data changes...
          myResults.on("data", function() {
              // This is the data object (a wrapper) that contains the results
              console.log("Here is the data object: ", myResults.data());
      
              // This is the Backbone collection with the same results
              console.log("Here is the Backbone collection: ", myResults.collection());
              var chartLabels = [];
              var chartData = [];
              var maxDload = 0;
      
              // Get the number of rows and the results
              var numResults = myResults.data().rows.length; // Number of results
              var artistData = myResults.data().rows;        // The search results
      
              // Populate the chart (labels, data) with the search result data
              _.each(artistData, function(artistDatum, i) {
                  // Use artist names as labels
                  chartLabels[i] = artistDatum[0];
                  // Use the number of downloads as the dataset
                  chartData[i] = parseInt(artistDatum[1])+1;
                  // Determine what the top download number is to set the chart scale
                  if (chartData[i] > maxDload) {
                      maxDload = chartData[i];
                  }
              });
      
              // Set the radar chart data
              var radarChartData = {
                  labels: chartLabels,
                  datasets: [
                      {
                          fillColor: "rgba(151,187,205,0.5)",
                          strokeColor: "rgba(151,187,205,1)",
                          pointColor: "rgba(151,187,205,1)",
                          pointStrokeColor: "#fff",
                          data: chartData
                      }
                  ]
              }
      
              // Set radar chart options if different from the default
              var radarOptions = {
                  scaleOverride: true,
                  scaleSteps: maxDload, // Set chart scale to top number of downloads
                  scaleStepWidth: 1,
                  scaleStartValue: 0,
                  scaleShowLabels: true,
                  scaleShowLabelBackdrop: false,
              }
      
              // Create the radar chart
              var myRadar = new Chart(document.getElementById("mychart").getContext("2d")).Radar(radarChartData,radarOptions);
      
          });
      
      });

      Here's what the code does:

      • Loads the Chart.js library.
      • Loads the Underscore library.
      • Retrieves the results from the search. The example above shows how to access the results data using both the Splunk Web Framework's data object and the Backbone's collection object (you can use either one). If you're familiar with Backbone, you might prefer using the collection object to access the raw data.
      • Iterates through the results to create an array of artist names for the chart labels, and an array of download numbers for the chart data.
      • Displays the chart using the chart labels and data from the search.
    5. Save your changes, refresh Splunk Web (http://<localhost:port>/debug/refresh), then navigate to the dashboard (http://<localhost:port>/app/customviewtutorial/custom_view):
    6. Chart.js visualization

     

    So, this basic dashboard is acceptable as is. But what if we want to display the results from a different search on the same page using another radar chart? Or maybe we want to display multiple radar charts on a different dashboard in our app? We could add the JavaScript to multiple pages. But we could also solve this problem by creating a custom view, which already includes methods to work with search data.

    Let's take the JavaScript from this extension and place it into our DemoView class.

    1. Open demoview.js and replace the code with the following:
    2. /* demoview.js */
      
      define(function(require, exports, module){
          // Base class for custom views
          var SimpleSplunkView = require('splunkjs/mvc/simplesplunkview');
      
          // Require Underscore.js to work with search results
          var _ = require("underscore"); 
          
          // Require the Chart.js library for the radar chart
          var chart = require("/static/app/customviewtutorial/chart.js");
      
          // Define the custom view class
          var DemoView = SimpleSplunkView.extend({
      
              className: "demoview",
      
              // Define our initial values, set the type of results to return
              options: {
                  maxDload: 0,     // Chart scale
                  mychartid: "",   // ID for the chart
                  data: "results"  // Results type
              },
      
              // Override this method to configure the view
              createView: function() {
                  // Create a unique chart ID based on the view's unique ID
                  mychartid = this.name + "-radar";
                  this.$el.html('<canvas id="' + mychartid + '" width="700" height="700"></canvas>');
                  return this;
              },
      
              // Override this method to format the data for the radar chart
              formatData: function(data) {
      
                  // The data object contains the search results
                  var chartLabels=[];
                  var chartData=[];
                  maxDload = 0;
      
                  // Populate the chart (labels, data) with the search result data
                  _.each(data, function(row, i){
                      chartLabels[i] = row[0];
                      chartData[i] = parseInt(row[1]) + 1;
                      // Determine what the top download number is to set the chart scale
                      if (chartData[i] > maxDload) {
                          maxDload = chartData[i];
                      }
                  });
      
                  // Set the radar chart data
                  var radarChartData = {
                      labels: chartLabels,
                      datasets: [
                          {
                              fillColor: "rgba(151,187,205,0.5)",
                              strokeColor: "rgba(151,187,205,1)",
                              pointColor: "rgba(151,187,205,1)",
                              pointStrokeColor: "#fff",
                              data: chartData
                          }
                      ]
                  }
      
                  return radarChartData;
              },
      
              // Override this method to put the Splunk data into the view
              updateView: function(viz, radarData) {
                  // Set radar chart options
                  var radarOptions = {
                      scaleOverride: true,
                      scaleSteps: maxDload,
                      scaleStepWidth: 1,
                      scaleStartValue: 0,
                      scaleShowLabels: true,
                      scaleShowLabelBackdrop: false,
                  }
      
                  // Create the radar chart
                  var myRadar = new Chart(document.getElementById(mychartid).getContext("2d")).Radar(radarData, radarOptions);
              }
          });
      
          return DemoView;
      
      });

      The code does the same things it did in the customview.js extension—it loads the Chart.js library, takes the data from the search and formats it for the radar chart, and then displays the chart. But in this format, we break out the steps over the options dictionary and methods we want to override in SimpleSplunkView:

      • options: Defines initial variables.
      • createView: Defines how the view is displayed using the <canvas> tag. This also creates a unique chart ID because we can't use a hard-coded "mychart" ID, especially if we want to be able to place multiple views on a page.
      • formatData: Takes the results, then extracts the artist names for chart labels and the number of downloads for chart data. Some of the code is simpler here because this method provides more direct access to the search data model.
      • updateView: Sets the chart properties, then displays the radar chart in the view, using the data that is passed in from formatData and the view that is passed in from createView.
    3. Now that the JavaScript code has been relocated, we need to add the div tag for the view back to the dashboard. In Splunk Web, navigate to your dashboard, click Edit > Edit Source, then replace the Simple XML code with the following:
    4. <dashboard script="customview.js">
      
        <label>Custom View</label>
      
        <row>
          <html>
            <h4>How many artists do you want to show? (1-21)</h4>
            <div id="txtNum"></div>
      
            <h2>Downloads per Artist</h2>
            <div id="mycustomview"></div>
          </html>
        </row>
      
      </dashboard>
      
    5. And, we need to instantiate the DemoView class again in the extension. Open customview.js and replace the code with the following:
    6. /* customview.js */
      
      require([
          "splunkjs/mvc",
          "/static/app/customviewtutorial/demoview.js",
          "splunkjs/mvc/searchmanager",
          "splunkjs/mvc/textinputview",
          "splunkjs/mvc/simplexml/ready!"
      ], function( 
          // Keep these variables in the same order as the libraries above:
          mvc,
          DemoView,
          SearchManager, 
          TextInputView
      ) {
      
          // Create a text input for the number of artists to show
          var myTextBox = new TextInputView({
              id: "txtNum",
              value: mvc.tokenSafe("$headNum$"),
              default: "10",
              el: $("#txtNum")
          }).render();
      
          // Downloads per artist
          var mySearch = new SearchManager({
              id: "mysearch",
              preview: true,
              cache: true,
              search: mvc.tokenSafe("| inputlookup musicdata.csv | search bc_uri=/sync/addtolibrary* | stats count by artist_name | table artist_name, count | head $headNum$")
          });
      
          // Custom view
          var customView = new DemoView({
              id: "mycustomview",
              managerid: "mysearch",
              el: $("#mycustomview")
          }).render();
      
      });
    7. Save these changes, refresh Splunk Web (http://<localhost:port>/debug/refresh), then navigate to the dashboard (http://<localhost:port>/app/customviewtutorial/custom_view). It should look exactly the same as it did previously:
    8. Chart.js visualization

      But let's show how useful the reusable custom view is by adding another chart to the same dashboard to display a different search.

    9. Add another div tag to the dashboard for the additional view. In Splunk Web, navigate to your dashboard, click Edit > Edit Source, then replace the Simple XML code with the following:
    10.  
      <dashboard script="customview.js">
      
        <label>Custom View</label>
      
        <row>
          <html>
            <h4>How many artists do you want to show? (1-21)</h4>
            <div id="txtNum"></div>
      
            <h2>Downloads per Artist</h2>
            <div id="mycustomview1"></div>
      
            <h2>Searches on Artist</h2>
            <div id="mycustomview2"></div>
          </html>
        </row>
      
      </dashboard>
      
    11. Let's add another search to the extension, and instantiate another custom radar chart view. Open customview.js and replace everything with the following code:
    12. /* customview.js */
      
      require([
          "splunkjs/mvc",
          "/static/app/customviewtutorial/demoview.js",
          "splunkjs/mvc/searchmanager",
          "splunkjs/mvc/textinputview",
          "splunkjs/mvc/simplexml/ready!"
      ], function( 
          // Keep these variables in the same order as the libraries above:
          mvc,
          DemoView,
          SearchManager, 
          TextInputView
      ) {
      
          // Create a text input for the number of artists to show
          var myTextBox = new TextInputView({
              id: "txtNum",
              value: mvc.tokenSafe("$headNum$"),
              default: "10",
              el: $("#txtNum")
          }).render();
      
      
          // Set up the search managers
      
          // Downloads per artist
          var mySearch1 = new SearchManager({
              id: "mysearch1",
              preview: true,
              cache: true,
              search: mvc.tokenSafe("| inputlookup musicdata.csv | search bc_uri=/sync/addtolibrary* | stats count by artist_name | table artist_name, count | head $headNum$")
          });
      
          // Searches on artist
          var mySearch2 = new SearchManager({
              id: "mysearch2",
              cache: true,
              search: mvc.tokenSafe("| inputlookup musicdata.csv | search bc_uri=/browse/search* | stats count by search_terms | table search_terms, count | head $headNum$")
          });
      
      
          // Create the custom views for each search
      
          // Custom view showing search1
          var customView1 = new DemoView({
              id: "mycustomview1",
              managerid: "mysearch1",
              el: $("#mycustomview1")
          }).render();
      
          // Custom view showing search2
          var customView2 = new DemoView({
              id: "mycustomview2",
              managerid: "mysearch2",
              el: $("#mycustomview2")
          }).render();
      
      });

      In this example, we've added another custom view and search manager, and updated the IDs with a "1" and "2" to keep things tidy. The new search lists the number of searches per artist keyword, and since the results have the same format as the original search (a list of strings and numbers), we don't need to do anything to the way the data is formatted. This other search also is bound to the same text box that sets the number of results to return.

    13. Save these changes, refresh Splunk Web (http://<localhost:port>/debug/refresh), then navigate to the dashboard (http://<localhost:port>/app/customviewtutorial/custom_view) to see both radar charts:
    14. Multiple Chart.js visualizations