Skip to main content

How to query and validate Splunk logs in Service Suite

Overview

The splunk.js library provides a reusable integration layer between AIQ Service Suite and Splunk. It allows test scripts to query Splunk indexes using SPL (Search Processing Language), filter and retrieve log data, and assert on the results as part of a functional or end-to-end test validation flow.

Common use cases include:

  • Asserting that an audit log event was written after a user action (e.g. login, purchase, payment)
  • Validating specific field values in application logs (e.g. transaction status, HTTP response codes)
  • Checking that no ERROR or FATAL log entries appeared after a critical scenario
  • Waiting for async events to appear in Splunk before continuing a test
  • Extracting values from Splunk and storing them in TheLocker for downstream test cases

Prerequisite: The AIQ TestNode executing the script must have network access to the Splunk REST API on port 8089. Verify security group / firewall rules before using this library.


Setup

1. Add splunk.js to your repository

splunk.js file: splunk.js 

Upload splunk.js to your scenario repository. A recommended location is a shared folder so it can be reused across multiple scenarios:

your-repo/
  shared/
    splunk.js <-- place it here
  scenarios/
    checkout-flow/
      setup.js
      script1.js

2. Configure DPL variables

Never hardcode Splunk credentials in scripts. Add the following variables to your DPL (Data Provider Library) or scenario data file:

DPL Variable

Description

Example

SPLUNK_HOST

Hostname or IP of the Splunk instance

splunk.internal.mycompany.com

SPLUNK_TOKEN

Splunk service account Bearer token

eyJrIjoiT...

To generate a Bearer token in Splunk: Settings → Tokens → New Token. Use a dedicated service account, not a personal user.

Do not commit credentials to the repository. Always use DPL variables or TheLocker for sensitive values.

3. Include the library in your script

Use the Service Suite include directive at the top of any script that needs Splunk access. The {ds} token resolves to the root of your scenario's data source directory at runtime:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');


Initialization

Call splunk.init() once at the beginning of each script before making any queries:

splunk.init({
  host:            $SPLUNK_HOST,    // DPL variable
  port:            8089,
  token:           $SPLUNK_TOKEN,   // DPL variable
  defaultIndex:    'app_logs',      // used when no index is specified in SPL
  defaultEarliest: '-15m',          // default look-back window for all searches
});

Full init options

Option

Default

Description

host

localhost

Splunk hostname or IP

port

8089

Splunk REST API port

scheme

https

https or http

token

null

Bearer token (preferred auth method)

username

null

Basic auth username (alternative to token)

password

null

Basic auth password (alternative to token)

defaultIndex

main

Fallback Splunk index

defaultEarliest

-15m

Default time window start

defaultLatest

now

Default time window end

defaultCount

1000

Max rows returned per search

pollIntervalMs

2000

How often to poll for job completion (ms)

maxPollAttempts

30

Max poll attempts before timeout (30 × 2s = 60s)


How HTTP calls work internally

The library uses native Service Suite HTTP commands throughout. No browser-style fetch or external modules are used.

Operation

Service Suite command used

Submit search job

createPost(url, body, "application/x-www-form-urlencoded") + process(call)

Poll job status

createGet(url) + process(call)

Fetch results

createGet(url) + process(call)

Per-request auth header

call.addHeader("Authorization", "Bearer ...")

Read response body

resp.toStringContent()

Read HTTP status code

resp.status

Sleep between polls

java.lang.Thread.sleep(ms)

Base64 encode (Basic auth)

Java.type("java.util.Base64").getEncoder().encodeToString(...)


API Reference

Query methods

Method

Returns

Description

splunk.search(spl, opts)

Array

Run any SPL query and return all result rows

splunk.searchOne(spl, opts)

Object|null

Return only the first result row, or null if none

splunk.count(spl, opts)

Number

Return the number of matching events

splunk.getField(spl, field, opts)

String

Return a single field value from the first row

splunk.getFields(spl, fields[], opts)

Object

Return multiple field values from the first row

splunk.getAllValues(spl, field, opts)

Array

Return one field's value across all rows

splunk.waitForEvent(spl, opts)

Array

Poll until at least one matching event appears

All query methods accept an optional opts object: { earliest, latest, count }. waitForEvent also accepts pollMs and maxAttempts.

Assertion methods

Method

Description

splunk.assertCount(rows, n, msg)

Fails if row count ≠ n

splunk.assertAtLeast(rows, n, msg)

Fails if fewer than n rows returned

splunk.assertFieldEquals(rows, field, value, msg)

Fails if the field on row[0] ≠ expected value

splunk.assertFieldContains(rows, field, substring, msg)

Fails if the field on row[0] does not contain the substring

splunk.assertNoErrors(index, earliest, msg)

Fails if any ERROR or FATAL entries exist in the time window

splunk.assertEventOccurred(spl, msg, opts)

Combined search + assertAtLeast(1) in one call

Utility methods

Method

Description

splunk.storeResult(rows, field, lockerKey)

Store a field value from row[0] into TheLocker for downstream scripts via _retrieve(lockerKey)


Usage Examples

Example 1 — Assert a login audit event was written

After executing a login API call, verify that Splunk captured the corresponding audit entry:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:         $SPLUNK_HOST,
  token:        $SPLUNK_TOKEN,
  defaultIndex: 'audit',
});
var testUser = $TEST_USERNAME;
splunk.assertEventOccurred(
  'index=audit action=login userId="' + testUser + '" result=SUCCESS',
  'Login audit event must appear in Splunk after successful authentication',
  { earliest: '-5m' }
);
log('✓ Login audit event confirmed in Splunk');

