Typically, applications are constructed from prebuilt modules distributed with Splunk. You can list modules available in your Splunk distribution by logging in to Splunkweb and entering http://<host>:<port>/en-US/modules. For example, http://localhost:8000/en-US/modules. This also provides basic documentation about how to use a module.
For your application domain, you may need to create a custom module to present your data in a more meaningful way. Modules are at the core of Splunk apps, where the UI view is constructed from a hierarchy of modules. Data returned from your search is passed between modules and handled, or rendered, according to module functionality. The following figure introduces you to the module components:
Modules are nested and search results are typically passed from parent to child modules for processing. The figure depicts a HiddenModule, because it has no UI, which initiates the search request in our recipes. Using the AJAX pattern, modules pend on search results generated by the module controller on the host. The module client templating mechanism renders the results.
Details
This recipe demonstrates the basic steps needed to create a module:
- Create the example source files in the correct directory.
- Define the module so it is recognized by the App Framework.
- On the server, handle the search request and return the requested data.
- On the client, render the response data.
- Integrate the module into the app view.
These steps are common to all modules, and demonstrated in this recipe using the CustomResultsTable module.
The CustomResultsTable module extracts CPU utilization data from the indexed metrics.log file using the following search string:
index=_internal source=*metrics.log group=pipeline |
stats sum(cpu_seconds) as totalCPU by name
and displays results in tabular format:
Creating custom modules is the foundation of most of the recipes in this cookbook.
Create the $SPLUNK_HOME/etc/apps/dev_tutorial/appserver/modules/CustomResultsTable directory and add the following files:
You can copy and paste the file contents from the sample code. Experiment with modifying the code to better understand how App Framework works.
Note: Remember that modules minimally require a .conf and a .js file.
[module] className = Splunk.Module.CustomResultsTable superClass = Splunk.Module.DispatchingModule description = This is my first custom module.
[param:myParam] required = False default = none
The configuration file hooks your module into the App Framework. The Splunk.Module.DispatchingModule abstract base class, itself a subclass of the root Splunk.Module abstract class, handles the attachment of jobProgress and jobDone events, such that the onJobProgress() and onJobDone() methods are only called when the job context actually changes.
Note: The description and parameter fields are used to auto-document your module, which you can view when you enter http://<host>:<port>/en-US/modules in Splunkweb.
Server-side code is implemented in the CustomResultsTable.py file.
Note: The root filename is the same as the unique className defined in CustomResultsTable.conf.
import controllers.module as module import splunk import splunk.search import splunk.util import splunk.entity import lib.util as util import lib.i18n as i18n import logging
Tip: It is the recommended practice to have each import statement on a separate line.
(See: PEP 8 -- Style Guide for Python Code)
logger = logging.getLogger('splunk.module.CustomResultsTable')
Messages are logged by calling logger.logLevel(logMessage), which appends logMessage to the $SPLUNK_HOME/var/log/splunk/web_service.log file.
Loggers are arranged in a namespace hierarchy so create your logger passing the module namespace, splunk.module.CustomResultsTable as a parameter.
class CustomResultsTable(module.ModuleHandler)
The ModuleHandler base class defines App Framework server-side extension points for handling module requests. Your module hooks into App Framework by implementing the methods for your particular application.
def generateResults(self, host_app, client_app, sid, count=1000,
offset=0, entity_name='results'):
Validate the parameters and set local variables.
count = max(int(count), 0)
offset = max(int(offset), 0)
if not sid:
raise Exception('CustomResultsTable.generateResults - sid not passed!')
try:
job = splunk.search.getJob(sid)
except splunk.ResourceNotFound, e:
logger.error('CustomResultsTable could not find job %s.' % sid)
return _('<p class="resultStatusMessage">Could not retrieve search data.</p>')
If the job ID parameter, sid, is not associated with your search request job, valid data are not available. In more advanced examples, you will learn how to work with job states.
Tip: Use standard Python logging to log errors.
The rest of this method builds the HTML displayed by this module, inserting the CPU Utilization search results in a table.
output = []
output.append('<div class="CustomResultsTableWrapper">')
output.append('<table class="CustomResultsTable splTable">')
fieldNames = [x for x in getattr(job, entity_name).fieldOrder if (not x.startswith('_'))]
dataset = getattr(job, entity_name)[offset: offset+count]
for i, result in enumerate(dataset):
output.append('<tr>')
for field in fieldNames:
output.append('<td')
fieldValues = result.get(cgi.escape(field), None)
if fieldValues:
output.append('>%s</td>' % fieldValues)
else:
output.append('></td>')
output.append('</tr>')
output.append('</table></div>')
output = ''.join(output)
return output
The fieldNames variable contains the names of the fields specified in the search request (the pipeline indexer stage name and cpu_seconds utilization), which are used to index the field value.
The method returns an HTML-formatted data table populated with CPU utilization data.
Tip: To avoid security vulnerabilities, make sure to use HTML escaping of your data, either on the client or server, as applicable. Escaping is conveniently specified on the server using the Mako template but, because this example does not have a template, manually escape the HTML by calling cgi.escape(<field>).
Client-side code is implemented in the CustomResultsTable.js file.
Note: The root filename is the same as the unique className defined in CustomResultsTable.conf.
Splunk.Module.CustomResultsTable = $.klass(Splunk.Module.DispatchingModule, {...}
The DispatchingModule base class, subclassing Splunk.Module, defines App Framework extension points for implementing client-side response data handling and rendering particular to your application.
Tip: Splunk provides a number of modules with predefined functionality. Choose the module from which to inherit most appropriate for your module functionality. Splunk modules are located at $SPLUNK_HOME/share/splunk/search_mrsparkle/modules. All modules have Splunk.Module as the base class.
initialize: function($super, container) {
$super(container);
this.resultsContainer = this.container;
},
All modules must have an initialize() method.
Call $super(), first, to guarantee that the base class constructor is called before the module initialize() method executes. Save the container where results are rendered with this object.
onJobDone: function(event) {
this.getResults();
},
This method is called when the search command is completed for the job associated with this instance. The only action needed for this simple example is to request the search results. For more complex applications, you might consider using other job progress event handlers.
getResultParams: function($super) {
var params = $super();
var context = this.getContext();
var search = context.get("search");
var sid = search.job.getSearchId();
if (!sid) this.logger.error(this.moduleType, "Assertion Failed.");
params.sid = sid;
return params;
},
renderResults: function($super, htmlFragment) {
if (!htmlFragment) {
this.resultsContainer.html('No content available.');
return;
}
this.resultsContainer.html(htmlFragment);
}
For this example, the HTML fragment constructed on the server can be rendered directly by calling resultsContainer.html(), passing the results as a parameter.
The following simplified sequence diagram shows the basic client-server interaction:

A search job is dispatched by the module with DispatchingModule binding job progress events to the module. The getResults() and response sequence is implemented as an AJAX pattern. Other apps may interact differently, responding to different job progress events or doing something other than render the data.
In the $SPLUNK_HOME/etc/apps/dev_tutorial/default/data/ui/views/Example4.xml file, add the following line in the view hierarchy where you want your module data rendered:
<module name="CustomResultsTable"></module>
In other examples, you will learn how to parameterize your module.
Getting Started with the App Framework
App Framework Developer Guide
Splunk.Job API
class Splunk.Module API
class Splunk.Module.DispatchingModule API
class ModuleHandler API
It might be helpful to review the following source code for a deeper understanding of how App Framework implements modules and job handling logic.
$SPLUNK_HOME/lib/Python2.x/site-packages/splunk/appserver/mrsparkle/lib/module.py
$SPLUNK_HOME/lib/Python2.x/site-packages/splunk/appserver/mrsparkle/controllers/view.py
$SPLUNK_HOME\Python-2.x\Lib\site-packages\splunk\appserver\mrsparkle\lib\module.py
$SPLUNK_HOME\Python-2.x\Lib\site-packages\splunk\appserver\mrsparkle\controllers\view.py
How to use JSON and do client-side rendering shows you how to transfer your data as JSON and format the display on the on the client.
How to include external libraries using templatesshows you how to import third-party graphics library for more advanced rendering.
How to parameterize your module shows you how to statically parameterize your module.
How to setup your app shows you how to configure your app at runtime.
How to parameterize your app shows you how to statically configure your app.