Axiom Processing Language (APL)
Use APL to query logs, traces, and events with a text-based query language.
Axiom Processing Language (APL) is a text-based query language for logs, traces, and events stored in EventDB. It provides the flexibility to filter, manipulate, and summarize your data exactly the way you need it.
Prerequisites
- Create an Axiom account.
- Create a dataset in Axiom where you send your data.
Build an APL query
APL queries consist of the following:
- Data source: The most common data source is one of your Axiom datasets.
- Operators: Operators filter, manipulate, and summarize your data.
Delimit operators with the pipe character (|).
A typical APL query has the following structure:
DatasetName
| Operator ...
| Operator ...DatasetNameis the name of the dataset you want to query.Operatoris an operation you apply to the data.
Example query
['github-issue-comment-event']
| extend isBot = actor contains '-bot' or actor contains '[bot]'
| where isBot == true
| summarize count() by bin_auto(_time), actorThe query above uses a dataset called github-issue-comment-event as its data source. It uses the following operators:
- extend adds a new field
isBotto the query results. It sets the values of the new field to true if the values of theactorfield in the original dataset contain-botor[bot]. - where filters for the values of the
isBotfield. It only returns rows where the value is true. - summarize aggregates the data and produces a chart.
Each operator is separated using the pipe character (|).
Example result
As a result, the query returns a chart and a table. The table counts the different values of the actor field where isBot is true, and the chart displays the distribution of these counts over time.
| actor | count_ |
|---|---|
| github-actions[bot] | 487 |
| sonarqubecloud[bot] | 208 |
| dependabot[bot] | 148 |
| vercel[bot] | 91 |
| codecov[bot] | 63 |
| openshift-ci[bot] | 52 |
| coderabbitai[bot] | 43 |
| netlify[bot] | 37 |
Quote dataset and field names
If the name of a dataset or field contains at least one of the following special characters, quote the name in your APL query:
- Space (
) - Dot (
.) - Dash (
-)
To quote the dataset or field in your APL query, enclose its name with quotation marks (' or ") and square brackets ([]). For example, ['my-field'].
For more information on rules about naming and quoting entities, see Entity names.
Common patterns
Handle nested JSON
A common scenario is dealing with fields that contain JSON objects. Use parse_json to access nested data.
['sample-http-logs']
| extend parsed_headers = parse_json(req_duration_ms)
| where isnotempty(method)
| project _time, method, status, geo.cityextend parsedField = parse_json(...)converts JSON text into a structured field you can access with dot notation.project _time, method, status, geo.cityselects only the fields you need.
Filter and project early
A well-written query runs faster, consumes fewer resources, and gets you answers more efficiently. The two most important principles:
- Filter early. Reduce the amount of data as soon as possible.
- Project only what you need. Avoid selecting unnecessary fields.
Some datasets are wide, containing hundreds or thousands of fields. When you query these datasets with APL, use project to select only the fields you need. Without project, Axiom retrieves all fields for each event, which slows down queries.
Sub-optimal:
['sample-http-logs']
| sort by _time desc
| take 10This retrieves all fields for each of the 10 events.
Optimized:
['sample-http-logs']
| project _time, method, status, uri, resp_body_size_bytes
| sort by _time desc
| take 10By adding project, the query ignores all other fields, minimizing I/O and reducing data sent over the network.
Virtual fields
Virtual fields let you define new fields based on an APL expression. Instead of pre-processing data before sending it to Axiom, you create these fields on the fly during a query. This provides flexibility for analysis without altering the raw data.
Example: Simple conversion
This example converts a response body size from bytes to kilobytes:
- Name:
resp_size_kb - Expression:
resp_body_size_bytes / 1024
Example: Categorization
This example uses conditional logic to segment data. Define a virtual field to categorize HTTP responses:
- Name:
response_category - Expression:
case(status >= 500, "Server Error", status >= 400, "Client Error", status >= 300, "Redirect", "Success")
Now you can run queries like ... | summarize count() by response_category to compare behavior across these groups.
Factors impacting query performance
- Catch-all queries: Queries that don't specify fields with
projectselect all fields. Avoid this on high-dimensionality datasets. - High cardinality
summarizeoperations: When thebyfield has very many unique values (like atraceId), the query may produce an enormous number of groups. Axiom has built-in limits to protect against this. - Mixed data types: If a field has mixed types (for example, a status code is sometimes a number
200and sometimes a string"200"), queries can produce unexpected results. For best performance, aim for consistent typing. If you can't avoid mixed types, normalize the data at query time using typecasting functions liketostring()ortoint(). For example,| where tostring(status) startswith "2"works reliably on a field with mixed types.
For more information, see Performance.
Platform limits
- Fields per dataset: A dataset can have a maximum number of fields. While the limit is high, ingesting logs with thousands of fields can cause issues.
- Data retention: Datasets have a configurable retention period. Data older than this period is automatically deleted.
- Query rate limits: Axiom imposes rate limits on queries to ensure service stability.
For more information, see Limits.
What’s next
Check out the list of example queries or explore the supported operators and functions: