What gets formatted
KQL is the query language behind Azure Data Explorer, Azure Monitor Log Analytics and Application Insights, Microsoft Sentinel, Defender XDR advanced hunting and Real-Time Intelligence in Microsoft Fabric. A query is a pipeline: a table, then a series of tabular operators joined by |. That shape is what this page makes visible.
PasteKit uses a built-in KQL tokenizer and printer. It starts each tabular operator — where, extend, summarize, project, join, order by, take, render and the rest — on its own line beginning with the pipe, and normalises the spacing around operators, commas and = in assignments. let statements at the top of a query stay as separate statements ending in ;, and the subquery inside a join or union is indented so you can see where it begins and ends.
String literals (including verbatim @"..." strings), datetime() and timespan literals such as 7d, and // comments are left exactly as written.
Using the formatter
KQL has no special options here; the toolbar’s indent and line-width settings control how nested subqueries are indented and when a long argument list is wrapped. The editor uses a monospace font, so the result pastes cleanly back into the Azure portal, Kusto Explorer, the Fabric KQL queryset or a .kql file in a Git repository of Sentinel detections.
Keyboard shortcuts: Ctrl/Cmd+Enter formats, Ctrl/Cmd+Shift+C copies the formatted query, Ctrl/Cmd+K opens the command palette. Minifying (Ctrl/Cmd+Shift+M) is reserved for formats such as JSON and CSS and does not apply to KQL.
If a query cannot be tokenised — an unclosed string or an unbalanced parenthesis — the problem is shown with its line and column instead of formatted output.
KQL details worth remembering while you tidy
Formatting makes a pipeline easier to review, and a few language rules are easier to spot once each step has its own line:
hasmatches whole terms using the index and is usually much faster thancontains, which scans for substrings.==is case-sensitive;=~is the case-insensitive comparison.- every
letstatement must be terminated with a semicolon before the query that uses it. - Filter early: put
whereon the time column first so the engine can skip data. takereturns arbitrary rows; usetopororder bywhen order matters.
Version control is where formatted KQL pays off most. Detection rules stored as code — Sentinel content in Git, Defender custom detections, or ADX functions deployed by pipeline — produce readable diffs when every operator sits on its own line, because a changed filter shows up as one changed line rather than a rewritten 400-character string.
The SQL formatter covers T-SQL, which Azure Data Explorer can also accept for simple queries.
Examples
Storm events summary (Azure Data Explorer sample data)
Each pipe stage becomes a line, making the two separate where filters easy to see.
StormEvents | where StartTime between (datetime(2026-01-01)..datetime(2026-06-30)) and State has "TEXAS" | summarize Events=count(), Damage=sum(DamageProperty) by EventType, bin(StartTime, 7d) | where Events > 10 | order by Damage desc | take 20StormEvents
| where StartTime between (datetime(2026-01-01) .. datetime(2026-06-30))
and State has "TEXAS"
| summarize
Events = count(),
Damage = sum(DamageProperty)
by EventType, bin(StartTime, 7d)
| where Events > 10
| order by Damage desc
| take 20
Sentinel: failed sign-ins per user
A typical detection query; the aggregation and the threshold filter end up on separate lines.
SigninLogs | where TimeGenerated > ago(1d) and ResultType != "0" | summarize Failures=count(), IPs=dcount(IPAddress) by UserPrincipalName | where Failures > 20 | top 10 by FailuresSigninLogs
| where TimeGenerated > ago(1d) and ResultType != "0"
| summarize Failures = count(), IPs = dcount(IPAddress) by UserPrincipalName
| where Failures > 20
| top 10 by Failures
let statement and join
The let statements stay separate, and the subquery inside join is indented.
let threshold = 500; let slow = requests | where duration > threshold | project operation_Id, name, duration; slow | join kind=inner (exceptions | project operation_Id, type, outerMessage) on operation_Id | take 50let threshold = 500;
let slow = requests
| where duration > threshold
| project operation_Id, name, duration;
slow
| join kind=inner (
exceptions
| project operation_Id, type, outerMessage
) on operation_Id
| take 50
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Unterminated string literal | A string started with " or ’ and was not closed on the same line. | Close the quote. For text with backslashes, such as Windows paths, a verbatim string @“C:\temp” avoids escaping. |
Unbalanced parentheses | A function call, a join subquery or a between (a … b) range is missing a parenthesis. | Count the parentheses on the reported line; join subqueries are the usual place to lose one. |
Formats fine, but Log Analytics reports a syntax error right after a let statement | The let statement is missing its terminating semicolon. The formatter only tokenises, so it cannot catch this. | Add ; after each let, then run the query again. |
Frequently asked questions
Does this work for Microsoft Sentinel and Defender queries?
Yes. Sentinel analytics rules, hunting queries and Defender XDR advanced hunting all use KQL, so they format the same way.
Does the formatter validate table and column names?
No. It has no connection to your cluster or workspace, so schema errors are only caught when the query runs.
Will it change string contents or comments?
No. Strings, verbatim strings and // comments are kept exactly as written.
What is the difference between KQL and SQL?
KQL is a read-only pipeline language: data flows left to right through operators joined by |. SQL describes the result in one declarative statement.