Splunk Cloud Platform

Developing Views and Apps for Splunk Web

Build a custom visualization

This tutorial introduces custom visualization app components and developer best practices.

For complete component information, see the Custom visualization API reference.

Tutorial overview

As an example project, the tutorial shows you how to build a radial meter visualization.

This is a gauge visualization showing a partially complete circle with a "count" caption and value. The circle changes color and appears less or more complete depending on the count value that the search returns.

This custom visualization uses the D3.js library to render the radial meter. The meter visualization represents a search result count value as a partial circle on a value range.


Getting started

Development mode settings

It is not necessary for the Splunk platform to be in development mode while building a custom visualization. However, development mode provides access to unbuilt file content and prevents caching. These options are helpful for debugging as well as for viewing changes as you make them.

You can enable development mode by adding the following settings to the web.conf configuration file in etc/system/local. If you do not already have a local copy of this file, create one and add these settings.

[settings]
minify_js = False
minify_css = False
js_no_cache = True
cacheEntriesLimit = 0
cacheBytesLimit = 0
enableWebDebug = True

Build the app

Start by setting up an app to contain the custom visualization. Use one of the following options.

App setup option What to do Notes
Download the app template

1. Download the custom visualization app template.

2. Unzip the template in the $SPLUNK_HOME/share/splunk/app_templates/ directory.

3. Place the finished app in the $SPLUNK_HOME/etc/apps directory.

The template contains the directory and file structure for the app.


Create the app manually

Use the directory and file structure shown below.

viz_tutorial_app is used as the app name in this tutorial.
Add the visualization to an existing app

Add the relevant visualization files to the app directory. Use the directory and file structure shown below.

viz_tutorial_app is used as the app name in this tutorial.

App directory and file structure

Here is the directory structure for an app that contains a custom visualization. The directory includes Webpack configuration files. Throughout this tutorial, viz_tutorial_app is used as the app name.

 
appname
    appserver
        static
            visualizations
                <visualization_name>
                    src
                        visualization_source.js
                    webpack.config.js
                    visualization.js
                    visualization.css
                    formatter.html
                    package.json
                    preview.png
    default
        visualizations.conf
        savedsearches.conf
    metadata
        default.meta
    README
        savedsearches.conf.spec

Visualization files in the app

There are four main files in a custom visualization.

File Description
visualization_source.js This file contains the source code for the custom visualization. You can edit the source code in this file. Webpack uses the source code file to build the visualization.js file.
visualization.js Built file for rendering the visualization.
formatter.html Contains HTML for rendering the visualization format menu. The format menu shows up on the Search page and in dashboards.
visualization.css Contains CSS style and behavior rules for the visualization. CSS rules should have names as specific to the visualization as possible.


Additional components for the tutorial visualization

The radial meter visualization uses the D3 library to render the meter. The tutorial shows you how to use the npm package manager to install D3 in the app package. Webpack then builds the D3 library into the visualization code.


Create the visualization logic

Set up the visualization source code

Create visualization source code using the custom visualizations app template.

  1. Rename the viz_tutorial_app/appserver/static/visualizations/standin directory to radial_meter.

  2. Install dependencies using npm package manager.
    From the viz_tutorial_app/appserver/static/visualizations/radial_meter directory, run $ npm install.
    Disregard any warning messages that might appear.
    This step generates a /node_modules sub-directory in this folder.
  3. Add D3 as a dependency.
    Run $ npm install --save d3@3.5.
    Observe that, for purposes of this tutorial, a specific version of D3 must be used.
    This step generates a D3 directory in /node_modules.
  4. Add other dependencies.
    a.) Run $ npm install --save underscore.
    b.) Run $ npm install --save jquery.
  5. In the viz_tutorial_app/appserver/static/visualizations/radial_meter/src directory, find the visualization_source.js file. Replace all of the code in visualization_source.js with the following code.


visualization_source.js code

