Kibana Query Language (KQL)
KQL is Kibana's built-in query language for filtering data across Discover, dashboards, and visualizations. It is simpler than Lucene and designed specifically for Kibana users who need fast, readable queries.
KQL vs Lucene
Kibana supports two query languages. KQL is the default since Kibana 7.x.
| Feature | KQL | Lucene |
|---|---|---|
| Default in Kibana 8.x | Yes | No (opt-in) |
| Autocomplete | Full support | Limited |
| Nested field queries | Yes | No |
| Scripted fields | No | No |
| Regular expressions | No | Yes |
| Proximity search | No | Yes |
| Boosting | No | Yes |
| Fuzzy matching | No | Yes |
| Syntax complexity | Simple | More complex |
Switching Between KQL and Lucene
- Click the "KQL" button to the left of the search bar
- Toggle to "Lucene" if needed
- The button label shows which language is active
Rule of thumb: Use KQL for everyday work. Switch to Lucene only when you need regex, fuzzy matching, or proximity searches.
Basic Syntax
Field:Value Pairs
The core of KQL is field: value:
status: active
response: 200
customer_first_name: "Eddie"
Quoting rules:
- Strings with spaces require quotes:
city: "New York" - Single words don't need quotes:
city: London - Numbers don't need quotes:
response: 200 - Booleans don't need quotes:
is_active: true
Free Text Search
Search across all fields by omitting the field name:
error
This searches every text field for the word "error". It is convenient but slower than field-specific queries.
Operators
Comparison Operators
# Equals
response: 200
# Greater than
bytes: > 1000
# Greater than or equal
response_time: >= 500
# Less than
age: < 30
# Less than or equal
price: <= 99.99
Boolean Operators
AND
Both conditions must be true:
status: "error" AND service: "api-gateway"
response: 500 AND method: POST
OR
Either condition can be true:
status: "error" OR status: "warning"
response: 404 OR response: 500
NOT
Exclude matches:
NOT status: "healthy"
status: "error" AND NOT service: "legacy-app"
Operator Precedence
KQL evaluates in this order:
NOT(highest)ANDOR(lowest)
# This query:
status: error OR status: warning AND service: api
# Is interpreted as:
status: error OR (status: warning AND service: api)
# To override, use parentheses:
(status: error OR status: warning) AND service: api
Grouping with Parentheses
Always use parentheses to make intent clear:
# Errors or warnings from the API service
(status: "error" OR status: "warning") AND service: "api-gateway"
# 500s from either the API or the web frontend
response: 500 AND (service: "api" OR service: "web")
# Complex multi-condition
(response: 500 OR response: 503) AND (method: POST OR method: PUT) AND NOT path: "/healthcheck"
Wildcards
Asterisk (*) - Any Characters
Match zero or more characters:
# Starts with
host: prod-*
# Matches: prod-web-01, prod-api-02, prod-db-01
# Ends with
filename: *.log
# Matches: app.log, error.log, access.log
# Contains
message: *timeout*
# Matches: "connection timeout", "request timeout occurred"
# Multiple wildcards
path: /api/*/users/*
# Matches: /api/v1/users/123, /api/v2/users/profile
Field Existence
Check if a field exists (has any value):
# Field exists
error.message: *
# Field does not exist
NOT error.message: *
This is equivalent to an exists filter but usable in the query bar.
Working with Data Types
Strings
# Exact match (keyword field)
status.keyword: "Active"
# Text search (analyzed text field)
message: "connection refused"
# Note: text fields are analyzed, so searching for
# "Connection Refused" matches "connection refused"
# keyword fields are case-sensitive
Numbers
# Exact match
response: 200
# Range
bytes: > 5000
response_time: >= 100 AND response_time: <= 500
# Combine ranges
cpu_percent: > 80 AND cpu_percent: < 100
Dates
KQL uses date math in date fields:
# Exact date (less common)
@timestamp: "2024-01-15"
# Usually you use the time picker for dates instead of KQL
# But date fields work with comparison operators:
order_date: > "2024-01-01" AND order_date: < "2024-02-01"
Tip: For date filtering, use the time picker or visual filters instead of KQL. They are faster and support date math like now-7d.
Booleans
is_active: true
is_deleted: false
IP Addresses
KQL supports CIDR notation:
# Exact IP
clientip: "192.168.1.1"
# CIDR range
clientip: "192.168.1.0/24"
# Matches: 192.168.1.0 through 192.168.1.255
# Private ranges
source.ip: "10.0.0.0/8"
source.ip: "172.16.0.0/12"
source.ip: "192.168.0.0/16"
Nested Field Queries
KQL can query nested objects, which Lucene cannot.
What Are Nested Fields?
Some Elasticsearch documents contain arrays of objects:
{
"order_id": "12345",
"products": [
{ "name": "Shirt", "price": 45.00, "quantity": 2 },
{ "name": "Pants", "price": 65.00, "quantity": 1 }
]
}
Querying Nested Fields
Without nested syntax, queries match across different objects in the array:
# This could match an order where ONE product is "Shirt"
# and a DIFFERENT product has quantity > 1
products.name: "Shirt" AND products.quantity: > 1
With nested syntax, conditions apply to the same object:
# This matches only if the SAME product is "Shirt" AND quantity > 1
products:{ name: "Shirt" AND quantity: > 1 }
Nested Query Examples
# Find orders where a single product costs more than $100 and has quantity > 2
products:{ price: > 100 AND quantity: > 2 }
# Find orders with a specific product in a specific category
products:{ name: "Premium Shirt" AND category: "Men's Clothing" }
# Combine nested with regular fields
customer_name: "Eddie" AND products:{ price: > 50 }
Autocomplete
KQL provides intelligent autocomplete in the search bar.
How Autocomplete Works
- Start typing a field name → Kibana suggests matching fields
- Type
:after field → Kibana suggests operators - Start typing a value → Kibana suggests matching values from your data
Step 1: Type "res"
Suggestions: response, response_time, response.keyword
Step 2: Select "response" and type ":"
Suggestions: 200, 301, 404, 500 (actual values from your data)
Step 3: Select "200"
Result: response: 200
Autocomplete Tips
- Press
Tabto accept a suggestion - Press
Escapeto dismiss suggestions - Autocomplete only works for fields in the selected data view
- Values shown are sampled from recent data (not exhaustive)
Common Query Patterns
Log Analysis
# Find errors in a specific service
level: "error" AND service.name: "payment-service"
# Find slow requests
response_time: > 5000 AND method: "POST"
# Find 5xx errors excluding health checks
response: >= 500 AND NOT url.path: "/health"
# Search log messages
message: *exception* AND NOT message: *expected*
Security Investigation
# Failed login attempts
event.action: "authentication_failed" AND source.ip: *
# Suspicious activity from external IPs
NOT source.ip: "10.0.0.0/8" AND event.action: "login"
# Privilege escalation
event.action: "role_change" AND user.roles: "admin"
# Multiple failed attempts from one IP
event.action: "authentication_failed" AND source.ip: "203.0.113.50"
E-commerce Analysis
# High-value orders
taxful_total_price: > 200
# Orders from specific region
geoip.country_iso_code: "US" AND geoip.region_name: "California"
# Specific product category with high spend
category.keyword: "Men's Shoes" AND taxful_total_price: > 100
# Customer search
customer_full_name: "Eddie*"
Infrastructure Monitoring
# High CPU servers
system.cpu.total.pct: > 0.90
# Disk space warnings
system.filesystem.used.pct: > 0.85
# Memory pressure
system.memory.actual.used.pct: > 0.80 AND host.name: prod-*
# Container issues
container.status: "OOMKilled" OR container.status: "CrashLoopBackOff"
KQL in Different Contexts
In Discover
The search bar in Discover uses KQL by default. Combine with the time picker and visual filters:
Query bar: status: "error" AND service: "api"
Time: Last 24 hours
Filter: environment is "production"
In Dashboards
The dashboard query bar applies KQL across all panels:
# Filter entire dashboard to production errors
environment: "production" AND level: "error"
In Visualizations
Individual visualizations can have their own KQL filters:
# Visualization: "Production Error Count"
# Built-in filter: environment: "production" AND level: "error"
# This filter persists regardless of dashboard-level queries
In Alerting Rules
KQL is used to define alert conditions:
# Alert when error rate spikes
level: "error" AND service: "payment-*"
# Alert for specific error messages
message: *"database connection refused"*
Performance Tips
Query Efficiency
✅ Use field-specific queries
response: 500
❌ Use free-text search
500
(searches every field - much slower)
✅ Use keyword fields for exact matches
status.keyword: "Active"
❌ Use text fields for exact matches
status: "Active"
(text fields are analyzed, may produce unexpected results)
✅ Combine with time range
Set time picker to "Last 1 hour" + status: error
❌ Query without time constraint
status: error (over entire index - slow)
Field Selection
✅ Know your field types
- .keyword fields: exact match, case-sensitive, aggregatable
- text fields: full-text search, analyzed, not aggregatable
✅ Use autocomplete to discover fields
Start typing and let Kibana help
❌ Guess field names
Field names are case-sensitive: Status ≠ status
Query Complexity
✅ Start simple, then refine
1. status: error
2. status: error AND service: api
3. status: error AND service: api AND NOT path: /health
❌ Write complex queries from scratch
Harder to debug when results are unexpected
Lucene Fallback
When KQL can't do what you need, switch to Lucene:
Regular Expressions (Lucene Only)
# Match pattern
status: /err.*/
# IP range with regex
clientip: /192\.168\.1\..*/
Fuzzy Matching (Lucene Only)
# Fuzzy search (handles typos)
customer_name: eddie~2
# Matches: Eddie, Eddy, Edd1e (within edit distance 2)
Proximity Search (Lucene Only)
# Words within N positions of each other
message: "connection timeout"~5
# Matches: "connection was refused due to timeout"
Range Syntax (Lucene)
# Inclusive range
response_time: [100 TO 500]
# Exclusive range
response_time: {100 TO 500}
# Open-ended
bytes: [1000 TO *]
Common Mistakes
Mistake 1: Missing Quotes
❌ city: New York
(Interpreted as: city: New AND York)
✅ city: "New York"
(Correct: city equals "New York")
Mistake 2: Wrong Field Type
❌ category: "Men's Clothing"
(If category is a text field, this does full-text search)
✅ category.keyword: "Men's Clothing"
(Keyword field does exact match)
Mistake 3: Case Sensitivity
# Keyword fields are case-sensitive
❌ status.keyword: "active" (if stored as "Active")
✅ status.keyword: "Active"
# Text fields are NOT case-sensitive (they're analyzed)
✅ message: "Error" (matches "error", "ERROR", etc.)
Mistake 4: Operator Case
❌ status: error and service: api
(Lowercase "and" is treated as a search term)
✅ status: error AND service: api
(Uppercase AND is the boolean operator)
Mistake 5: Escaping Special Characters
# Parentheses, colons, and quotes need escaping in values
❌ message: error (timeout)
✅ message: "error (timeout)"
# Backslash for literal special characters
url.path: /api\/v1\/users
Quick Reference
Syntax Cheat Sheet
BASIC
field: value Exact match
field: "value with spaces" Quoted value
value Free-text (all fields)
COMPARISON
field: > 100 Greater than
field: >= 100 Greater or equal
field: < 100 Less than
field: <= 100 Less or equal
BOOLEAN
expr1 AND expr2 Both must match
expr1 OR expr2 Either can match
NOT expr Must not match
(expr1 OR expr2) AND expr3 Grouping
WILDCARDS
field: val* Starts with
field: *val Ends with
field: *val* Contains
field: * Field exists
NOT field: * Field missing
NESTED
nested_field:{ a: 1 AND b: 2 } Same nested object
IP
ip_field: "10.0.0.0/8" CIDR notation
Summary
In this chapter, you learned:
- ✅ KQL syntax fundamentals: fields, values, operators
- ✅ Boolean logic with AND, OR, NOT and parentheses
- ✅ Wildcards for pattern matching and existence checks
- ✅ Handling different data types (strings, numbers, IPs, dates)
- ✅ Nested field queries for array objects
- ✅ Practical query patterns for logs, security, and monitoring
- ✅ Performance tips for writing efficient queries
- ✅ When and how to fall back to Lucene syntax
Next: Understanding index patterns and data views for connecting Kibana to your data!