JfrPath is a path-based query language for navigating and querying Java Flight Recorder (JFR) files. It provides a concise syntax for accessing events, metadata, chunks, and constant pools with filtering, projection, and aggregation capabilities.
<query> ::= <root> "/" <segments> [<filters>] [<projection>] [<pipeline>]
<root> ::= "events" | "metadata" | "chunks" | "constants"
<segments> ::= <segment> ("/" <segment>)*
<segment> ::= <identifier> | <index> | <slice>
<filters> ::= "[" <predicate> "]" ("[" <predicate> "]")*
<predicate> ::= <simple-filter> | <expr-filter>
<projection>::= "/" <segment> ("/" <segment>)*
<pipeline> ::= "|" <operator> ("|" <operator>)*
JfrPath queries start with one of four roots:
Access event data from the recording.
- Requires: Event type name (e.g.,
jdk.ExecutionSample,jdk.FileRead) - Returns: Event instances with all fields
- Example:
events/jdk.FileRead
Access type metadata (structure, fields, annotations).
- Requires: Type name (e.g.,
java.lang.Thread,jdk.types.Method) - Returns: Type metadata as structured data
- Example:
metadata/jdk.types.StackTrace
Access chunk-level information from the recording.
- Returns: Chunk metadata (offset, size, duration, compression status)
- Example:
chunks - Specific chunk:
chunks/0(by index)
Access constant pool entries.
- Summary:
constantsreturns all CP types with counts - Entries:
constants/<type>returns entries for specific type - Crossref:
constants/<type> | crossref()crosschecks CP entries against event references - Example:
constants/jdk.types.Symbol
After the root, segments navigate through the structure:
events/jdk.FileRead/path
metadata/jdk.types.Method/name
constants/jdk.types.Symbol/string
events/jdk.ExecutionSample/thread/name
events/jdk.FileRead/stackTrace/frames
metadata/jdk.types.StackTrace/fields/name
events/jdk.ExecutionSample/stackTrace/frames/0 # First element
events/jdk.ExecutionSample/stackTrace/frames/0:3 # Slice [0,3)
Convenient shortcuts for metadata navigation:
metadata/jdk.types.Method/fields.name # Same as /fields/name
metadata/jdk.types.Method/fieldsByName.name # Access field by name
Filters use square brackets [...] to constrain results. Filters can be placed:
- After the root/type:
events/jdk.FileRead[bytes>1000] - After any segment (interleaved):
events/jdk.GCHeapSummary/heapSpace[committedSize>1000000]
Simple field comparisons:
[field op value]
Operators:
=: Equal!=: Not equal>: Greater than>=: Greater than or equal<: Less than<=: Less than or equal~: Regex match
Examples:
events/jdk.FileRead[bytes>1000]
events/jdk.ExecutionSample[thread/name="main"]
events/jdk.FileRead[path~"/tmp/.*"]
metadata/jdk.types.Method[name="toString"]
Complex conditions with functions and logic:
[<expr> and <expr>]
[<expr> or <expr>]
[not <expr>]
[(<expr>) and (<expr>)]
Examples:
events/jdk.FileRead[bytes>1000 and path~"/tmp/.*"]
events/jdk.ExecutionSample[thread/name="main" or thread/name="worker"]
events/jdk.FileRead[not (bytes<100)]
contains(field, "substr")- Check if string contains substringstartsWith(field, "prefix")- Check if string starts with prefixendsWith(field, "suffix")- Check if string ends with suffixmatches(field, "regex")- Check if string matches regexmatches(field, "regex", "i")- Case-insensitive regex match
Examples:
events/jdk.FileRead[contains(path, "tmp")]
events/jdk.ExecutionSample[startsWith(thread/name, "pool-")]
events/jdk.FileRead[endsWith(path, ".log")]
events/jdk.ExecutionSample[matches(thread/name, "worker-[0-9]+")]
exists(field)- Check if field is present and not nullempty(field)- Check if string/list is empty
Examples:
events/jdk.FileRead[exists(path)]
events/jdk.ExecutionSample[not empty(stackTrace/frames)]
between(field, min, max)- Check if value is between min and max (inclusive). Bounds can be numbers or datetime strings (see below)len(field)- Get length of string or list (can be used in comparisons)
Examples:
events/jdk.FileRead[between(bytes, 1000, 10000)]
events/jdk.ExecutionSample[len(stackTrace/frames)>10]
events/jdk.FileRead[len(path)>50]
Filter events by timestamp. All time fields (startTime) store epoch nanoseconds.
Datetime strings are parsed in order: ISO-8601 instant → local datetime → date (start of day in local timezone).
before(field, datetime)- Event occurred before the given datetimeafter(field, datetime)- Event occurred after the given datetimebetween(field, "datetime1", "datetime2")- Event occurred within a datetime range (inclusive)on(field, "yyyy-MM-dd")- Event occurred on a specific calendar date (local timezone)
Supported datetime formats:
- ISO-8601 instant:
"2024-08-13T16:24:00Z"or"2024-08-13T16:24:00+02:00" - Local datetime:
"2024-08-13T16:24:00"(interpreted in system timezone) - Date only:
"2024-08-13"(treated as start of day in system timezone)
Examples:
# Events before a point in time
events/jdk.ExecutionSample[before(startTime, "2024-08-13T17:00:00")]
# Events after a point in time
events/jdk.ExecutionSample[after(startTime, "2024-08-13T16:00:00")]
# Events within a time window
events/jdk.ExecutionSample[between(startTime, "2024-08-13T16:00:00", "2024-08-13T17:00:00")]
# Events on a specific date
events/jdk.ExecutionSample[on(startTime, "2024-08-13")]
# Combine with other filters
events/jdk.JavaMonitorEnter[on(startTime, "2024-08-13") and duration>1000000]
For list and array fields, control how filters apply to elements:
any:(default) - Filter matches if ANY element satisfies conditionall:- Filter matches if ALL elements satisfy conditionnone:- Filter matches if NO elements satisfy condition
Syntax:
[any:<listField>[condition]]
[all:<listField>[condition]]
[none:<listField>[condition]]
Examples:
events/jdk.ExecutionSample[any:stackTrace/frames[matches(method/name/string, ".*Foo.*")]]
events/jdk.ExecutionSample[all:stackTrace/frames[lineNumber>0]]
events/jdk.ExecutionSample[none:stackTrace/frames[matches(method/name/string, ".*Test.*")]]
Filters can be placed after any segment to filter at that level:
events/jdk.GCHeapSummary[when/when="After GC"]/heapSpace[committedSize>1000000]/reservedSize
This filters:
- Events where
when/when="After GC" - Then projects to
heapSpace - Then filters heapSpace entries where
committedSize>1000000 - Then projects to
reservedSize
Pipeline operators transform or aggregate results. Append with |:
events/jdk.FileRead | count()
events/jdk.FileRead/bytes | stats()
| count()
Count the number of rows/events.
Returns: { "count": N }
Examples:
events/jdk.FileRead | count()
metadata/jdk.types.Method/name | count()
constants/jdk.types.Symbol | count()
| sum([path])
Sum numeric values. If path is omitted, uses the projection path.
Returns: { "sum": total, "count": N }
Examples:
events/jdk.FileRead/bytes | sum()
events/jdk.FileRead | sum(bytes)
| stats([path])
Compute statistics for numeric values: min, max, avg, stddev.
Returns: { "min": M, "max": N, "avg": A, "stddev": S, "count": C }
Examples:
events/jdk.FileRead/bytes | stats()
events/jdk.FileRead | stats(bytes)
| quantiles(q1, q2, ...[, path=...])
Compute percentiles at specified quantiles (0.0 to 1.0).
Returns: Columns pXX for each quantile (e.g., p50, p90, p99)
Examples:
events/jdk.FileRead/bytes | quantiles(0.5, 0.9, 0.99)
events/jdk.FileRead | quantiles(0.5, 0.9, path=bytes)
| sketch([path])
Shortcut for stats + common percentiles (p50, p90, p99).
Returns: { "min": M, "max": N, "avg": A, "stddev": S, "p50": X, "p90": Y, "p99": Z, "count": C }
Examples:
events/jdk.FileRead/bytes | sketch()
events/jdk.FileRead | sketch(bytes)
| head(n)
Return only the first N rows from the result set.
Examples:
events/jdk.FileRead | head(10)
events/jdk.ExecutionSample | groupBy(thread/name) | head(5)
| tail(n)
Return only the last N rows from the result set.
Examples:
events/jdk.FileRead | tail(10)
events/jdk.FileRead | sortBy(bytes) | tail(5)
| filter([predicate])
Filter rows in the pipeline using the same predicate syntax as path filters.
Examples:
events/jdk.FileRead | groupBy(path, agg=sum, value=bytes) | filter([sum>1048576])
events/jdk.ExecutionSample | groupBy(thread/name) | filter([count>100])
| distinct([field])
Return only distinct rows by the specified field, or deduplicate the entire row if no field is given.
Examples:
events/jdk.FileRead | distinct(path)
events/jdk.ExecutionSample | distinct(sampledThread/javaName)
| stackprofile([direction=top-down|bottom-up][, buckets=N][, minPct=D])
Aggregate stack trace data into a weighted call tree with temporal bucketing. Intended for CPU profiling event types with a stackTrace field (e.g., jdk.ExecutionSample).
Parameters:
direction-top-down(default) orbottom-upbuckets- number of time buckets (default: 10)minPct- minimum percentage threshold to include a frame (default: 1.0)
Examples:
events/jdk.ExecutionSample | stackprofile()
events/jdk.ExecutionSample | stackprofile(direction=bottom-up, buckets=20)
events/jdk.ExecutionSample[sampledThread/javaName="main"] | stackprofile(minPct=0.5)
| flamegraph([direction=bottom-up|top-down])
Aggregate stack trace data into a flame graph. Intended for event types with a stackTrace field (e.g., jdk.ExecutionSample). Renders an interactive HTML flame graph in the terminal (inline ANSI in plain mode, scrollable pane in --tui mode).
Parameters:
direction-bottom-up(default, classic flame graph) ortop-down(icicle graph)
Examples:
events/jdk.ExecutionSample | flamegraph()
events/jdk.ExecutionSample | flamegraph(direction=top-down)
events/jdk.ExecutionSample[sampledThread/javaName="main"] | flamegraph()
| groupBy(keyPath[, agg=count|sum|avg|min|max, value=path, sortBy=key|value, asc=false])
Group results by key and apply aggregation function with optional sorting.
Parameters:
keyPath- Field path to group byagg- Aggregation function (default:count)value- Value path for sum/avg/min/maxsortBy- Sort results bykey(grouping key) orvalue(aggregated value)asc- Sort ascending (default:false, descending)
Returns: { "key": groupKey, "<agg>": result }
Examples:
events/jdk.ExecutionSample/thread/name | groupBy(value) # Count by thread name
events/jdk.FileRead | groupBy(path, agg=count) # Count by file path
events/jdk.FileRead | groupBy(path, agg=sum, value=bytes) # Total bytes by path
events/jdk.ExecutionSample | groupBy(thread/name, agg=count) # Count by thread name
events/jdk.FileRead | groupBy(path, agg=avg, value=bytes) # Avg bytes by path
events/jdk.FileRead | groupBy(path, agg=min, value=bytes) # Min bytes by path
events/jdk.FileRead | groupBy(path, agg=max, value=bytes) # Max bytes by path
# Sorted results
events/jdk.ExecutionSample | groupBy(thread/name, sortBy=value) # Sort by count descending
events/jdk.ExecutionSample | groupBy(thread/name, sortBy=key, asc=true) # Sort alphabetically
events/jdk.FileRead | groupBy(path, agg=sum, value=bytes, sortBy=value) # Sort by total bytes
| sortBy(field[, asc=false])
Sort rows by any field in the current result set. Works after any operator that produces multiple rows.
Parameters:
field- Field name to sort by (must exist in current row structure)asc- Sort ascending (default:false, descending)
Key constraint: Can only sort by fields available after previous operators:
- After
select(a, b)→ onlya,bavailable - After
groupBy(x)→ onlykey,<aggFunc>available - After
len(path)→ all original fields +len
Examples:
events/jdk.FileRead | select(path, bytes) | sortBy(bytes) # Sort by bytes descending
events/jdk.FileRead | select(path, bytes) | sortBy(path, asc=true) # Sort by path ascending
events/jdk.ExecutionSample | groupBy(thread/name) | sortBy(count) # Sort grouped results by count
events/jdk.FileRead | len(path) | sortBy(len) # Sort by computed length
| top(n[, by=path, asc=false])
Sort and return top N rows.
Parameters:
n- Number of results to returnby- Path to sort by (default:value)asc- Sort ascending (default:false, descending)
Examples:
events/jdk.FileRead | top(10, by=bytes) # Top 10 by bytes (descending)
events/jdk.FileRead/bytes | top(5) # Top 5 values
events/jdk.FileRead | top(10, by=bytes, asc=true) # Bottom 10 by bytes (ascending)
| decorateByTime(decoratorEventType, fields=field1,field2[, threadPath=..., decoratorThreadPath=...])
Decorate events with information from time-overlapping events on the same thread.
Use Cases: Correlate events that occur during the same time period (e.g., execution samples during monitor waits, allocations during GC phases).
Parameters:
decoratorEventType- Event type to use as decorator (quoted string)fields- Comma-separated list of fields to extract from decoratorthreadPath- Optional: path to thread ID in primary event (default:eventThread/javaThreadId)decoratorThreadPath- Optional: path to thread ID in decorator (default:eventThread/javaThreadId)
Overlap Logic: Events overlap if their time ranges intersect:
primaryStart < decoratorEnd && primaryEnd > decoratorStart
Decorator Field Access: Decorated fields are accessed using $decorator. prefix:
$decorator.fieldName
Examples:
# Find execution samples during monitor waits
events/jdk.ExecutionSample | decorateByTime(jdk.JavaMonitorWait, fields=monitorClass,duration)
# Correlate allocations with GC phases
events/jdk.ObjectAllocationSample | decorateByTime(jdk.GCPhase, fields=name,duration)
# Execution samples during I/O operations
events/jdk.ExecutionSample | decorateByTime(jdk.SocketWrite, fields=host,port,bytesWritten)
# Access decorator fields
events/jdk.ExecutionSample | decorateByTime(jdk.JavaMonitorEnter, fields=monitorClass)
| groupBy($decorator.monitorClass)
| decorateByKey(decoratorEventType, key=keyPath, decoratorKey=decoratorKeyPath, fields=field1,field2)
Decorate events using correlation keys derived from event fields.
Use Cases: Join events with shared identifiers (e.g., request tracing, correlation by thread or custom IDs).
Parameters:
decoratorEventType- Event type to use as decorator (quoted string)key- Path to correlation key in primary eventdecoratorKey- Path to correlation key in decorator eventfields- Comma-separated list of fields to extract from decorator
Decorator Field Access: Decorated fields are accessed using $decorator. prefix:
$decorator.fieldName
Examples:
# Correlate execution samples with request context by thread ID
events/jdk.ExecutionSample | decorateByKey(RequestStart,
key=sampledThread/javaThreadId,
decoratorKey=thread/javaThreadId,
fields=requestId,endpoint,userId)
# Join file reads with thread metadata
events/jdk.FileRead | decorateByKey(jdk.ThreadStart,
key=eventThread/javaThreadId,
decoratorKey=thread/javaThreadId,
fields=javaName,group)
# Group execution samples by request endpoint
events/jdk.ExecutionSample | decorateByKey(RequestStart,
key=sampledThread/javaThreadId,
decoratorKey=thread/javaThreadId,
fields=endpoint)
| groupBy($decorator.endpoint)
# Access decorator fields in filters
events/jdk.ExecutionSample | decorateByKey(RequestStart,
key=sampledThread/javaThreadId,
decoratorKey=thread/javaThreadId,
fields=endpoint,requestId)
| top(10, by=$decorator.requestId)
Notes:
- If no decorator matches,
$decorator.fields will benull - Multiple decorators matching the same event: first match is used
- Only requested fields from decorator are accessible
- Memory-efficient: decorator fields are lazily accessed
| select(field1, field2, expr1 as alias1, ...)
Project specific fields from events, filtering out all other fields. Supports both simple field paths and computed expressions.
Use Cases: Reduce output to only relevant fields, compute derived values, transform data, control JSON output structure, focus analysis on specific attributes.
Parameters:
field- Simple field path to include in outputfield as alias- Field path with custom output nameexpression as alias- Computed expression (requires alias)- Supports nested fields using
/syntax (e.g.,eventThread/javaThreadId)
Behavior:
- Only specified fields/expressions are included in the output
- Simple fields use leaf segment as column name (or alias if provided)
- Expressions require
as aliasclause - Works with filters and other query operations
- Expressions are evaluated per row
Simple Field Selection:
# Select single field
events/jdk.ExecutionSample | select(startTime)
# Select multiple top-level fields
events/jdk.FileRead | select(path, bytes)
# Select nested fields
events/jdk.ExecutionSample | select(eventThread/javaThreadId, eventThread/name)
# Field with alias
events/jdk.ExecutionSample | select(eventThread/javaThreadId as threadId)
# Combine with filters
events/jdk.FileRead[bytes>1000] | select(path, bytes)
# Select decorator fields
events/jdk.ExecutionSample | decorateByTime(jdk.JavaMonitorWait, fields=monitorClass)
| select(startTime, $decorator.monitorClass)
Computed Expressions:
Expressions support arithmetic operations, string concatenation, and built-in functions.
Arithmetic Operators:
+- Addition or string concatenation-- Subtraction*- Multiplication/- Division
# Convert bytes to kilobytes
events/jdk.FileRead | select(bytes / 1024 as kilobytes)
# Calculate throughput
events/jdk.FileRead | select(bytes / duration as bytesPerNs)
# Multiply duration by 1000
events/jdk.FileRead | select(duration * 1000 as micros)
# Complex arithmetic
events/jdk.FileRead | select((bytes * count) / 1024 as totalKb)
String Concatenation:
# Build descriptive string
events/jdk.FileRead | select(path + ' (' + bytes + ' bytes)' as description)
# Combine fields
events/jdk.ExecutionSample | select(thread/name + ' [' + thread/javaThreadId + ']' as threadInfo)
# Format output
events/jdk.FileRead | select('File: ' + path as label)
String Templates:
String templates provide a cleaner syntax for string interpolation using ${...} embedded expressions:
# Simple field interpolation
events/jdk.FileRead | select("File: ${path}" as description)
# Multiple expressions in one template
events/jdk.FileRead | select("${path} (${bytes} bytes)" as info)
# Arithmetic in templates
events/jdk.FileRead | select("${path}: ${bytes / 1024} KB" as summary)
# Functions in templates
events/jdk.FileRead | select("File: ${upper(path)} - ${bytes} bytes" as info)
# Nested fields in templates
events/jdk.ExecutionSample | select("Thread ${eventThread/name} (ID: ${eventThread/javaThreadId})" as threadInfo)
# Mix templates with regular fields
events/jdk.FileRead | select(path, "${bytes / 1024} KB" as sizeKb)
Templates are equivalent to string concatenation but more readable:
"${path} (${bytes} bytes)"is the same aspath + ' (' + bytes + ' bytes)'- Use double quotes for templates:
"${expr}" - Any expression can be embedded in
${...} - Null values render as empty strings
Built-in Functions:
if(condition, trueValue, falseValue) - Conditional expression
events/jdk.FileRead | select(if(bytes, 'large', 'small') as size)
events/jdk.FileRead | select(if(duration, 'slow', 'fast') as speed)
upper(string) - Convert to uppercase
events/jdk.FileRead | select(upper(path) as upperPath)
events/jdk.ExecutionSample | select(upper(thread/name) as threadName)
lower(string) - Convert to lowercase
events/jdk.FileRead | select(lower(path) as lowerPath)
substring(string, start[, length]) - Extract substring
events/jdk.FileRead | select(substring(path, 0, 20) as shortPath)
events/jdk.FileRead | select(substring(path, 5) as pathTail)
events/jdk.FileRead | select(substring(path, 0, 10) as prefix)
length(string) - Get string length
events/jdk.FileRead | select(length(path) as pathLength)
events/jdk.ExecutionSample | select(length(thread/name) as nameLen)
coalesce(value1, value2, ...) - Return first non-null value
events/jdk.FileRead | select(coalesce(path, altPath, 'unknown') as finalPath)
events/jdk.ExecutionSample | select(coalesce(thread/name, thread/osName, 'unnamed') as name)
asDateTime(epochNanos[, format]) - Format epoch-nanoseconds as datetime string
events/jdk.ExecutionSample | select(asDateTime(startTime) as time)
events/jdk.FileRead | select(asDateTime(startTime, "HH:mm:ss.SSS") as time, path)
truncate(epochNanos, unit) - Truncate timestamp to a time boundary, returns epoch nanoseconds
# Units: "second", "minute", "hour", "day", "week", "month"
events/jdk.ExecutionSample | select(asDateTime(truncate(startTime, "minute")) as bucket)
events/jdk.ExecutionSample
| select(asDateTime(truncate(startTime, "minute")) as bucket, eventThread/name as thread)
| groupBy(bucket, agg=count)
formatDuration(nanos) - Format nanosecond duration as human-readable string. Also available as a pipeline operator: | formatDuration([path])
events/jdk.JavaMonitorEnter | select(startTime, formatDuration(duration) as dur, monitorClass)
events/jdk.FileRead | select(path, formatDuration(duration) as dur) | sortBy(duration) | top(10)
events/jdk.JavaMonitorEnter/duration | formatDuration()
Mixed Fields and Expressions:
# Simple fields with computed expressions
events/jdk.FileRead | select(path, bytes / 1024 as kb, duration)
# Multiple expressions and fields
events/jdk.FileRead | select(
path as file,
bytes / 1024 as kilobytes,
if(bytes, 'large', 'small') as sizeCategory,
duration * 1000 as microseconds
)
# Transform and compute
events/jdk.ExecutionSample | select(
startTime,
upper(thread/name) as threadName,
thread/javaThreadId as tid,
length(stackTrace/frames) as stackDepth
)
Expression Evaluation:
- Field references in expressions access the current row's data
- Arithmetic operations convert values to numbers (null becomes 0)
- String concatenation converts all values to strings
- Division by zero returns NaN
- Functions receive evaluated arguments
Column Naming:
- Simple fields: Use leaf segment name (e.g.,
javaThreadIdfromeventThread/javaThreadId) - Aliased fields: Use the specified alias
- Expressions: Must provide alias (e.g.,
bytes / 1024 as kb)
Transform individual values (can also be used in filters where applicable):
| len([path])- String or list length| uppercase([path])- Convert to uppercase| lowercase([path])- Convert to lowercase| trim([path])- Trim whitespace| contains([path], "substr")- Check if contains substring| replace([path], "old", "new")- Replace occurrences
| abs([path])- Absolute value| round([path])- Round to nearest integer| floor([path])- Round down| ceil([path])- Round up| formatDuration([path])- Format nanosecond value as human-readable duration string
Examples:
constants/jdk.types.Symbol/string | len()
events/jdk.ExecutionSample/thread/name | uppercase()
events/jdk.FileRead/path | replace("/tmp/", "/data/")
events/jdk.FileRead/bytes | abs()
events/jdk.JavaMonitorEnter/duration | formatDuration()
| timerange([path], [duration=path], [format=str])
Computes the min/max time span covered by events. Returns a single-row summary with human-readable wall-clock times and duration.
Parameters:
path- Time field to analyze (default:startTime)duration- Duration field; if present, max is computed asstartTime + durationformat- Output format string forminTime/maxTime(default: ISO local datetime)
Output fields: count, field, minEpochNanos, maxEpochNanos, minTime, maxTime, durationNanos, durationMs, duration
Examples:
# Recording time span
events/jdk.ExecutionSample | timerange()
# Span of file reads including their duration
events/jdk.FileRead | timerange(startTime, duration=duration)
# Custom output format
events/jdk.ExecutionSample | timerange(format="HH:mm:ss")
| asDateTime([path][, format=str])
Converts epoch-nanosecond fields to human-readable datetime strings. Can be used both as a pipeline operator and as a select() expression function.
Parameters:
path- Time field to format (default:startTime)format- Date format string (default: ISO local datetime)
Examples:
# Format startTime (pipeline operator)
events/jdk.ExecutionSample | asDateTime(startTime)
# Format as select expression
events/jdk.ExecutionSample | select(asDateTime(startTime) as time, eventThread/name as thread)
# Custom format
events/jdk.ExecutionSample | select(asDateTime(startTime, "HH:mm:ss.SSS") as time)
truncate(field, unit) is a select() expression function that truncates an epoch-nanoseconds value to a time boundary, enabling time-series groupby.
Supported units: "second", "minute", "hour", "day", "week", "month"
Returns epoch nanoseconds of the truncated instant (chains naturally with asDateTime()).
Examples:
# Count samples per minute
events/jdk.ExecutionSample
| select(asDateTime(truncate(startTime, "minute")) as bucket, eventThread/name as thread)
| groupBy(bucket, agg=count)
# Allocations per hour
events/jdk.ObjectAllocationSample
| select(truncate(startTime, "hour") as hourBucket, allocationSize)
| groupBy(hourBucket, agg=sum, value=allocationSize)
formatDuration(field) is a select() expression function that converts a nanosecond duration to a human-readable string.
Output examples: "123ns", "4.56us", "789.12ms", "1.23s", "5m 30s", "2h 15m 30s"
Examples:
# Show lock wait durations in readable form
events/jdk.JavaMonitorEnter | select(startTime, formatDuration(duration) as dur, monitorClass)
# Top slowest file reads
events/jdk.FileRead | select(path, formatDuration(duration) as dur) | sortBy(duration) | top(10)
List all events of a type:
events/jdk.FileRead
events/jdk.ExecutionSample
Project to specific field:
events/jdk.FileRead/path
events/jdk.ExecutionSample/thread/name
Filter and project:
events/jdk.FileRead[bytes>1000]/path
events/jdk.ExecutionSample[thread/name="main"]/stackTrace
Count matching events:
events/jdk.FileRead[bytes>1000] | count()
events/jdk.ExecutionSample[thread/name~"worker-.*"] | count()
Aggregate values:
events/jdk.FileRead/bytes | stats()
events/jdk.FileRead[path~"/tmp/.*"]/bytes | sum()
events/jdk.ExecutionSample | groupBy(thread/name)
events/jdk.FileRead | top(10, by=bytes)
Inspect type structure:
metadata/jdk.types.StackTrace
metadata/java.lang.Thread
List field names:
metadata/jdk.types.Method/fields/name
metadata/jdk.types.StackTrace/fields.name
Access specific field metadata:
metadata/jdk.types.Method/fields/name/type
metadata/jdk.types.StackTrace/fields.frames/annotations
Tree view (recursive):
metadata/jdk.types.StackTrace --tree --depth 2
metadata/jdk.types.Method/fields/name --tree
List all chunks:
chunks
Specific chunk by index:
chunks/0
chunks/5
Filter chunks:
chunks[size>1000000]
chunks[compressed=true]
Chunk summary:
chunks --summary
CP summary (all types with counts):
constants
Entries for specific type:
constants/jdk.types.Symbol
constants/jdk.types.Method
constants/jdk.types.Package
Filter CP entries:
constants/jdk.types.Symbol[string~"java/.*"]
constants/jdk.types.Method[name="toString"]
Project CP entry field:
constants/jdk.types.Symbol/string
constants/jdk.types.Method/name
Aggregate:
constants/jdk.types.Symbol | count()
constants/jdk.types.Symbol/string | len()
Crosscheck CP entries against event references:
constants/jdk.types.StackTrace | crossref()
constants/jdk.types.Method | crossref()
Many operators are chainable in a pipeline. For example:
events/jdk.FileRead | select(path, bytes) | sortBy(bytes) | top(10)
events/jdk.ExecutionSample | groupBy(thread/name) | sortBy(count) | head(5)
events/jdk.ExecutionSample | decorateByTime(jdk.JavaMonitorWait, fields=monitorClass) | select(startTime, $decorator.monitorClass) | top(10)
Terminal aggregation operators (count(), sum(), stats(), quantiles(), sketch(), timerange(), flamegraph(), stackprofile()) consume the stream and produce a summary result — they cannot be chained with each other.
Transform and filter operators (select(), sortBy(), top(), head(), tail(), filter(), distinct(), groupBy(), decorateByTime(), decorateByKey(), value transforms) can be combined freely.
- Streaming First: All operations use streaming parser when possible
- Lazy Evaluation: Events processed incrementally, not loaded into memory
- Early Abort:
--limitstops parsing once enough matches collected - Minimal Syntax: Path-based navigation, filters inline with brackets
- Composability: Filters, projection, and pipelines compose naturally
# Count execution samples
events/jdk.ExecutionSample | count()
# Top 10 files by bytes read
events/jdk.FileRead | top(10, by=bytes)
# Execution samples by thread
events/jdk.ExecutionSample | groupBy(thread/name)
# File reads to /tmp, sum bytes
events/jdk.FileRead[path~"/tmp/.*"] | sum(bytes)
# Execution samples with deep stacks
events/jdk.ExecutionSample[len(stackTrace/frames)>20] --limit 5
# GC events after GC
events/jdk.GCHeapSummary[when/when="After GC"]/heapSpace
# Monitor contention analysis: samples during lock waits
events/jdk.ExecutionSample | decorateByTime(jdk.JavaMonitorWait, fields=monitorClass,duration)
# Request tracing: correlate samples with request context
events/jdk.ExecutionSample | decorateByKey(RequestStart,
key=sampledThread/javaThreadId,
decoratorKey=thread/javaThreadId,
fields=requestId,endpoint)
# GC impact: allocations during GC phases
events/jdk.ObjectAllocationSample | decorateByTime(jdk.GCPhase, fields=name)
| groupBy($decorator.name, agg=sum, value=allocationSize)
# Flame graph from execution samples
events/jdk.ExecutionSample | flamegraph()
# Icicle graph (top-down)
events/jdk.ExecutionSample | flamegraph(direction=top-down)
# Weighted call tree with temporal bucketing
events/jdk.ExecutionSample | stackprofile()
# Recording time span
events/jdk.ExecutionSample | timerange()
# Filter events to a specific hour
events/jdk.ExecutionSample[between(startTime, "2024-08-13T16:00:00", "2024-08-13T17:00:00")]
# Filter events on a specific date
events/jdk.ExecutionSample[on(startTime, "2024-08-13")]
# CPU samples per minute (time-series)
events/jdk.ExecutionSample
| select(asDateTime(truncate(startTime, "minute")) as bucket, eventThread/name as thread)
| groupBy(bucket, agg=count)
# Lock contention with human-readable durations
events/jdk.JavaMonitorEnter | select(startTime, formatDuration(duration) as dur, monitorClass)# Inspect StackTrace structure
show metadata/jdk.types.StackTrace
# List all field names in Method
show metadata/jdk.types.Method/fields/name
# Field annotations
show metadata/jdk.types.StackTrace/fields.frames/annotations
# Recursive tree view
show metadata/jdk.types.StackTrace --tree --depth 3
# Count metadata types
show metadata/jdk.types.Method/name | count()# List all chunks
show chunks
# Chunk summary stats
show chunks --summary
# Large chunks only
show chunks[size>10000000]
# Compressed chunks
show chunks[compressed=true]
# Specific chunk
show chunks/0# CP summary
constants
# All Symbol entries
constants/jdk.types.Symbol
# Symbols matching pattern
constants/jdk.types.Symbol[string~"java/.*"]
# Count symbols
constants/jdk.types.Symbol | count()
# Symbol string lengths
constants/jdk.types.Symbol/string | len()
# Methods named "toString"
constants/jdk.types.Method[name="toString"]
# Crosscheck StackTrace CP entries against event references
constants/jdk.types.StackTrace | crossref()In the interactive shell, use these commands:
open <path> [--alias NAME]- Open a recordingsessions- List all sessionsuse <id|alias>(orsession) - Switch sessionclose [<id|alias>|--all]- Close session(s)info [<id|alias>]- Show session infoshow <expr> [options]- Execute JfrPath querymetadata [options]- List typesmetadata class <name> [options]- Inspect classchunks [options]- List chunkschunk <index> show- Show specific chunkconstants [<type>] [options]- List constant pool entries (alias:cp)constants/<type> | crossref()- Crosscheck CP entries against event referencesevents/<type>[filter] [options]- Query events (shorthand forshow events)help [<command>]- Show helpexit/quit- Exit
Execute queries without entering the shell:
# Show command
jfr-shell show recording.jfr "events/jdk.FileRead | count()"
jfr-shell show recording.jfr "events/jdk.FileRead/bytes | stats()" --format json
# Metadata command
jfr-shell metadata recording.jfr --events-only
jfr-shell metadata recording.jfr --search FileRead
# Chunks command
jfr-shell chunks recording.jfr --summary
# Constants command (alias: cp)
jfr-shell constants recording.jfr --type jdk.types.Symbol
jfr-shell constants recording.jfr --summaryAll non-interactive commands:
- Return exit code 0 on success, 1 on error
- Write errors to stderr
- Support
--format jsonfor structured output - Execute without prompts (suitable for CI/scripting)
See jfr-shell --help and jfr-shell <command> --help for full options.