Skip to main content
Version: v4.25

TQL2 Migration

This page answers the most frequently asked questions about TQL2.

What is TQL2?

TQL2 is the next generation of the Tenzir Query Language for writing pipelines.

How do I use TQL2?

Start your pipeline with a // tql2 comment to opt into using the new language. This is supported for Tenzir v4.19 or newer.

How do I enable TQL2-only mode?

Set the TENZIR_TQL2=true environment variable or start your Tenzir Node with tenzir-node --tql2. This is supported for Tenzir v4.25 or newer.

When will TQL1 be removed?

TQL2-only mode will be the default in Q1 2025, and TQL1 support will be removed later in 2025.

Why create an all-new language?

TQL1 has grown historically. Over time, we identified the following shortcomings that TQL2 fixes:

  • Lack of composable expressions, in particular functions and arithmetic operations.
  • Absence of nested pipelines, which was partially worked around with operator modifiers.
  • Context-dependent grammar for operators that was unintuitive for users, and close to impossible to parse and syntax-highlight.
  • Inability to define constants with let, and also soon types, functions, and custom operators within the pipeline definition directly.
  • Lack of control flow within a pipeline like if { … } else { … } made it unnecessarily hard to work with your data.

Can I mix-and-match TQL1 and TQL2?

Yes! The legacy operator takes a string as an argument that is parsed as a TQL1 pipeline and then replaces itself with it. For example:

metrics "cpu"
where timestamp > now() - 1h
sort timestamp
// Use the `chart` operator from TQL1
legacy "chart area"

When should I upgrade?

Yesterday! TQL2 is the future of Tenzir, and we are committed to making it the best-in-class language for writing data pipelines.

Where can I give feedback about TQL2?

Join our community Discord.

What features does TQL2 currently support?

StatusExplanation
(mostly) complete
🔧in progress
FeatureExampleStatusComments
Simple assignmentconn.test = "done"Insert-or-replace semantics
Statement separationnewline or |
Dynamic field namesobject[expr]🔧
Arbitrary field namesthis["@!?$#"]
Conditionalsif foo == 42 {...} else {...}
Pattern matchingmatch proto { "UDP" => ..., "TCP" => ... }🔧
High performanceColumnar computation engine
Object expression{ foo: 1, bar.baz.qux: 2 }Field names can also be strings
List expression[1, 2, null, 4]
Functions/methodsfoo(bar) && baz.starts_with("bar")
Named argumentsfoo.bar(baz=1, qux=2)
Arithmetic operatorsa + b / c < d - eAlso works with durations, etc.
Event metadatawhere @name == "something"
Constant bindingslet $bad_ip = 1.2.3.4 | search $bad_ip
Domain typessrc in 255.0.0.0/24 && duration > 2minip, subnet, duration, time, blob
Nested pipelinesgroup protocol {...}, every 1h {...}🔧
Spread operator{...foo, bar: [42, ...baz, qux]}Can expand objects and lists
Modulesmy_module::my_function(foo)
OCSF functionalityocsf::validate, ocsf::class_uid
Type definitionstype flow = {src: ip, dst: ip}🔧
Escape hatchespython "..." | shell "..."
Secretssecret("MY_SECRET")
Function definitionsfn my_function(...) { ... }🔧
Multi-stage parsingmsg.parse_json().content.parse_cef()