define([
            'jquery',
            'underscore',
            'api/SplunkVisualizationBase',
            'api/SplunkVisualizationUtils',
            'd3'
        ],
        function(
            $,
            _,
            SplunkVisualizationBase,
            SplunkVisualizationUtils,
            d3
        ) {
  
    return SplunkVisualizationBase.extend({
 
        initialize: function() {
            // Save this.$el for convenience
            this.$el = $(this.el);
             
            // Add a css selector class
            this.$el.addClass('splunk-radial-meter');
        },
 
        getInitialDataParams: function() {
            return ({
                outputMode: SplunkVisualizationBase.ROW_MAJOR_OUTPUT_MODE,
                count: 10000
            });
        },
  
        updateView: function(data, config) {
          
            // Fill in this part in the next steps.
           
        }
    });
});


How visualization_source.js manages visualization logic

To manage visualization logic, visualization_source.js uses important custom visualization framework conventions.

It returns an object that extends the SplunkVisualizationBase class. As part of extending this class, visualization_source.js overrides two functions in SplunkVisualizationBase.

  • updateView
    This function is called whenever search results are updated or the visualization format changes. It handles visualization rendering using the following two parameters.
    • data. An object containing search result data.
    • config. An object containing visualization format information.
    The next part of the tutorial shows you how to complete the updateView function.
  • getInitialDataParams
    This function is required for data to be returned from the search. This function specifies the data output format for search results. You can also use getInitialDataParams to specify the maximum number of results.

Add the updateView function

updateView needs to check for data and render the visualization according to the specified configuration. Add the following code to fill in the function in visualization_source.js. Read the inline comments to learn more about each part of the function.

updateView code

        updateView: function(data, config) {
             
            // Guard for empty data
            if(data.rows.length < 1){
                return;
            }
            // Take the first data point
            datum = data.rows[0][0];
            // Clear the div
            this.$el.empty();
  
            // Pick a color for now
            var mainColor = 'yellow';
            // Set domain max
            var maxValue = 100;
  
            // Set height and width
            var height = 220;
            var width = 220;
  
            // Create a radial scale representing part of a circle
            var scale = d3.scale.linear()
                .domain([0, maxValue])
                .range([ - Math.PI * .75, Math.PI * .75])
                .clamp(true);
  
            // Create parameterized arc definition
            var arc = d3.svg.arc()
                .startAngle(function(d){
                    return scale(0);
                })
                .endAngle(function(d){
                    return scale(d)
                })
                .innerRadius(70)
                .outerRadius(85);
  
            // SVG setup
            var svg  = d3.select(this.el).append('svg')
                .attr('width', width)
                .attr('height', height)
                .style('background', 'white')
                .append('g')
                .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
  
            // Background arc
            svg.append('path')
                .datum(maxValue)
                .attr('d', arc)
                .style('fill', 'lightgray');
  
            // Fill arc
            svg.append('path')
                .datum(datum)
                .attr('d', arc)
                .style('fill', mainColor);
  
            // Text
            svg.append('text')
                .datum(datum)
                .attr('class', 'meter-center-text')
                .style('text-anchor', 'middle')
                .style('fill', mainColor)
                .text(function(d){
                    return parseFloat(d);
                })
                .attr('transform', 'translate(' + 0 + ',' + 20 + ')');
        }

Add data chunking to the updateView function

The Splunk custom visualization API only supports the return of 50,000 results at a time. If you're running searches that return over 50,000 results, you must use code that manages the data in chunks of 50,000 events at a time. To do this, add the following code in the updateView function above:

updateView: function(data, config) {
    // get data
    var dataRows = data.rows;

    // check for data
    if (!dataRows || dataRows.length === 0 || dataRows[0].length === 0) {
        return this;
    }

    // the rest of your updateView logic here

    // fetch the next chunk after processing the current chunk
    this.offset += dataRows.length;
    this.updateDataParams({count: this.chunk, offset: this.offset});
}

And add the following code to your initialize function:

initialize: function() {
    this.chunk = 50000;
    this.offset = 0;

    // the rest of your initialization logic here
}

To learn more about the initialize function, see: "Interface Methods" in the Custom visualization API reference section.


Add CSS

At this point, you can add CSS rules to manage the visualization appearance. Add the following rules to the visualization.css file.

/* Formatting for text element*/
.meter-center-text {
    font-size: 40px;
    font-weight: 200;
    font-family: "Helvetica Neue", Helvetica, sans-serif;
}
 
/* Center the main SVG in the page */
.splunk-radial-meter svg {
    display: block;
    margin: auto;
}

