Map variables provide structured data storage in JFR Shell, enabling cleaner organization, configuration management, and custom report structures.
- Overview
- Creating Maps
- Accessing Map Fields
- Nested Maps
- Using Maps in Queries
- Map Operations
- Practical Examples
- Tab Completion
Map variables use JSON-like syntax and support immutable structured data storage. They're perfect for:
- Configuration management: store thresholds, patterns, and settings
- Custom reports: build structured analysis results
- Cleaner organization: namespace related values
- Data transformation: intermediate processing steps
Maps use JSON-like syntax with quoted keys and typed values:
jfr> set config = {"threshold": 1000, "enabled": true, "pattern": ".*Error.*"}Strings:
jfr> set names = {"first": "John", "last": "Doe"}Numbers (integers and decimals):
jfr> set stats = {"count": 42, "ratio": 3.14, "negative": -5}Booleans:
jfr> set flags = {"enabled": true, "debug": false}Null values:
jfr> set optional = {"required": "value", "optional": null}Empty maps:
jfr> set empty = {}Use dot notation to access map fields in variable substitutions:
jfr> set config = {"threshold": 1000, "pattern": ".*Error.*"}
jfr> echo "Threshold: ${config.threshold}"
Threshold: 1000
jfr> echo "Pattern: ${config.pattern}"
Pattern: .*Error.*Get map size (entry count):
jfr> set config = {"a": 1, "b": 2, "c": 3}
jfr> echo "Config has ${config.size} entries"
Config has 3 entriesAccessing non-existent fields returns an empty string (no error):
jfr> set config = {"key": "value"}
jfr> echo "Missing: ${config.missing}"
Missing:Maps can contain other maps for hierarchical organization:
# Create nested structure
jfr> set db = {
...> "primary": {"host": "db1.local", "port": 5432},
...> "replica": {"host": "db2.local", "port": 5433}
...> }
# Access nested fields with dot notation
jfr> echo "Primary: ${db.primary.host}:${db.primary.port}"
Primary: db1.local:5432
jfr> echo "Replica: ${db.replica.host}:${db.replica.port}"
Replica: db2.local:5433Arbitrary nesting depth is supported:
jfr> set app = {
...> "config": {
...> "db": {
...> "connection": {
...> "host": "localhost",
...> "pool": {"min": 5, "max": 20}
...> }
...> }
...> }
...> }
jfr> echo "Pool max: ${app.config.db.connection.pool.max}"
Pool max: 20Map values can be used in JfrPath queries:
# Configuration-driven filtering
jfr> set config = {"threshold": 1000, "limit": 10}
jfr> events/jdk.FileRead[bytes>=${config.threshold}] --limit ${config.limit}
# Pattern matching
jfr> set patterns = {"error": ".*Error.*", "warn": ".*Warning.*"}
jfr> events/jdk.FileRead[path~"${patterns.error}"]
# Complex conditions
jfr> set limits = {"min": 1000, "max": 10000}
jfr> events/jdk.FileRead[bytes>=${limits.min} and bytes<=${limits.max}]View all variables including maps:
jfr> vars
Session variables (session #1):
config = map{threshold=1000, enabled=true, pattern=".*Error.*"}
db = map{primary={host="db1.local", port=5432}, replica={...}}Use vars --info to see map structure:
jfr> vars --info config
Variable: config
Type: map
Size: 3 entries
Structure: map{threshold=1000, enabled=true, pattern=".*Error.*"}jfr> unset configSession scope (default) - cleared when session closes:
jfr> set config = {"key": "value"}Global scope - persists across all sessions:
jfr> set --global defaults = {"threshold": 100, "limit": 50}Store analysis configuration in one place:
jfr> set analysis = {
...> "file_io": {"min_bytes": 1000, "top_n": 10},
...> "threads": {"sample_threshold": 100},
...> "gc": {"heap_threshold": 1000000000}
...> }
jfr> events/jdk.FileRead[bytes>=${analysis.file_io.min_bytes}]
...> | top(${analysis.file_io.top_n}, by=bytes)
jfr> events/jdk.ExecutionSample
...> | groupBy(sampledThread/javaName)
...> | top(10, by=count)[count>${analysis.threads.sample_threshold}]Define environment configurations and switch between them:
jfr> set dev = {"log_level": "DEBUG", "timeout": 30, "retries": 3}
jfr> set prod = {"log_level": "ERROR", "timeout": 10, "retries": 5}
# Use dev settings directly
jfr> echo "Dev timeout: ${dev.timeout}s with ${dev.retries} retries"
Dev timeout: 30s with 3 retries
# Use prod settings
jfr> echo "Prod timeout: ${prod.timeout}s with ${prod.retries} retries"
Prod timeout: 10s with 5 retriesCreate structured summary data:
# Store query results first
jfr> set reads = events/jdk.FileRead
jfr> set total = events/jdk.FileRead/bytes | sum()
# Build report structure with literal values
jfr> set report = {
...> "total_reads": 42,
...> "total_bytes": 524288,
...> "timestamp": "2024-01-15"
...> }
# Access report fields
jfr> echo "Report: ${report.total_reads} reads, ${report.total_bytes} bytes on ${report.timestamp}"Note: Expression interpolation in map literals (e.g., {"count": ${reads.size}}) is not yet supported. This feature is planned for Phase 2. For now, use literal values in maps and access query results separately.
Store reusable path patterns:
jfr> set paths = {
...> "logs": "/var/log/.*",
...> "temp": "/tmp/.*",
...> "home": "/home/.*"
...> }
jfr> events/jdk.FileRead[path~"${paths.logs}"]
jfr> events/jdk.FileWrite[path~"${paths.temp}"]Compare settings across environments:
jfr> set environments = {
...> "dev": {"host": "dev.local", "threads": 10},
...> "staging": {"host": "staging.local", "threads": 20},
...> "prod": {"host": "prod.local", "threads": 50}
...> }
jfr> echo "Dev: ${environments.dev.host} (${environments.dev.threads} threads)"
jfr> echo "Prod: ${environments.prod.host} (${environments.prod.threads} threads)"The shell provides intelligent tab completion for map fields:
Variable names:
jfr> echo ${config⇥Shows: config, configBackup, etc.
Map field names:
jfr> echo ${config.⇥Shows: threshold, enabled, pattern, size
Nested field names:
jfr> echo ${db.primary.⇥Shows: host, port, size
In set command:
jfr> set config = ⇥Shows: { (with description: "map literal - {"key": value, ...}")
Map literal patterns:
jfr> set config = {⇥Shows example patterns:
{"key": "value"}- simple map with string value{"count": 0}- map with numeric value{"enabled": true}- map with boolean value{}- empty map
-
Use descriptive keys:
{"max_bytes": 1000}is better than{"mb": 1000} -
Organize hierarchically: Group related settings in nested maps
set config = { "thresholds": {"bytes": 1000, "duration": 5000}, "limits": {"top_n": 10, "max_results": 100} }
-
Keep maps immutable: Create new maps instead of trying to modify existing ones
-
Use global scope for shared config: Put reusable configuration in global scope
set --global defaults = {"threshold": 100, "limit": 50}
-
Document complex structures: Use comments in scripts
# Analysis configuration # thresholds: filtering criteria # limits: result set constraints set config = {"thresholds": {...}, "limits": {...}}
- Immutable: Maps cannot be modified after creation; create a new map instead
- JSON-like syntax only: Keys must be quoted strings
- No array literals: Use nested maps for structure (arrays coming in Phase 2)
- No expressions in values: Values must be literals (expression interpolation coming in Phase 2)
Current implementation is Phase 1 of map variables. Future phases may include:
- Phase 2: Expression interpolation in map values (
{"total": ${reads.size}}) - Phase 3: Map operations (merge, filter, transform) and pipeline integration
- Scripting Guide - Using maps in scripts
- Usage Guide - Complete command reference
- JfrPath Reference - Query language syntax