Getting Started With SplunkUI

When developing new Splunk apps with a customised user interface, everything but SplunkUI is deprecated. Thus, it is only a matter of time before you need to jump from that building with faith.

Most Splunk users are not web developers. Developing web UI is known to be a nightmare, that’s why they chose to be Splunk users in the first place, ya know. The bad news is Splunk UI is a ReactJS based library.

Splunk users don’t have much time either. Developing Splunk apps should not require too much of it, and work out of sewing together SPL requests and blocks of code taken from dubious blog posts. The bad news is there’s not much to copy from that actually works.

Therefore, one would argue with the requirements selected by Splunk to serve their long-lived user base. But, as Splunk users, we don’t have time for this. In this blog post, we’ll provide a way to bootstart your Splunk UI journey by removing some of the frustration I faced.

Requirements

Let’s start by what you don’t want to hear. You need to undertake the ReactJS official tutorial. “You might be tempted to skip it because you’re not building games — but give it a chance”, they say.

You know what`? They’re damn right. But you don’t have time, so you skip it.

Splunk VM

Get the Splunk OVA from Splunkbase. If you plan on using the Dashboard framework (and you ought not), upscale to 20 GB RAM and 8 vCPUs.

Boot. Log on the Linux console as splunk / changeme and remember the new password you set, because you’ll need to type it every time you fire up your dev environment.

Enable the KV store if you plan on using it (it’s disabled out of the box). In/opt/splunk/etc/system/local/server.conf:

[kvstore]
disabled = false

Disable the web cache or you’re going to scratch your head against the wall, the fridge, and drown yourself in your barrel of beer. In /opt/splunk/etc/system/local/web.conf:

[settings]
js_no_cache = true
js_logger_mode = Firebug
cacheEntriesLimit = 0
cacheBytesLimit = 0

However, should you release your app on a cached server and don’t see your changes, you may wish to know about the _bump endpoint.

Yarn, the tool which will watch your changes and build the app into Splunk app folder, needs not worry about limits. Give it some freedom with:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl --system

Good, you now have a Splunk instance that will later be started with splunk start. Time to get serious. At the time of writing (2022-12), NodeJS 14 is required.

curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash -
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
sudo yum install yarn gcc-c++ make git

You haven’t just run that without checking what’s inside these files, have you?

If, at some point and for some reason, you’re debugging so deep you need a specific version of node, you might wish to know this:

yum --showduplicates list nodejs
sudo yum downgrade nodejs-14.18.3-1nodesource

Coding

No surprise, fetch Visual Studio Code and it’s add-on Remote-SSH.

Connect with Remote-SSH to the Splunk VM, as splunk. Work under /home/splunk and not /opt/splunk.

Remember you may spawn many terminals from VS Code, and any new process you start that binds a port will be routed to localhost. So if you can’t access your VM (say, it’s in the cloud and you don’t want to expose it), start splunk from these terminals and it’ll be reachable on localhost.

You may wish to clone an empty git repo here to ease future versioning instead of just creating a directory.

Then, you’re ready to undergo the Splunk App official tutorial (please note it’s built on the Tutorial I, so the commands won’t work out unless you did it). But you don’t have time for that, correct?

Get the Sample Up and Running

Create your app:

mkdir -p ~/Code/MyMagicApp&& cd ~/Code/MyMagicApp
npx @splunk/create
# select "splunk app with a react component", name it with the name your splunk app will have, MyMagicApp, doesn't support underscores]
yarn install
yarn run build
cd packages/magic-app
yarn run link:app
splunk start
npx @splunk/create partial output

If you input the same name in any of these 3 lines above, your project won’t build because of a cycling dependency.

You should see your new app in Splunk (default creds: admin / changeme). Or not.

If the build hangs, scale up your VM computing power.

What Now?

ReactJS core concept is that you design components, which render some HTML.

1: The magic-app folder contains the definition of your app. Splunk users will find familiar structure under src/main/resources/splunk. The index.jsx is going to be the default page. New pages can either be created manually as in other Splunk apps, or with npx.

2: magic-app’s index.jsx calls the magic-app-main component (see second screenshot below).

3: when built by yarn build, or whenever saved if you keep yarn start running, the resulting Splunk app is located under stage, and it’s this folder which is symlinked under /opt/splunk/etc/apps

Code of the main page

So, one is placing components as other HTML components. To give inputs, tag properties are used. To keep the internal state of a component, state is declared and maintained inside the component.

Note this: the state remains inside the component, it cannot be sent outside of it. It is used to render the component or pass properties to subcomponents. Any change of the state eventually rerenders the component automagically.

Note also this: properties are input parameters. They cannot be altered from inside the component. But any property change will eventually cause a rerender.

Reread both previous paragraphs. It’s the pain point in getting anything done with ReactJS.

If you think you need to get a piece of information flowing up the component chain, you did not understand React and should go through the official tutorial. It’s a mental shift. It’s painful. It’s unnatural. Whatever. Information flow only downwards, through properties (which, in turn, may affect state).

Side Note on Coding Style

You will encounter two coding styles, due to two generations of code. Class-looking components and functional looking component. They do the same thing : render some HTML code. But the way they manage properties and state differs. Class-looking components require more code and I found them more readable for a developer coming from another language. Functions are easier to write but hide even more javascript shit magic.

Mixing code styles should be possible, as components are autonomous. In my experience, it results in build errors. Mostly because, as Splunk user, I don’t have time.

About CSS

To embed CSS in your render function, make use of styled components. Just open the MagicAppMainStyles.js to get started, it’s fairly intuitive. Any class exported at the bottom needs to be imported in the component file, and enclose the content onto which the CSS should be applied.

The pain point is to understand what variable is available from the Splunk theme, but VS Code does a good autocomplete to try to figure it out.

About this

In JavaScript, I was told the content of this depends on the way the function is called. Hence the concept of binding. With Class-looking style, this could happen in the constructor:

constructor(props) {
        super(props);

        this.myFunction = this.myFunction.bind(this);

        this.state = {
            title: "",
            rows: [],
        };
    }

Or in the property itself in the render function (parameters may be added):

<myComponent onChange={() => this.doChange()}/>

Debug

Soon enough, this won’t work. The fastest way to debug is through your web browser development tools. To add a breakpoint in your code, just type debugger; save (yarn startwill rebuild your app on save) and refresh.

About Splunk Requests With Widgets

SplunkUI widgets require two common attributes: fields and columns. For instance with the Table component:

<Table key={this.props.id}
        dataSources={{
            primary: {
                requestParams: { offset: this.state.data.init_offset, count: 20 },
                data: {
                    fields: this.state.data.fields,
                    columns: this.state.data.columns,
                },
                meta: { totalCount: this.state.data.columns.length },
            },
        }}
    />

It is not the default output of Splunk to an API request, and until the official SplunkUI doc is updated, it’s good to know that there is an alternative output to get these two fields. Here’s an example of a fetch function with the proper output:

fetch() {
    const mySearchJob = SearchJob.create({
        search: this.props.search,
        earliest_time: this.props.earliest,
        latest_time: this.props.latest,
    }, {
        app: 'MyMagicApp', /* so it can access app unshared lookups */
    });

    const params = {
        output_mode: "json_cols" /* THE thing to know about */
    }

    mySearchJob.getResults(params).subscribe({
        next: response => {
            // Prepare results (will call async render())
            debugger; // So you see for yourself
            this.setState({ data: response });
        },
        error: err => {
            console.log("Caught error while running a search job");
            console.log(err);
        },
    });
}

Adapting Property Change

Let’s say you have a drop-down list whose content is tied to a Splunk search. And part of the search string comes from a property of your component. How do you rerun the Splunk search with the updated string when the property changes? In the example below, another component is a time picker that I need to be reflected in the search. It’s a method of the my component class (adapt when coding with function-like style):

componentDidUpdate(prevProps) {
    if (prevProps.earliest !== this.props.earliest ||
        prevProps.latest !== this.props.latest) {
        this.setState({ data: null});
        this.fetch();
    }
}

Regarding the Dashboard Framework

On Splunk Slack #splunkui channel, I was quickly told not to use the Dashboard Framework if I could do otherwise. But, hey? Why’s there a dashboard framework if not to be used? I just want to get a dynamic dashboard, I leverage that dashboard and here we go, don’t we?

In the end (8 dev days later), my app no longer leverages this dashboard. Because the resulting JS is > 1 MB and shrinking it requires some magic (explained in the Slack channel), but who’s got time for this?

In addition to the painfully big javascript, the grid layout (as of 2022-12) requires absolute coordinates. That is, you can’t define the grid size and just add() elements to it. And it’s tricky to manage the zoom level (see this answer). So if the coordinates needs be computed, better generate your own HTML.

TL;DR

Don’t make a Splunk app if you didn’t read through ;p This blog post recaps the most important findings during this first approach to this ReactJS framework. Many thanks to Splunk developers who answer in Slack, and let’s bet that, given time, it’ll become more accessible. Once mastered, Splunk UI indeed enables to create a customised experience within a couple of days.