Add configuration settings

Register and export the visualization using configuration files in the app directory.

Register the visualization

Register the visualization to make it visible to the Splunk platform.

  1. In the viz_tutorial_app/default folder, find the visualizations.conf file.
  2. Open the file and delete the entire [standin] stanza.
  3. Add the following stanza to the file. Note that the stanza name must match the visualization folder name. In this case, it is radial_meter.
    [radial_meter]
    label = Radial Meter
    


Every visualization in an app must have a stanza in the visualizations.conf file. The stanza name and the label attribute are required but there are other optional settings. Here is the complete list of settings that the stanza can include.

Settings Description Required?
label Public label used throughout Splunk Web to refer to the visualization. Yes
default_height Default visualization height. No. Defaults to 250 if unspecified.
description Brief description for the visualization, appearing in Splunk Web. No
search_fragment Brief search portion to indicate how to structure results properly for the visualization. Used in Splunk Web. No
allow_user_selection Whether the visualization should be available for users to select in Splunk Web. No. Defaults to true, meaning the visualization is available.
disabled If set to 1, the visualization is not available anywhere in the Splunk platform. In this case, overrides a true setting for allow_user_selection. No. Defaults to 0 if unspecified, meaning that the visualization is available.



Export the visualization

By default, a custom visualization is only available within its own app context. Export the visualization to make it available globally, including to the Search and Reporting app. To export the custom visualization app, follow these steps.

  1. In the viz_tutorial_app/metadata folder, find the default.meta file.
  2. Open the file and add the following content.
    [visualizations/radial_meter]
    export = system
    
    Note that the stanza name syntax is visualizations/<visualization_folder_name>. In this case, the visualization folder name is radial_meter.



Try out the visualization

The essential visualization rendering code is in place. Now you can build the visualization and run a search.

Rebuild the visualization

Every time you update the source code in visualization_source.js, you must rebuild the visualization to see your changes in Splunk Web.

If developer mode is enabled, it is not necessary to restart the Splunk platform to see changes from the rebuilt visualization in Splunk Web.

Prerequisites
Ensure that the $SPLUNK_HOME environment variable is pointing to the Splunk installation folder. Use the command
echo $SPLUNK_HOME in a terminal window to verify that the Splunk installation folder path prints. If it does not, set it with this export command: export SPLUNK_HOME=/Applications/Splunk.

Steps

  1. Build the visualization by running $ npm run build from the /radial_meter directory. Notice that this generates the built visualization.js file in the same directory.
  2. From the Search and Reporting home page, run this search.
    index=_internal | stats count
  3. Select the Visualizations tab.
  4. Select the Visualization Picker at left to review the available visualizations. The Radial Meter visualization appears under More. Note that this tutorial does not include adding a visualization icon, so the Radial Meter visualization uses this generic icon: 6.4 mod viz generic icon.png.
  5. Select the Radial Meter visualization to see it render the search results.

Error handling

If you run a different search than the one shown here, results might not be formatted to generate the visualization properly. You might notice that there are no error messages to indicate this problem to a user. The next tutorial steps show you how to add data format error handling to the Radial Meter visualization.


Handle data format errors

To introduce some data format error handling, go back to visualization_source.js. The next steps show you how to override the formatData method that visualization_source.js inherits from SplunkVisualizationBase.

Override formatData

formatData gets a raw data object from splunkd and returns an object formatted for rendering. This object passes to updateView as its data argument. Add the following code to visualization_source.js to specify how formatData validates and processes raw data.

Make sure to add the new formatData code exactly as shown. The ... symbols indicate code already added.

getInitialDataParams: function() {
    ...
},
 
formatData: function(data, config) {
    
    // Check for an empty data object
    if(data.rows.length < 1){
        return false;
    }
    var datum = SplunkVisualizationUtils.escapeHtml(parseFloat(data.rows[0][0]));
 
    // Check for invalid data
    if(_.isNaN(datum)){
        throw new SplunkVisualizationBase.VisualizationError(
            'This meter only supports numbers'
        );
    }
     
    return datum;
},
 
updateView: function(data, config) {
        ...

}
 
   ...


Best practices for data validation and handling

