This guide shows you how to create user-defined operators (UDOs) for your package. You’ll learn how to define operators with positional and named arguments, and how to test them with the Test Framework.
Create a user-defined operator
Section titled “Create a user-defined operator”User-defined operators (UDOs) are reusable building blocks that you can use
in your pipelines. Place operator files in the operators directory of your
package.
Tenzir names operators using the convention <package>::[dirs...]::<basename>.
For example, a file at operators/ocsf/map.tql in a package with ID acme
becomes the operator acme::ocsf::map.
// Normalize authentication logs to OCSF Authentication event classclass_uid = 3002category_uid = 3activity_id = 1 if outcome == "success" else 2actor.user.name = usernameactor.user.uid = user_idsrc_endpoint.ip = src_ipdst_endpoint.ip = dst_ipdrop username, user_id, src_ip, dst_ip, outcomeAfter installing the package, use the operator in any pipeline:
from_file "auth.json"acme::ocsf::authpublish "ocsf-events"Add parameters to operators
Section titled “Add parameters to operators”Operators can accept positional and named arguments, enabling you to create
flexible, reusable building blocks that match the calling conventions of
built-in operators. Define parameters in a YAML frontmatter block at the
beginning of the .tql file.
Define positional arguments
Section titled “Define positional arguments”Positional arguments are passed in order when calling the operator. Define them
under the args.positional key:
---args: positional: - name: field type: field - name: value type: string---
$field = $valueCall this operator with positional arguments:
from {x: 1}acme::tag name, "Alice"{x: 1, name: "Alice"}Define named arguments
Section titled “Define named arguments”Named arguments use the name=value syntax and can have default values. Define
them under the args.named key:
---args: positional: - name: field type: field - name: value type: string named: - name: prefix type: string default: ""---
$field = $prefix + $valueCall this operator with both positional and named arguments:
from {x: 1}acme::tag name, "Alice", prefix="User: "{x: 1, name: "User: Alice"}Parameter schema
Section titled “Parameter schema”Each parameter supports the following fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Parameter name, used as $name in the operator body |
type | No | Type constraint for the parameter value |
description | No | Documentation string for the parameter |
default | No | Default value if the argument is not provided |
Supported types
Section titled “Supported types”The type field constrains what values the parameter accepts:
| Type | Description |
|---|---|
field | A field selector (for example, name). Cannot have defaults |
string | A string literal or expression |
int | An integer value |
double | A floating-point value |
bool | A boolean value |
ip | An IP address |
secret | A secret string (accepts string literals) |
If you omit the type field, the parameter accepts any value.
Optional positional arguments
Section titled “Optional positional arguments”Positional arguments with a default value become optional. Callers can omit
them, and Tenzir substitutes the default:
---args: positional: - name: name type: string default: "World"---
greeting = "Hello, " + $name + "!"Calling the operator without arguments uses the default value:
from {}acme::greet{greeting: "Hello, World!"}Passing an explicit argument overrides the default:
from {}acme::greet "Alice"{greeting: "Hello, Alice!"}Use field parameters
Section titled “Use field parameters”The field type enables dynamic field selection. The caller passes a field
path, and the operator uses it to read or write data:
---args: positional: - name: target type: field---
$target = $target * 2Using the operator:
from {count: 5, score: 10}acme::double_value count{count: 10, score: 10}Call other operators
Section titled “Call other operators”Parameterized operators can call other operators, including passing through their own parameters:
---args: positional: - name: field type: field named: - name: multiplier type: int default: 2---
utils::scale $field, factor=$multiplierType checking
Section titled “Type checking”Tenzir validates parameter types at compile time when possible:
- Compile-time checking occurs when arguments are constant values
- Runtime checking defers validation for expressions containing runtime data
If a type mismatch occurs, Tenzir reports an error with the expected type and shows usage information for the operator.