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 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 3. Place the finished app in the |
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.
- Rename the
viz_tutorial_app/appserver/static/visualizations/standin
directory toradial_meter
. - 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.
- From the
- 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
.
- Run
- Add other dependencies.
- a.) Run
$ npm install --save underscore
. - b.) Run
$ npm install --save jquery
.
- a.) Run
- In the
viz_tutorial_app/appserver/static/visualizations/radial_meter/src
directory, find thevisualization_source.js
file. Replace all of the code invisualization_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.
- This function is called whenever search results are updated or the visualization format changes. It handles visualization rendering using the following two parameters.
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.
- 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
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 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.
- In the
viz_tutorial_app/default
folder, find thevisualizations.conf
file. - Open the file and delete the entire
[standin]
stanza. - 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.
- In the
viz_tutorial_app/metadata
folder, find thedefault.meta
file. - Open the file and add the following content.
[visualizations/radial_meter] export = system
Note that the stanza name syntax isvisualizations/<visualization_folder_name>
. In this case, the visualization folder name isradial_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
- Build the visualization by running
$ npm run build
from the/radial_meter
directory. Notice that this generates the builtvisualization.js
file in the same directory. - From the Search and Reporting home page, run this search.
index=_internal | stats count
- Select the Visualizations tab.
- 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: .
- 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 aSplunkVisualizationBase.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 throughescapeHtml( )
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.
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.
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
- Locate the
viz_tutorial_app/appserver/static/visualizations/radial_meter/formatter.html
file. - 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 thesplunkCategorical
color palette if no palettetype
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 inconfig
.
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.
This documentation applies to the following versions of Splunk® Enterprise: 7.0.0, 7.0.1, 7.0.2, 7.0.3, 7.0.4, 7.0.5, 7.0.6, 7.0.7, 7.0.8, 7.0.9, 7.0.10, 7.0.11, 7.0.13, 7.1.0, 7.1.1, 7.1.2, 7.1.3, 7.1.4, 7.1.5, 7.1.6, 7.1.7, 7.1.8, 7.1.9, 7.1.10, 7.2.0, 7.2.1, 7.2.2, 7.2.3, 7.2.4, 7.2.5, 7.2.6, 7.2.7, 7.2.8, 7.2.9, 7.2.10, 7.3.0, 7.3.1, 7.3.2, 7.3.3, 7.3.4, 7.3.5, 8.0.0, 8.0.1, 8.0.2, 8.0.3, 8.0.4
Feedback submitted, thanks!