The code just added to formatData does not do much formatting. However, it demonstrates important practices for data validation and error handling. Review these best practices when creating any custom visualization.

  • Check for empty data
    Whenever the search results change, formatData is called. Sometimes the results data object is empty and this case needs to be handled.
  • Check for invalid data
    Handle cases in which the visualization search does not generate data in the correct format for rendering. Check for the expected data format before trying to render the visualization. In this example, formatData checks for a number.
  • Throw helpful errors
    When a visualization throws a SplunkVisualizationBase.VisualizationError, users see the error in Splunk Web. Errors can provide information about what went wrong and help users to troubleshoot.
  • Sanitize values added to the DOM
    Any dynamic value that will be added to the DOM should be passed through escapeHtml( ) for security.

Change rendering on data format errors

In addition to these updates to formatData, change updateView so that it does nothing if formatData does not return anything to render.

Replace all of the code in updateView with the following code.

    updateView: function(data, config) {
             
        // Return if no data
        if (!data) {
            return;
        }
       
        // Assign datum to the data object returned from formatData
        var datum = data;

        // Clear the div
        this.$el.empty();
 
        // Pick a color for now
        var mainColor = 'yellow';
    
        // Set domain max
        var maxValue = 100;

        // Set height and width
        var height = 220;
        var width = 220;
  
        // Create a radial scale representing part of a circle
        var scale = d3.scale.linear()
            .domain([0, maxValue])
            .range([ - Math.PI * .75, Math.PI * .75])
            .clamp(true);
  
        // Create parameterized arc definition
        var arc = d3.svg.arc()
            .startAngle(function(d){
                return scale(0);
            })
            .endAngle(function(d){
                return scale(d)
            })
            .innerRadius(70)
            .outerRadius(85);

        // SVG setup
        var svg  = d3.select(this.el).append('svg')
            .attr('width', width)
            .attr('height', height)
            .style('background', 'white')
            .append('g')
            .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

        // Background arc
        svg.append('path')
            .datum(maxValue)
            .attr('d', arc)
            .style('fill', 'lightgray');

        // Fill arc
        svg.append('path')
            .datum(datum)
            .attr('d', arc)
            .style('fill', mainColor);

        // Text
        svg.append('text')
            .datum(datum)
            .attr('class', 'meter-center-text')
            .style('text-anchor', 'middle')
            .style('fill', mainColor)
            .text(function(d){
                return parseFloat(d);
            })
            .attr('transform', 'translate(' + 0 + ',' + 20 + ')');

        }

updateView now checks for null data and does not render in this case.

After these updates, if a search does not return numbers a useful error message appears.

Run $npm run build to rebuild the visualization.js file if you want to try out the visualization at this point.


Add user-configurable properties

Custom visualizations can include user-configurable properties and an interface for users to specify settings. This part of the tutorial shows you how to declare two configurable properties and handle property settings in visualization_source.js. These steps set up the visualization to work with a user interface.

Property namespacing

Property namespacing follows this syntax for configuration file declarations.

display.visualizations.custom.<app_name>.<visualization_ name>.<property_name>

When building the formatter.html user interface, developers can use a shortened namespace syntax for property references. This syntax option is shown later in this tutorial.


Property naming

In configuration files, property names refer to the specific part of the visualization that they affect. For example, use this name for the property that determines the main color of the radial meter.

display.visualizations.custom.viz_tutorial_app.radial_meter.mainColor

Use this name for the property determining the meter maximum count value.

display.visualizations.custom.viz_tutorial_app.radial_meter.maxValue

Declare property information

Once you determine names for the configurable properties, the next step is to declare the property names, types, and default values.

The radial meter app needs properties for its dial color and maximum value. Add the following property names and types to viz_tutorial_app/README/savedsearches.conf.spec.

display.visualizations.custom.viz_tutorial_app.radial_meter.mainColor = <string>
display.visualizations.custom.viz_tutorial_app.radial_meter.maxValue = <float>


Next, specify property default values by adding the following content to viz_tutorial_app/default/savedsearches.conf.

[default]
display.visualizations.custom.viz_tutorial_app.radial_meter.mainColor = #f7bc38
display.visualizations.custom.viz_tutorial_app.radial_meter.maxValue = 100


Now you can add code to visualization_source.js for handling property configurations.