Example 2 — Validate field values on a specific transaction

Retrieve a transaction log by its ID and assert that its status and currency fields are correct:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:  $SPLUNK_HOST,
  token: $SPLUNK_TOKEN,
});
var txnId = _retrieve('LAST_TXN_ID');  // value stored by an upstream test case
var rows = splunk.search(
  'index=payments txnId="' + txnId + '" | head 1',
  { earliest: '-10m' }
);
splunk.assertAtLeast(rows, 1, 'Transaction ' + txnId + ' must appear in Splunk');
splunk.assertFieldEquals(rows, 'status',   'APPROVED', 'Transaction must be APPROVED');
splunk.assertFieldEquals(rows, 'currency', 'USD',      'Currency must be USD');
log('✓ Transaction ' + txnId + ' validated — status=APPROVED, currency=USD');

Example 3 — No-error assertion after a critical flow

After running a checkout scenario, assert that no ERROR or FATAL entries were written to the application log index during the test window:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:         $SPLUNK_HOST,
  token:        $SPLUNK_TOKEN,
  defaultIndex: 'app_logs',
});
splunk.assertNoErrors('app_logs', '-10m',
  'No ERROR or FATAL log entries expected after checkout flow'
);
log('✓ No errors found in Splunk after checkout flow');

Example 4 — Wait for an async event

Some flows write to Splunk with a delay (e.g. payment processors, message queues). Use waitForEvent to poll rather than failing immediately:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:  $SPLUNK_HOST,
  token: $SPLUNK_TOKEN,
});
var txnId = _retrieve('LAST_TXN_ID');
// Poll every 3 seconds, up to 10 times (30s total)
var rows = splunk.waitForEvent(
  'index=payments event=PAYMENT_CONFIRMED txnId="' + txnId + '"',
  {
    earliest:    '-5m',
    pollMs:      3000,
    maxAttempts: 10
  }
);
splunk.assertFieldEquals(rows, 'processorCode', '00',
  'Processor must return approval code 00'
);
log('✓ Async payment confirmation found in Splunk');

Example 5 — Extract a value and store it in TheLocker

Retrieve a generated value from Splunk and make it available to downstream test cases:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:  $SPLUNK_HOST,
  token: $SPLUNK_TOKEN,
});
var testUser = $TEST_USERNAME;
var rows = splunk.search(
  'index=sessions userId="' + testUser + '" | sort -_time | head 1',
  { earliest: '-5m' }
);
splunk.storeResult(rows, 'sessionId', 'SPLUNK_SESSION_ID');
// Downstream scripts: var sid = _retrieve('SPLUNK_SESSION_ID');
log('✓ Session ID extracted from Splunk and stored in TheLocker');

Example 6 — SPL aggregation: validate counts across all hosts

Use a | stats pipe to check that no host served 5xx errors on a specific endpoint:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:  $SPLUNK_HOST,
  token: $SPLUNK_TOKEN,
});
var rows = splunk.search(
  'index=api endpoint="/checkout" | stats count(eval(status>=500)) as server_errors by host | where server_errors=0',
  { earliest: '-30m' }
);
log('✓ ' + rows.length + ' host(s) had zero 5xx errors on /checkout in the last 30 minutes');

Example 7 — getAllValues: check for unexpected HTTP status codes

Retrieve all HTTP status codes from recent API calls and fail if any 5xx appears:

/**
 * @aiq.servicesworkbench
 */
include('{ds}/shared/splunk.js');
splunk.init({
  host:  $SPLUNK_HOST,
  token: $SPLUNK_TOKEN,
});
var statuses = splunk.getAllValues(
  'index=api endpoint="/api/order" | head 200',
  'status',
  { earliest: '-5m' }
);
var badStatuses = statuses.filter(function(s) {
  return parseInt(s) >= 500;
});
assert(badStatuses.length === 0,
  'Found ' + badStatuses.length + ' 5xx response(s) on /api/order: ' + badStatuses.join(', ')
);
log('✓ All ' + statuses.length + ' /api/order responses were below 500');


Error handling

All library methods throw a descriptive error on failure. These surface directly in the AIQ test report with context including the HTTP status code, Splunk SID, field name, or assertion mismatch. No additional try/catch is required — let errors propagate so the scenario fails with a clear message.


Common issues

Symptom

Likely cause

Fix

HTTP 401 on job creation

Invalid or expired token

Regenerate the token in Splunk Settings → Tokens

Connection refused / timeout

TestNode cannot reach Splunk port 8089

Check EC2 security groups and firewall rules

assertAtLeast fails even though event exists

Log ingestion lag — event not yet indexed

Use splunk.waitForEvent() instead of splunk.search()

Search times out

Broad query or slow Splunk cluster

Narrow the SPL time window or add more filters; increase maxPollAttempts

Field not found error

Field name differs from what Splunk extracts

Run the same SPL in the Splunk UI and verify field names in the results table


Best practices

  • Always use DPL variables for SPLUNK_HOST and SPLUNK_TOKEN. Never hardcode connection details in scripts.
  • Keep time windows short (e.g. -5m or -10m) to keep searches fast and assertions specific to the current test run.
  • Use | head N in SPL to cap result sizes when you only need a few rows.
  • Prefer waitForEvent over a fixed sleep for async flows — it stops as soon as the event appears.
  • Use assertNoErrors as a blanket post-scenario health check in addition to specific field assertions.
  • Use storeResult to pass Splunk values into TheLocker rather than re-querying Splunk in every downstream script.
  • Call splunk.init() once per script at the top, before any other splunk.* call.