Skip to content

PPL for SPL Users

This cheat sheet helps Splunk users transition to OpenSearch’s PPL. It maps common Splunk Search Processing Language (SPL) commands to their PPL equivalents with examples using OpenTelemetry observability data.

Try PPL in the live playground →
AspectSplunk SPLOpenSearch PPLNotes
Query structuresearch terms | commandsearch term source = index | commandPPL requires explicit source at the beginning
Index referenceindex=name*source=name*Different command to specify data source, PPL support referring to multiple indices
Raw fieldSpecial _raw fieldIdentify a field in your OpenSearch data that contains the text content you want to work with (often message or content fields in log data)Default field configured by the index.query.default_field setting (defaults to * which searches all fields)
Time fieldSpecial _time fieldUser-specified timestamp fieldPPL uses @timestamp by default

This table provides a mapping between Splunk SPL commands and their OpenSearch PPL equivalents:

Splunk SPLOpenSearch PPLPurpose
dedupdedupRemove duplicate results
evalevalCalculate and create new fields
eventstatseventstatsCalculate statistics while preserving events
mvexpandexpandExpand multi-value fields
fieldsfieldsInclude or exclude fields
fillnullfillnullReplace null values
headheadRetrieve the first N results
joinjoinCombine results from multiple sources
lookuplookupEnrich data with lookups
rarerareFind the least common values
renamerenameRename fields in results
rexrexExtract with regular expression pattern
searchsearchBasic searching of data
sortsortSort results by specified fields
spathspathExtracting fields from structured text data
statsstatsStatistical aggregation of data
timecharttimechartStatistical aggregation of time-series data
toptopFind the most common values
trendlinetrendlineCalculate moving averages of fields
wherewhereFilter results based on conditions

Simple search:

  • SPL: error failed
  • PPL: source=logs-otel-v1* error failed

Aggregation:

  • SPL: index=logs-otel-v1* | stats count by resource.attributes.service.name | sort -count
  • PPL: source=logs-otel-v1* | stats count by `resource.attributes.service.name` | sort -count

Time-based query:

  • SPL: index=logs-otel-v1* | stats count by span(@timestamp, 5m)
  • PPL: source=logs-otel-v1* | stats count by span(@timestamp, 5m)

Complex calculation:

  • SPL: index=logs-otel-v1* | eval severity_category=case(severityNumber >= 17, "high", 1==1, "low")
  • PPL: source=logs-otel-v1* | eval severity_category=case(severityNumber >= 17 then "high", else "low")

Search for log entries containing specific text terms.

SPL:

error failed

PPL:

source=logs-otel-v1* error failed
Try in playground →

Basic search results showing error logs with highlighted terms

Find logs with a specific severity level.

SPL:

index=logs-otel-v1* severityText="ERROR"

PPL:

source=logs-otel-v1* severityText="ERROR"
Try in playground →

Combine multiple conditions to find errors from a specific service.

SPL:

index=logs-otel-v1* resource.attributes.service.name="load-generator" AND severityText="ERROR"

PPL:

source=logs-otel-v1* `resource.attributes.service.name`="load-generator" AND severityText="ERROR"
Try in playground →

Find logs matching multiple severity levels using OR.

SPL:

index=logs-otel-v1* severityText="ERROR" OR severityText="WARN"

PPL:

source=logs-otel-v1* severityText="ERROR" OR severityText="WARN"
Try in playground →

Count the number of log entries grouped by service name.

SPL:

index=logs-otel-v1* | stats count by resource.attributes.service.name

PPL:

source=logs-otel-v1* | stats count by `resource.attributes.service.name`
Try in playground →

Count logs by service visualization showing bar chart of log counts grouped by service name

Calculate both count and average severity for each service.

SPL:

index=logs-otel-v1* | stats count as log_count, avg(severityNumber) as avg_severity by resource.attributes.service.name

PPL:

source=logs-otel-v1* | stats count as log_count, avg(severityNumber) as avg_severity by `resource.attributes.service.name`
Try in playground →

Sort the aggregated results by log count in descending order.

SPL:

index=logs-otel-v1* | stats count as log_count by resource.attributes.service.name | sort -log_count

PPL:

source=logs-otel-v1* | stats count as log_count by `resource.attributes.service.name` | sort -log_count
Try in playground →

Sort results by count visualization showing bar chart with log counts sorted in descending order

Create human-readable categories from numeric severity values.

SPL:

index=logs-otel-v1* | eval severity_category=case(severityNumber >= 17, "high", severityNumber >= 9, "medium", 1==1, "low")

PPL:

source=logs-otel-v1* | eval severity_category=case(severityNumber >= 17, "high", severityNumber >= 9, "medium" else "low")
Try in playground →

Return only specific fields in the results.

SPL:

index=logs-otel-v1* | fields @timestamp, resource.attributes.service.name, severityText

PPL:

source=logs-otel-v1* | fields @timestamp, `resource.attributes.service.name`, severityText
Try in playground →

Group logs into 5-minute time intervals and count by service.

SPL:

index=logs-otel-v1* | stats count by span(@timestamp, 5m) as time_bucket, resource.attributes.service.name

PPL:

source=logs-otel-v1* | stats count by span(@timestamp, 5m) as time_bucket, `resource.attributes.service.name`
Try in playground →

Create time-based charts showing log volume over time.

SPL:

index=logs-otel-v1* | timechart span=5m count by resource.attributes.service.name

PPL:

source=logs-otel-v1* | timechart span=5m count by `resource.attributes.service.name`
Try in playground →

Use pattern matching to find logs where the body field contains “error”.

SPL:

index=logs-otel-v1* | where like(body, "%error%")

PPL:

source=logs-otel-v1* | where like(body, "%error%")
Try in playground →

Combine service name and severity into a readable label.

SPL:

index=logs-otel-v1* | eval service_info=resource.attributes.service.name + " (" + severityText + ")"

PPL:

source=logs-otel-v1* | eval service_info=concat(`resource.attributes.service.name`, " (", severityText, ")")
Try in playground →

Get only one log entry per unique service name.

SPL:

index=logs-otel-v1* | dedup resource.attributes.service.name

PPL:

source=logs-otel-v1* | dedup `resource.attributes.service.name`
Try in playground →

Replace empty service names with a default value for cleaner reporting.

SPL:

index=logs-otel-v1* | fillnull value="unknown" resource.attributes.service.name

PPL:

source=logs-otel-v1* | fillnull with "unknown" in `resource.attributes.service.name`
Try in playground →