Handle property settings

This part of the tutorial shows you how to use the following two properties to get user settings for the foreground color and the maximum radial meter value.

  • display.visualizations.custom.viz_tutorial_app.radial_meter.mainColor
  • display.visualizations.custom.viz_tutorial_app.radial_meter.maxValue

Start by changing updateView to check for the properties and use default values in case the properties are not set.

Replace these lines in updateView

 
    // Pick a color for now
    var mainColor = 'yellow'; 

    // Set domain max
    var maxValue = 100;

with this code.

updateView: function(data, config) {
        ...
 
        // Get color config or use a default yellow shade
        var mainColor = config[this.getPropertyNamespaceInfo().propertyNamespace + 'mainColor'] || '#f7bc38';
 
        // Set meter max value or use a default
        var maxValue = parseFloat(config[this.getPropertyNamespaceInfo().propertyNamespace + 'maxValue']) || 100;
        ...

After these updates, run $ npm run build from the /radial_meter directory to rebuild the visualization. At this point, the visualization is ready for a configuration user interface.


Implement a format menu

The code now checks for two visualization properties and handles user settings. The next step is to provide users with a user interface for setting these properties. The following steps show you how to define a format menu in HTML and using Splunk Web components. The menu appears in the Search page and in dashboard menus.


Define the format menu

The format menu defined here has two sections. The first one lets users specify a maximum meter value. The second section uses Splunk Web components to define a color picker for the meter foreground.


Steps

  1. Locate the viz_tutorial_app/appserver/static/visualizations/radial_meter/formatter.html file.

  2. Open the file and add the following content to it.
    <form class="splunk-formatter-section" section-label="Max value">
        <splunk-control-group label="Maximum dial value">
            <splunk-text-input name="{{VIZ_NAMESPACE}}.maxValue" value="100">
            </splunk-text-input>
        </splunk-control-group>
    </form>
    <form class="splunk-formatter-section" section-label="Dial color">
        <splunk-control-group label="Color">
            <splunk-color-picker name="{{VIZ_NAMESPACE}}.mainColor" value="#f7bc38">
            </splunk-color-picker>
        </splunk-control-group>
    </form>
    
    

Working with formatter.html

The code just added shows some important aspects of implementing a format menu.

  • Each menu section needs its own form.
  • For each section to render separately, make sure to assign each section the splunk-formatter-section class.
  • Each input element should be named according to the property that it impacts.
  • The splunk-color-picker component defaults to using the splunkCategorical color palette if no palette type attribute is specified.
  • When a user changes a setting in the format menu, the config dictionary gets the updated property value. updateView is called using the new values in config.

Additionally, the code just added demonstrates using the shortened property namespacing syntax available in formatter.html.

Formatter property namespacing
In formatter.html, you can refer to visualization properties using the following shortened namespace syntax.

{{VIZ_NAMESPACE}}.<property_name>

This shortened syntax is equivalent to the fully qualified name used to declare the property in savedsearches.conf.

display.visualizations.custom.<app_name>.<viz_name>.<property_name>

The following procedure for defining the format menu includes the shortened syntax.

Note: The shortened property name syntax is available only in the formatter.html file. Use the fully qualified property name in any other visualization app files.


For more information on implementing and configuring the format menu interface, see the Formatter API reference.


Conclusion

You now have a working example custom visualization. You can use the design patterns and best practices in this tutorial to build any custom visualization.

To learn more about creating custom visualizations, see the following topics.

Last modified on 21 May, 2020
API updates and migration advice   Custom visualization API reference

This documentation applies to the following versions of Splunk Cloud Platform: 9.3.2408, 8.2.2112, 8.2.2201, 8.2.2202, 8.2.2203, 9.0.2205, 9.0.2208, 9.0.2209, 9.0.2303, 9.0.2305, 9.1.2308, 9.1.2312, 9.2.2403, 9.2.2406 (latest FedRAMP release)


Was this topic useful?







You must be logged into splunk.com in order to post comments. Log in now.

Please try to keep this discussion focused on the content covered in this documentation topic. If you have a more general question about Splunk functionality or are experiencing a difficulty with Splunk, consider posting a question to Splunkbase Answers.

0 out of 1000 Characters