Skip to main content
Version: Next

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()