Probe Expression Engine Syntax for Templating: A Complete Guide
Probe uses the Expr-Lang expression engine with double-curly-brace syntax {{ <expression> }} to evaluate dynamic values, supporting type preservation for single expressions and string concatenation for mixed content.
The Probe monitoring tool (linyows/probe) leverages a powerful expression engine to enable dynamic templating within configuration files, step definitions, and action payloads. Understanding the Probe expression engine syntax for templating is essential for writing flexible, data-driven health checks and automation workflows.
Core Templating Syntax in Probe
The Double-Curly-Brace Pattern
All template expressions in Probe follow a consistent delimiter pattern defined in expr.go:
{{ <expression> }}
This syntax wraps valid Expr-Lang expressions that the engine evaluates against a provided environment map. The implementation in expr.go registers safe functions and validates expressions before execution to prevent security vulnerabilities.
Type Preservation vs String Concatenation
Probe distinguishes between two evaluation modes based on template structure:
- Single expression templates: When the entire string consists of one
{{ … }}block,EvalTemplateWithTypePreservationreturns the raw evaluated value (e.g., integers remainint, booleans remainbool). - Mixed content templates: When text contains one or more
{{ … }}fragments, the engine evaluates each fragment and concatenates results into a string.
This behavior is extensively tested in expr_test.go, which defines the supported syntax and type-handling expectations.
Supported Operators and Functions
Standard Operators and Variables
Probe's expression engine supports Go-like operators and variable references from the evaluation environment. Expressions can access nested maps using dot notation:
env := map[string]any{
"vars": map[string]any{"count": 42},
}
// Access via: {{ vars.count }}
Null-Coalescing and Ternary Operators
The engine implements advanced control flow operators demonstrated in expr_test.go:
- Null-coalescing (
??): Returns the right-hand operand if the left is null or empty - Ternary (
cond ? a : b): Evaluates conditions and returns the appropriate branch
tmpl := "{{ env.URL ?? 'http://localhost' }} is {{ status == 200 ? 'OK' : 'FAIL' }}"
Built-in Custom Functions
expr.go registers several custom functions for dynamic data generation:
random_int(max)– Returns random integer (0 ≤ n < max)random_str(length)– Generates random alphanumeric stringunixtime()– Returns current Unix timestampencode_base64(string)/decode_base64(string)– Base64 encoding operations
Implementation in expr.go
The core templating logic resides in expr.go, which provides the primary API methods:
Eval(expression, env)– Evaluates raw expressions without templatingEvalTemplate(template, env)– Processes mixed-content templates with string outputEvalTemplateWithTypePreservation(template, env)– Preserves types for single-expression templatesEvalTemplateMap(input, env)– Recursively evaluates templates within map structures
Security validation occurs within these methods to prevent code injection, as verified by the comprehensive test suite in expr_test.go.
Practical Code Examples
Simple Variable Substitution
env := map[string]any{
"name": "Alice",
}
result, _ := (&probe.Expr{}).EvalTemplate("Hello {{ name }}!", env)
// → "Hello Alice!"
Type-Preserved Evaluation
env := map[string]any{
"vars": map[string]any{"count": 42},
}
val, _ := (&probe.Expr{}).EvalTemplateWithTypePreservation("{{ vars.count }}", env)
// → 42 (int, not "42")
Using Advanced Operators
env := map[string]any{
"status": 200,
"env": map[string]any{"URL": ""},
}
tmpl := "{{ env.URL ?? 'http://localhost' }} is {{ status == 200 ? 'OK' : 'FAIL' }}"
out, _ := (&probe.Expr{}).EvalTemplate(tmpl, env)
// → "http://localhost is OK"
Built-in Function Usage
env := map[string]any{}
expr := &probe.Expr{}
// Random integer (0 ≤ n < 100)
randInt, _ := expr.Eval("random_int(100)", env)
// Random alphanumeric string of length 16
randStr, _ := expr.Eval("random_str(16)", env)
// Current Unix timestamp
ts, _ := expr.Eval("unixtime()", env)
// Base64 encode/decode
enc, _ := expr.Eval("encode_base64('user:pass')", env) // → "dXNlcjpwYXNz"
dec, _ := expr.Eval("decode_base64(enc)", map[string]any{"enc": enc}) // → "user:pass"
Evaluating Nested Map Templates
input := map[string]any{
"url": "{{ env.URL }}",
"auth": "Bearer {{ env.TOKEN }}",
"nested": map[string]any{
"msg": "Hello {{ name }}",
},
}
env := map[string]any{
"env": map[string]any{"URL": "https://api.example.com", "TOKEN": "s3cr3t"},
"name": "Bob",
}
out := (&probe.Expr{}).EvalTemplateMap(input, env)
// out["url"] → "https://api.example.com"
// out["auth"] → "Bearer s3cr3t"
// out["nested"].(map[string]any)["msg"] → "Hello Bob"
Summary
- Probe uses double-curly-brace syntax (
{{ expression }}) for all templating operations via the Expr-Lang engine. - Type preservation occurs when templates contain only a single expression block, while mixed content produces string concatenation.
- The engine supports Go-like operators, null-coalescing (
??), ternary conditionals (? :), and custom functions likerandom_intandencode_base64. - Core implementation resides in
expr.gowith comprehensive validation and security checks defined inexpr_test.go.
Frequently Asked Questions
What is the basic syntax for Probe's expression templates?
Probe templates use double-curly-brace delimiters: {{ <expression> }}. Any valid Expr-Lang expression can be placed inside the braces, including variable references, arithmetic operations, and function calls. This syntax is processed by the EvalTemplate method in expr.go.
How does Probe handle type conversion in templates?
Probe distinguishes between single-expression and mixed-content templates. When a template consists solely of one {{ … }} block, EvalTemplateWithTypePreservation returns the raw evaluated type (integer, boolean, etc.). When text contains multiple expressions or literal strings, the engine concatenates everything into a final string result.
What built-in functions are available in Probe's expression engine?
Probe registers several custom functions in expr.go for dynamic data generation: random_int(max) for random integers, random_str(length) for alphanumeric strings, unixtime() for timestamps, and encode_base64/decode_base64 for Base64 operations. These functions are validated and sandboxed to prevent security vulnerabilities.
Can Probe templates reference nested variables and use advanced operators?
Yes, Probe supports dot-notation for nested map access (e.g., {{ vars.count }}) and implements advanced operators including null-coalescing (??) for default values and ternary conditionals (cond ? a : b) for inline branching. These features are extensively tested in expr_test.go to ensure consistent evaluation behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →