# Concepts
Source: https://axiom.co/docs/ai-engineering/concepts
Learn about the core concepts in AI engineering: Capabilities, Collections, Evals, Scorers, Annotations, and User Feedback.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
This page defines the core terms used in the AI engineering workflow. Understanding these concepts is the first step toward building robust and reliable generative AI capabilities.
## AI engineering lifecycle
The concepts in AI engineering are best understood within the context of the development lifecycle. While AI capabilities can become highly sophisticated, they typically start simple and evolve through a disciplined, iterative process:
Development starts by defining a task and prototyping a capability with a prompt to solve it.
The prototype is then tested against a collection of reference examples (so called "ground truth") to measure its quality and effectiveness using scorers. This process is known as an eval.
Once a capability meets quality benchmarks, it’s deployed. In production, scorers can be applied to live traffic (online evals) to monitor performance and cost in real-time.
Insights from production monitoring reveal edge cases and opportunities for improvement. These new examples are used to refine the capability, expand the ground truth collection, and begin the cycle anew.
## AI engineering terms
### Capability
A generative AI capability is a system that uses large language models to perform a specific task by transforming inputs into desired outputs.
Capabilities exist on a spectrum of complexity, ranging from simple to sophisticated architectures:
* **Single-turn model interactions**: A single prompt and response, such as classifying a support ticket’s intent or summarizing a document.
* **Workflows**: Multi-step processes where each step’s output feeds into the next, such as research → analysis → report generation.
* **Single-agent**: An agent that can reasons and make decisions to accomplish a goal, such as a customer support agent that can search documentation, check order status, and draft responses.
* **Multi-agent**: Multiple specialized agents collaborating to solve complex problems, such as software engineering through architectural planning, coding, testing, and review.
### Collection
A collection is a curated set of reference records used for development, testing, and evaluation of a capability. Collections serve as the test cases for prompt engineering.
### Collection record
Collection records are the individual input-output pairs within a collection. Each record consists of an input and its corresponding expected output (ground truth).
### Ground truth
Ground truth is the validated, expert-approved correct output for a given input. It represents the gold standard that the AI capability should aspire to match.
### Scorer
A scorer is a function that evaluates a capability’s output. It programmatically assesses quality by comparing the generated output against ground truth or other criteria, returning a score.
### Evaluation or "eval"
An evaluation, or eval, is the process of testing a capability against a collection of ground truth data using one or more scorers. An eval runs the capability on every record in the collection and reports metrics like accuracy, pass-rate, and cost. Evals are typically run before deployment to benchmark performance.
### Flag
A flag is a configuration parameter that controls how your AI capability behaves. Flags let you parameterize aspects like model choice, tool availability, prompting strategies, or retrieval approaches. By defining flags, you can run experiments to compare different configurations and systematically determine which approach performs best.
### Experiment
An experiment is an evaluation run with a specific set of flag values. By running multiple experiments with different flag configurations, you can compare performance across different models, prompts, or strategies to find the optimal setup for your capability.
### Online evaluation
An online evaluation is the process of applying a scorer to a capability’s live production traffic. This provides real-time feedback on performance degradation, cost, and quality drift, enabling continuous monitoring and improvement.
### Annotation
Annotations are expert-provided observations, labels, or corrections added to production traces or evaluation results. Domain experts review AI capability runs and document what went wrong, what should have happened differently, or categorize failure modes. These annotations help identify patterns in capability failures, validate scorer accuracy, and create new test cases for collections.
### User feedback
User feedback is direct signal from end users about AI capability performance, typically collected through ratings (thumbs up/down, stars) or text comments. Feedback events are associated with traces to provide context about both system behavior and user perception. Aggregated feedback reveals quality trends, helps prioritize improvements, and surfaces issues that might not appear in evaluations.
## What’s next?
Now that you understand the core concepts, get started with the [Quickstart](/ai-engineering/quickstart) or dive into [Evaluate](/ai-engineering/evaluate/overview) to learn about systematic testing.
# Create
Source: https://axiom.co/docs/ai-engineering/create
Build AI capabilities using any framework, with best support for TypeScript-based tools.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
Building an AI capability starts with prototyping. You can use whichever framework you prefer. Axiom is focused on helping you evaluate and observe your capabilities rather than prescribing how to build them.
TypeScript-based frameworks like Vercel’s [AI SDK](https://sdk.vercel.ai) do integrate most seamlessly with Axiom’s tooling today, but that’s likely to evolve over time.
## Build your capability
Define your capability using your framework of choice. Here’s an example using Vercel's [AI SDK](https://ai-sdk.dev/), which includes [many examples](https://sdk.vercel.ai/examples) covering different capability design patterns. Popular alternatives like [Mastra](https://mastra.ai) also exist.
```ts src/lib/capabilities/classify-ticket.ts expandable theme={null}
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
import { z } from 'zod';
export async function classifyTicket(input: {
subject?: string;
content: string
}) {
const result = await generateObject({
model: wrapAISDKModel(openai('gpt-4o-mini')),
messages: [
{
role: 'system',
content: 'Classify support tickets as: question, bug_report, or feature_request.',
},
{
role: 'user',
content: input.subject
? `Subject: ${input.subject}\n\n${input.content}`
: input.content,
},
],
schema: z.object({
category: z.enum(['question', 'bug_report', 'feature_request']),
confidence: z.number().min(0).max(1),
}),
});
return result.object;
}
```
The `wrapAISDKModel` function instruments your model calls for Axiom’s observability features. Learn more in the [Observe](/ai-engineering/observe) section.
## Gather reference examples
As you prototype, collect examples of inputs and their correct outputs.
```ts theme={null}
const referenceExamples = [
{
input: {
subject: 'How do I reset my password?',
content: 'I forgot my password and need help.'
},
expected: { category: 'question' },
},
{
input: {
subject: 'App crashes on startup',
content: 'The app immediately crashes when I open it.'
},
expected: { category: 'bug_report' },
},
];
```
These become your ground truth for evaluation. Learn more in the [Evaluate](/ai-engineering/evaluate/overview) section.
## Structured prompt management
The features below are experimental. Axiom’s current focus is on the evaluation and observability stages of the AI engineering workflow.
For teams wanting more structure around prompt definitions, Axiom’s SDK includes experimental utilities for managing prompts as versioned objects.
### Define prompts as objects
Represent capabilities as structured `Prompt` objects:
```ts src/prompts/ticket-classifier.prompt.ts theme={null}
import {
experimental_Type,
type experimental_Prompt
} from 'axiom/ai';
export const ticketClassifierPrompt = {
name: "Ticket Classifier",
slug: "ticket-classifier",
version: "1.0.0",
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: "Classify support tickets as: {{ categories }}",
},
{
role: "user",
content: "{{ ticket_content }}",
},
],
arguments: {
categories: experimental_Type.String(),
ticket_content: experimental_Type.String(),
},
} satisfies experimental_Prompt;
```
### Type-safe arguments
The `experimental_Type` system provides type safety for prompt arguments:
```ts theme={null}
arguments: {
user: experimental_Type.Object({
name: experimental_Type.String(),
preferences: experimental_Type.Array(experimental_Type.String()),
}),
priority: experimental_Type.Union([
experimental_Type.Literal("high"),
experimental_Type.Literal("medium"),
experimental_Type.Literal("low"),
]),
}
```
### Local testing
Test prompts locally before using them:
```ts theme={null}
import { experimental_parse } from 'axiom/ai';
const parsed = await experimental_parse(ticketClassifierPrompt, {
context: {
categories: 'question, bug_report, feature_request',
ticket_content: 'How do I reset my password?',
},
});
console.log(parsed.messages);
```
These utilities help organize prompts in your codebase. Centralized prompt management and versioning features may be added in future releases.
## What's next?
Once you have a working capability and reference examples, systematically evaluate its performance.
To learn how to set up and run evaluations, see [Evaluate](/ai-engineering/evaluate/overview).
# Analyze results
Source: https://axiom.co/docs/ai-engineering/evaluate/analyze-results
Understand how changes to your AI capabilities impact performance, cost, and quality.
After running an evaluation, the CLI provides a link to view results in the Axiom Console:
```
your-eval-name (your-eval.eval.ts)
• scorer-one 95.00%
• scorer-two 87.50%
• scorer-three 100.00%
View full report:
https://app.axiom.co/:org-id/ai-engineering/evaluations?runId=:run-id
Test Files 1 passed (1)
Tests 4 passed (4)
Duration 5.2 s
```
The evaluation interface helps you answer three core questions:
1. How well does this configuration perform?
2. How does it compare to previous versions?
3. Which tradeoffs are acceptable?
## Compare configurations
To understand the impact of changes, compare evaluation runs to see deltas in accuracy, latency, and cost.
### Using the Console
Run your evaluation before and after making changes, then compare both runs in the Axiom Console:
```bash theme={null}
# Run baseline
axiom eval your-eval-name
# Make changes to your capability (update prompt, switch models, etc.)
# Run again
axiom eval your-eval-name
```
The Console shows both runs where you can analyze differences side-by-side.
### Using the baseline flag
For direct CLI comparison, specify a baseline evaluation ID:
```bash theme={null}
# Run baseline and note the trace ID from the output
axiom eval your-eval-name
# Make changes, then run with baseline
axiom eval your-eval-name --baseline
```
The CLI output will show deltas for each metric.
The `--baseline` flag expects a trace ID. After running an evaluation, copy the trace ID from the CLI output or Console URL to use as a baseline for comparison.
Example: Switching from `gpt-4o-mini` to `gpt-4o` might show:
* Accuracy: 85% → 95% (+10%)
* Latency: 800 ms → 1.6 s (+100%)
* Cost per run: $0.002 → $0.020 (+900%)
This data helps you decide whether the quality improvement justifies the cost and latency increase for your use case.
## Investigate failures
When test cases fail, click into them to see:
* The exact input that triggered the failure
* What your capability output vs what was expected
* The full trace of LLM calls and tool executions
Look for patterns:
* Do failures cluster around specific input types?
* Are certain scorers failing consistently?
* Is high token usage correlated with failures?
Use these insights to add targeted test cases or refine your capability.
## Experiment with flags
Flags let you test multiple configurations systematically. Run several experiments:
```bash theme={null}
# Compare model and retrieval configurations
axiom eval --flag.model=gpt-4o-mini --flag.retrieval.topK=3
axiom eval --flag.model=gpt-4o-mini --flag.retrieval.topK=10
axiom eval --flag.model=gpt-4o --flag.retrieval.topK=3
axiom eval --flag.model=gpt-4o --flag.retrieval.topK=10
```
Compare all four runs in the Console to find the configuration that best balances quality, cost, and latency for your requirements.
## Track progress over time
For teams running evaluations regularly (nightly or in CI), the Console shows whether your capability is improving or regressing across iterations.
Compare your latest run against your initial baseline to verify that accumulated changes are moving in the right direction.
## What's next?
To learn how to use flags for experimentation, see [Flags and experiments](/ai-engineering/evaluate/flags-experiments).
# Flags and experiments
Source: https://axiom.co/docs/ai-engineering/evaluate/flags-experiments
Use flags to parameterize AI capabilities and run experiments comparing different configurations.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
Flags are configuration parameters that control how your AI capability behaves. By defining flags, you can run experiments that systematically compare different models, prompts, retrieval strategies, or architectural approaches - all without changing your code.
This is one of Axiom’s key differentiators: type-safe, version-controlled configuration that integrates seamlessly with your evaluation workflow.
## Why flags matter
AI capabilities have many tunable parameters: which model to use, which tools to enable, which prompting strategy, how to structure retrieval, and more. Without flags, you’d need to:
* Hard-code values and manually change them between tests
* Maintain multiple versions of the same code
* Lose track of which configuration produced which results
* Struggle to reproduce experiments
Flags solve this by:
* **Parameterizing behavior**: Define what can vary in your capability
* **Enabling experimentation**: Test multiple configurations systematically
* **Tracking results**: Axiom records which flag values produced which scores
* **Automating optimization**: Run experiments in CI/CD to find the best configuration
## Setting up flags
Flags are defined using [Zod](https://zod.dev/) schemas in an "app scope" file. This provides type safety and ensures flag values are validated at runtime.
### Create the app scope
Create a file to define your flags (typically `src/lib/app-scope.ts`):
```ts src/lib/app-scope.ts theme={null}
import { createAppScope } from 'axiom/ai';
import { z } from 'zod';
export const flagSchema = z.object({
// Flags for ticket classification capability
ticketClassification: z.object({
model: z.string().default('gpt-4o-mini'),
systemPrompt: z.enum(['concise', 'detailed']).default('concise'),
useStructuredOutput: z.boolean().default(true),
}),
// Flags for document summarization capability
summarization: z.object({
model: z.string().default('gpt-4o'),
maxTokens: z.number().default(500),
style: z.enum(['bullet-points', 'paragraph']).default('bullet-points'),
}),
});
const { flag, pickFlags } = createAppScope({ flagSchema });
export { flag, pickFlags };
```
### Use flags in your capability
Reference flags in your capability code using the `flag()` function:
```ts src/lib/capabilities/classify-ticket/prompts.ts theme={null}
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
import { flag } from '../../app-scope';
import { z } from 'zod';
const systemPrompts = {
concise: 'Classify tickets briefly as: spam, question, feature_request, or bug_report.',
detailed: `You are an expert customer support engineer. Carefully analyze each ticket
and classify it as spam, question, feature_request, or bug_report. Consider context and intent.`,
};
export async function classifyTicket(input: { subject?: string; content: string }) {
// Get flag values
const model = flag('ticketClassification.model');
const promptStyle = flag('ticketClassification.systemPrompt');
const useStructured = flag('ticketClassification.useStructuredOutput');
const result = await generateObject({
model: wrapAISDKModel(openai(model)),
messages: [
{
role: 'system',
content: systemPrompts[promptStyle],
},
{
role: 'user',
content: input.subject
? `Subject: ${input.subject}\n\n${input.content}`
: input.content,
},
],
schema: z.object({
category: z.enum(['spam', 'question', 'feature_request', 'bug_report']),
}),
});
return result.object;
}
```
### Declare flags in evaluations
Tell your evaluation which flags it depends on using `pickFlags()`. This provides two key benefits:
* **Documentation**: Makes flag dependencies explicit and visible
* **Validation**: Warns about undeclared flag usage, catching configuration drift early
```ts src/lib/capabilities/classify-ticket/evaluations/spam-classification.eval.ts theme={null}
import { Eval, Scorer } from 'axiom/ai/evals';
import { pickFlags } from '../../../app-scope';
import { classifyTicket } from '../prompts';
Eval('spam-classification', {
// Declare which flags this eval uses
configFlags: pickFlags('ticketClassification'),
capability: 'classify-ticket',
data: [/* test cases */],
task: async ({ input }) => await classifyTicket(input),
scorers: [/* scorering functions */],
});
```
## Running experiments
With flags defined, you can run experiments by overriding flag values at runtime.
### CLI flag overrides
Override individual flags directly in the command:
```bash theme={null}
# Test with GPT-4o instead of the default
axiom eval --flag.ticketClassification.model=gpt-4o
# Test with different prompt style
axiom eval --flag.ticketClassification.systemPrompt=detailed
# Test multiple flags
axiom eval \
--flag.ticketClassification.model=gpt-4o \
--flag.ticketClassification.systemPrompt=detailed \
--flag.ticketClassification.useStructuredOutput=false
```
### JSON configuration files
For complex experiments, define flag overrides in JSON files:
```json experiments/gpt4-detailed.json theme={null}
{
"ticketClassification": {
"model": "gpt-4o",
"systemPrompt": "detailed",
"useStructuredOutput": true
}
}
```
```json experiments/gpt4-mini-concise.json theme={null}
{
"ticketClassification": {
"model": "gpt-4o-mini",
"systemPrompt": "concise",
"useStructuredOutput": false
}
}
```
Run evaluations with these configurations:
```bash theme={null}
# Run with first configuration
axiom eval --flags-config=experiments/gpt4-detailed.json
# Run with second configuration
axiom eval --flags-config=experiments/gpt4-mini-concise.json
```
Store experiment configurations in version control. This makes it easy to reproduce results and track which experiments you've tried.
### Comparing experiments
Run the same evaluation with different flag values to compare approaches:
```bash theme={null}
# Baseline: default flags (gpt-4o-mini, concise, structured output)
axiom eval spam-classification
# Experiment 1: Try GPT-4o
axiom eval spam-classification --flag.ticketClassification.model=gpt-4o
# Experiment 2: Use detailed prompting
axiom eval spam-classification --flag.ticketClassification.systemPrompt=detailed
# Experiment 3: Test without structured output
axiom eval spam-classification --flag.ticketClassification.useStructuredOutput=false
```
Axiom tracks all these runs in the Console, making it easy to compare scores and identify the best configuration.
## Best practices
### Organize flags by capability
Group related flags together to make them easier to manage:
```ts theme={null}
export const flagSchema = z.object({
// One group per capability
ticketClassification: z.object({
model: z.string().default('gpt-4o-mini'),
temperature: z.number().default(0.7),
}),
emailGeneration: z.object({
model: z.string().default('gpt-4o'),
tone: z.enum(['formal', 'casual']).default('formal'),
}),
documentRetrieval: z.object({
topK: z.number().default(5),
similarityThreshold: z.number().default(0.7),
}),
});
```
### Set sensible defaults
Choose defaults that work well for most cases. Experiments then test variations:
```ts theme={null}
ticketClassification: z.object({
model: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo']).default('gpt-4o-mini'),
systemPrompt: z.enum(['concise', 'detailed']).default('concise'),
useStructuredOutput: z.boolean().default(true),
}),
```
For evaluations that test your application code, it’s best to use the same defaults as your production configuration.
### Use enums for discrete choices
When flags have a fixed set of valid values, use enums for type safety:
```ts theme={null}
// Good: type-safe, prevents invalid values
model: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo']).default('gpt-4o-mini'),
tone: z.enum(['formal', 'casual', 'friendly']).default('formal'),
// Avoid: any string is valid, causes runtime errors with AI SDK
model: z.string().default('gpt-4o-mini'),
tone: z.string().default('formal'),
```
## Advanced patterns
### Model comparison matrix
Test your capability across multiple models systematically:
```bash theme={null}
# Create experiment configs for each model
echo '{"ticketClassification":{"model":"gpt-4o-mini"}}' > exp-mini.json
echo '{"ticketClassification":{"model":"gpt-4o"}}' > exp-4o.json
echo '{"ticketClassification":{"model":"gpt-4-turbo"}}' > exp-turbo.json
# Run all experiments
axiom eval --flags-config=exp-mini.json
axiom eval --flags-config=exp-4o.json
axiom eval --flags-config=exp-turbo.json
```
### Prompt strategy testing
Compare different prompting approaches:
```ts theme={null}
export const flagSchema = z.object({
summarization: z.object({
strategy: z.enum([
'chain-of-thought',
'few-shot',
'zero-shot',
'structured-output',
]).default('zero-shot'),
}),
});
```
```bash theme={null}
# Test each strategy
for strategy in chain-of-thought few-shot zero-shot structured-output; do
axiom eval --flag.summarization.strategy=$strategy
done
```
### Cost vs quality optimization
Find the sweet spot between performance and cost:
```json experiments/cost-quality-matrix.json theme={null}
[
{ "model": "gpt-4o-mini", "temperature": 0.7 },
{ "model": "gpt-4o-mini", "temperature": 0.3 },
{ "model": "gpt-4o", "temperature": 0.7 },
{ "model": "gpt-4o", "temperature": 0.3 }
]
```
Run experiments and compare cost (from telemetry) against accuracy scores to find the optimal configuration.
### CI/CD integration
Run experiments automatically in your CI pipeline:
```yaml .github/workflows/eval.yml theme={null}
name: Run Evaluations
on: [pull_request]
jobs:
eval:
runs-on: ubuntu-latest
strategy:
matrix:
model: [gpt-4o-mini, gpt-4o]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: |
npx axiom eval \
--flag.ticketClassification.model=${{ matrix.model }}
env:
AXIOM_TOKEN: ${{ secrets.AXIOM_TOKEN }}
AXIOM_DATASET: ${{ secrets.AXIOM_DATASET }}
```
This automatically tests your capability with different configurations on every pull request.
## What's next?
* To learn all CLI commands for running evaluations, see [Run evaluations](/ai-engineering/evaluate/run-evaluations).
* To view results in the Console and compare experiments, see [Analyze results](/ai-engineering/evaluate/analyze-results).
# Evaluation overview
Source: https://axiom.co/docs/ai-engineering/evaluate/overview
Systematically measure and improve your AI capabilities through offline evaluation.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
Evaluation is the systematic process of measuring how well your AI capability performs against known correct examples. Instead of relying on manual spot-checks or subjective assessments, evaluations provide quantitative, repeatable benchmarks that let you confidently improve your AI systems over time.
## Why systematic evaluation matters
AI systems fail in non-deterministic ways. The same prompt can produce different results. Edge cases emerge unpredictably. As capabilities grow from simple single-turn interactions to complex multi-agent systems, manual testing becomes impossible to scale.
Systematic evaluation solves this by:
* **Establishing baselines**: Measure current performance before making changes
* **Preventing regressions**: Catch quality degradation before it reaches production
* **Enabling experimentation**: Compare different models, prompts, or architectures
* **Building confidence**: Deploy changes knowing they improve aggregate performance
## The evaluation workflow
Axiom's evaluation framework follows a simple pattern:
Build a set of test cases with inputs and expected outputs (ground truth). Start small with 10-20 examples and grow over time.
Write functions that compare your capability’s output against the expected result. Use custom logic or prebuilt scorers from libraries like `autoevals`.
Execute your capability against the collection and score the results. Track metrics like accuracy, pass rate, and cost.
Review results in the Axiom Console. Compare against baselines. Identify failures. Make improvements and re-evaluate.
## What’s next?
* To set up your environment and authenticate, see [Setup and authentication](/ai-engineering/evaluate/setup).
* To learn how to write evaluation functions, see [Write evaluations](/ai-engineering/evaluate/write-evaluations).
* To understand flags and experiments, see [Flags and experiments](/ai-engineering/evaluate/flags-experiments).
* To view results in the Console, see [Analyze results](/ai-engineering/evaluate/analyze-results).
# Run evaluations
Source: https://axiom.co/docs/ai-engineering/evaluate/run-evaluations
Learn how to run evaluations using the Axiom CLI and interpret the results.
The Axiom AI SDK CLI provides commands for running evaluations locally or in CI/CD pipelines.
## Run evaluations
The simplest way to run evaluations is to execute all of them in your project:
```bash theme={null}
axiom eval
```
You can also target specific evaluations by name, file path, or glob pattern:
```bash theme={null}
# By evaluation name
axiom eval spam-classification
# By file path
axiom eval src/evals/spam-classification.eval.ts
# By glob pattern
axiom eval "**/*spam*.eval.ts"
```
To see which evaluations are available without running them:
```bash theme={null}
axiom eval --list
```
## Common options
For quick local testing without sending traces to Axiom, use debug mode:
```bash theme={null}
axiom eval --debug
```
To compare results against a previous evaluation, view both runs in the Axiom Console where you can analyze differences in scores, latency, and cost.
## Run experiments with flags
Flags let you test different configurations without changing code. Override flag values directly in the command:
```bash theme={null}
# Single flag
axiom eval --flag.ticketClassification.model=gpt-4o
# Multiple flags
axiom eval \
--flag.ticketClassification.model=gpt-4o \
--flag.ticketClassification.temperature=0.3
```
For complex experiments, load flag overrides from a JSON file:
```bash theme={null}
axiom eval --flags-config=experiments/gpt4.json
```
## Understanding output
When you run an evaluation, the CLI shows progress, scores, and a link to view detailed results in the Axiom Console:
```
✓ spam-classification (4/4 passed)
✓ Test case 1: spam detection
✓ Test case 2: legitimate question
Scorers:
category-match: 100% (4/4)
high-confidence: 75% (3/4)
Results:
Total: 4 test cases
Passed: 4 (100%)
Duration: 3.2s
Cost: $0.0024
View full report:
https://app.axiom.co/your-org/ai-engineering/evaluations?runId=ABC123
```
Click the link to view results in the Console, compare runs, and analyze performance.
## What's next?
To learn how to view and analyze evaluation results, see [Analyze results](/ai-engineering/evaluate/analyze-results).
# Setup and authentication
Source: https://axiom.co/docs/ai-engineering/evaluate/setup
Install the Axiom AI SDK and authenticate with the CLI to run evaluations.
This guide walks you through installing the Axiom AI SDK and authenticating with the Axiom AI SDK CLI so your evaluation results are tracked and attributed correctly in the Axiom Console.
## Prerequisites
* Node.js 18 or later
* An Axiom account with a dataset for storing evaluation traces
* A TypeScript or JavaScript project (evaluations work best with TypeScript frameworks like Vercel AI SDK)
## Install the Axiom AI SDK
Install the `axiom` package in your project:
```bash theme={null}
npm install axiom
```
This package provides:
* The `Eval` function for defining evaluations
* The `Scorer` wrapper for creating custom scorers
* Instrumentation helpers for capturing AI telemetry
* The Axiom AI SDK CLI for running evaluations
## Authenticate with Axiom AI SDK CLI
The Axiom AI SDK includes a dedicated CLI for running evaluations. This CLI is separate from Axiom's main data platform CLI and is focused specifically on AI engineering workflows.
Authenticating with the CLI ensures that evaluation runs are recorded in Axiom and attributed to your user account. This makes it easy to track who ran which experiments and compare results across your team.
### Login with OAuth
Run the login command to authenticate via OAuth:
```bash theme={null}
npx axiom auth login
```
This opens your browser and prompts you to authorize the CLI with your Axiom account. Once authorized, the CLI stores your credentials securely on your machine.
### Check authentication status
Verify you're logged in and see which organization you're using:
```bash theme={null}
npx axiom auth status
```
This displays your current authentication state, including your username and active organization.
### Switch organizations
If you belong to multiple Axiom organizations, switch between them:
```bash theme={null}
npx axiom auth switch
```
This presents a list of organizations you can access and lets you select which one to use for evaluations.
### Logout
To remove stored credentials:
```bash theme={null}
npx axiom auth logout
```
## Authenticate with environment variables
Instead of using OAuth, you can authenticate using environment variables:
```bash theme={null}
export AXIOM_TOKEN="API_TOKEN"
export AXIOM_DATASET="DATASET_NAME"
export AXIOM_ORG_ID="ORGANIZATION_ID"
export AXIOM_URL="AXIOM_DOMAIN"
```
Replace `API_TOKEN` with the Axiom API token you have generated. For added security, store the API token in an environment variable.
Replace `DATASET_NAME` with the name of the Axiom dataset where you send your data.
Replace `ORGANIZATION_ID` with the organization ID.
Replace `AXIOM_DOMAIN` with the base domain of your edge deployment. For more information, see [Edge deployments](/reference/edge-deployments).
## Create the Axiom configuration file
Create an `axiom.config.ts` file in your project root to configure how evaluations run:
```ts axiom.config.ts theme={null}
import { defineConfig } from 'axiom/ai/config';
import { setupAppInstrumentation } from './src/instrumentation';
export default defineConfig({
eval: {
// Glob patterns for evaluation files
include: ['**/*.eval.{ts,js,mts,mjs,cts,cjs}'],
exclude: ['**/node_modules/**'],
// Instrumentation hook - called before running evals
instrumentation: ({ url, token, dataset, orgId }) =>
setupAppInstrumentation({ url, token, dataset, orgId }),
// Timeout for individual test cases (milliseconds)
timeoutMs: 60_000,
},
});
```
### Set up instrumentation
The `instrumentation` function initializes OpenTelemetry tracing so evaluation runs are captured as traces in Axiom. Create a file to set up your tracing provider:
```ts src/instrumentation.ts theme={null}
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node';
import { initAxiomAI } from 'axiom/ai';
import type { AxiomEvalInstrumentationHook } from 'axiom/ai/config';
let provider: NodeTracerProvider | undefined;
export const setupAppInstrumentation: AxiomEvalInstrumentationHook = async (options) => {
if (provider) {
return { provider };
}
const exporter = new OTLPTraceExporter({
url: `${options.url}/v1/traces`,
headers: {
Authorization: `Bearer ${options.token}`,
'X-Axiom-Dataset': options.dataset,
...(options.orgId ? { 'X-AXIOM-ORG-ID': options.orgId } : {}),
},
});
provider = new NodeTracerProvider({
spanProcessors: [new BatchSpanProcessor(exporter)],
});
provider.register();
// Initialize Axiom AI instrumentation
initAxiomAI({
tracer: provider.getTracer('axiom-ai'),
});
return { provider };
};
```
If you’re already using Axiom for observability in your application, you can reuse your existing tracing setup. The evaluation framework integrates seamlessly with your existing instrumentation.
## Recommended folder structure
Organize your evaluation files for easy discovery and maintenance:
```
your-project/
├── axiom.config.ts
├── src/
│ ├── lib/
│ │ ├── app-scope.ts
│ │ └── capabilities/
│ │ └── capability-name/
│ │ ├── prompts.ts
│ │ ├── schemas.ts
│ │ └── evaluations/
│ │ └── eval-name.eval.ts
│ ├── instrumentation.ts
│ └── ...
```
Name evaluation files with the `.eval.ts` extension so they’re automatically discovered by the CLI.
## Verify your setup
Test that everything is configured correctly:
```bash theme={null}
npx axiom eval --list
```
This lists all evaluation files found in your project without running them. If you see your evaluation files listed, you're ready to start writing evaluations.
## What's next?
Now that you're set up, learn how to write your first evaluation in [Write evaluations](/ai-engineering/evaluate/write-evaluations).
# Write evaluations
Source: https://axiom.co/docs/ai-engineering/evaluate/write-evaluations
Learn how to create evaluation functions with collections, tasks, and scorers.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
An evaluation is a test suite for your AI capability. It runs your capability against a collection of test cases and scores the results using scorers. This page explains how to write evaluation functions using Axiom's `Eval` API.
## Anatomy of an evaluation
The `Eval` function defines a complete test suite for your capability. Here’s the basic structure:
```ts theme={null}
import { Eval, Scorer } from 'axiom/ai/evals';
Eval('evaluation-name', {
data: [/* test cases */],
task: async ({ input }) => {/* run capability */},
scorers: [/* scoring functions */],
metadata: {/* optional metadata */},
});
```
### Key parameters
* **`data`**: An array of test cases, or a function that returns an array of test cases. Each test case has an `input` (what you send to your capability) and an `expected` output (the ground truth).
* **`task`**: An async function that executes your capability for a given input and returns the output.
* **`scorers`**: An array of scorer functions that evaluate the output against the expected result.
* **`metadata`**: Optional metadata like a description or tags.
## Creating collections
The `data` parameter defines your collection of test cases. Start with a small set of examples and grow it over time as you discover edge cases.
### Inline collections
For small collections, define test cases directly in the evaluation:
```ts theme={null}
Eval('classify-sentiment', {
data: [
{
input: { text: 'I love this product!' },
expected: { sentiment: 'positive' },
},
{
input: { text: 'This is terrible.' },
expected: { sentiment: 'negative' },
},
{
input: { text: 'It works as expected.' },
expected: { sentiment: 'neutral' },
},
],
// ... rest of eval
});
```
### External collections
For larger collections, load test cases from external files or databases:
```ts theme={null}
import { readFile } from 'fs/promises';
Eval('classify-sentiment', {
data: async () => {
const content = await readFile('./test-cases/sentiment.json', 'utf-8');
return JSON.parse(content);
},
// ... rest of eval
});
```
We recommend storing collections in version control alongside your code. This makes it easy to track how your test suite evolves and ensures evaluations are reproducible.
## Defining the task
The `task` function executes your AI capability for each test case. It receives the `input` from the test case and should return the output your capability produces.
```ts theme={null}
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
async function classifySentiment(text: string) {
const result = await generateText({
model: wrapAISDKModel(openai('gpt-4o-mini')),
prompt: `Classify the sentiment of this text as positive, negative, or neutral: "${text}"`,
});
return { sentiment: result.text };
}
Eval('classify-sentiment', {
data: [/* ... */],
task: async ({ input }) => {
return await classifySentiment(input.text);
},
scorers: [/* ... */],
});
```
The task function should generally be the same code you use in your actual capability. This ensures your evaluations accurately reflect real-world behavior.
## Creating scorers
Scorers evaluate your capability's output. They receive the `input`, `output`, and `expected` values, and return a score (a number between 0-1, or boolean).
### Custom scorers
Create custom scorers using the `Scorer` wrapper:
```ts theme={null}
import { Scorer } from 'axiom/ai/evals';
const ExactMatchScorer = Scorer(
'exact-match',
({ output, expected }) => {
return output.sentiment === expected.sentiment ? true : false;
}
);
```
Scorers can return just a score, or an object with a score and metadata:
```ts theme={null}
const DetailedScorer = Scorer(
'detailed-match',
({ output, expected }) => {
const match = output.sentiment === expected.sentiment;
return {
score: match ? true : false,
metadata: {
outputValue: output.sentiment,
expectedValue: expected.sentiment,
matched: match,
},
};
}
);
```
### Using autoevals
The [`autoevals`](https://github.com/braintrustdata/autoevals) library provides prebuilt scorers for common tasks:
```bash theme={null}
npm install autoevals
```
```ts theme={null}
import { Scorer } from 'axiom/ai/evals';
import { Levenshtein, FactualityScorer } from 'autoevals';
// Wrap autoevals scorers with Axiom's Scorer
const LevenshteinScorer = Scorer(
'levenshtein',
({ output, expected }) => {
return Levenshtein({ output: output.text, expected: expected.text });
}
);
const FactualityCheck = Scorer(
'factuality',
async ({ output, expected }) => {
return await FactualityScorer({
output: output.text,
expected: expected.text,
});
}
);
```
Use multiple scorers to evaluate different aspects of your capability. For example, check both exact accuracy and semantic similarity to get a complete picture of performance.
## Complete example
Here's a complete evaluation for a support ticket classification system:
```ts src/lib/capabilities/classify-ticket/evaluations/spam-classification.eval.ts expandable theme={null}
import { Eval, Scorer } from 'axiom/ai/evals';
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
import { z } from 'zod';
// The capability function
async function classifyTicket({
subject,
content
}: {
subject?: string;
content: string
}) {
const result = await generateObject({
model: wrapAISDKModel(openai('gpt-4o-mini')),
messages: [
{
role: 'system',
content: `You are a customer support engineer. Classify tickets as:
spam, question, feature_request, or bug_report.`,
},
{
role: 'user',
content: subject ? `Subject: ${subject}\n\n${content}` : content,
},
],
schema: z.object({
category: z.enum(['spam', 'question', 'feature_request', 'bug_report']),
confidence: z.number().min(0).max(1),
}),
});
return result.object;
}
// Custom scorer for category matching
const CategoryScorer = Scorer(
'category-match',
({ output, expected }) => {
return output.category === expected.category ? true : false;
}
);
// Custom scorer for high-confidence predictions
const ConfidenceScorer = Scorer(
'high-confidence',
({ output }) => {
return output.confidence >= 0.8 ? true : false;
}
);
// Define the evaluation
Eval('spam-classification', {
data: [
{
input: {
subject: "Congratulations! You've Won!",
content: 'Claim your $500 gift card now!',
},
expected: {
category: 'spam',
},
},
{
input: {
subject: 'How do I reset my password?',
content: 'I forgot my password and need help resetting it.',
},
expected: {
category: 'question',
},
},
{
input: {
subject: 'Feature request: Dark mode',
content: 'Would love to see a dark mode option in the app.',
},
expected: {
category: 'feature_request',
},
},
{
input: {
subject: 'App crashes on startup',
content: 'The app crashes immediately when I try to open it.',
},
expected: {
category: 'bug_report',
},
},
],
task: async ({ input }) => {
return await classifyTicket(input);
},
scorers: [CategoryScorer, ConfidenceScorer],
metadata: {
description: 'Classify support tickets into categories',
},
});
```
## File naming conventions
Name your evaluation files with the `.eval.ts` extension so they're automatically discovered by the Axiom CLI:
```
src/
└── lib/
└── capabilities/
└── classify-ticket/
└── evaluations/
├── spam-classification.eval.ts
├── category-accuracy.eval.ts
└── edge-cases.eval.ts
```
The CLI will find all files matching `**/*.eval.{ts,js,mts,mjs,cts,cjs}` based on your `axiom.config.ts` configuration.
## What's next?
* To parameterize your capabilities and run experiments, see [Flags and experiments](/ai-engineering/evaluate/flags-experiments).
* To run evaluations using the CLI, see [Run evaluations](/ai-engineering/evaluate/run-evaluations).
# Iterate
Source: https://axiom.co/docs/ai-engineering/iterate
Run a systematic improvement loop to continuously enhance your AI capabilities based on production data and evaluation results.
export const Badge = ({children}) => {
return
{children}
;
};
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
The Iterate stage closes the loop in AI engineering. By analyzing production performance, validating changes through evaluation, and deploying improvements with confidence, you create a continuous cycle of data-driven enhancement.
## The improvement loop
Successful AI engineering follows a systematic pattern:
1. **Analyze production** - Identify what needs improvement
2. **Create test cases** - Turn failures into ground truth examples
3. **Experiment with changes** - Test variations using flags
4. **Validate improvements** - Run evaluations to confirm progress
5. **Deploy with confidence** - Ship changes backed by data
6. **Repeat** - New production data feeds the next iteration
## Identify what to improve
Start by understanding how your capability performs in production. The Axiom Console provides multiple signals to help you prioritize:
### Production traces
Review traces in the [Observe](/ai-engineering/observe) section to find:
* Real-world inputs that caused failures or low-quality outputs
* High-cost or high-latency interactions that need optimization
* Unexpected tool calls or reasoning paths
* Edge cases your evaluations didn’t cover
Filter to AI spans and examine the full interaction path, including model choices, token usage, and intermediate steps.
### User feedback Coming soon
User feedback capture is coming soon. [Contact Axiom](https://www.axiom.co/contact) to join the design partner program.
User feedback provides direct signal about which interactions matter most to your customers. Axiom's AI SDK will include lightweight functions to capture both explicit and implicit feedback as timestamped event data:
* **Explicit feedback** includes direct user signals like thumbs up, thumbs down, and comments on AI-generated outputs.
* **Implicit feedback** captures behavioral signals like copying generated text, regenerating responses, or abandoning interactions.
Because user feedback is stored as timestamped events linked to specific AI runs, you can easily correlate feedback with traces to understand exactly what went wrong and prioritize high-value failures over edge cases that rarely occur.
### Domain expert annotations Coming soon
Annotation workflows are coming soon. [Contact Axiom](https://www.axiom.co/contact) to join the design partner program.
Axiom will provide a seamless workflow for domain experts to review production traces and identify patterns in AI capability failures. The Console will surface traces that warrant attention, such as those with negative user feedback or anomalous behavior, and provide an interface for reviewing conversations and annotating failure modes.
Annotations can be categorized into failures modes to guide prioritization. For example:
* **Critical failures** - Complete breakdowns like API outages, unhandled exceptions, or timeout errors
* **Quality degradation** - Declining accuracy scores, increased hallucinations, or off-topic responses
* **Coverage gaps** - Out-of-distribution inputs the system wasn’t designed to handle, like unexpected languages or domains
* **User dissatisfaction** - Negative feedback on outputs that technically succeeded but didn’t meet user needs
This structured analysis helps teams coordinate improvement efforts, prioritize which failure modes to address first, and track patterns over time.
## Create test cases from production
Once you've identified high-priority failures, turn them into test cases for your evaluation collections. Organizations typically maintain multiple collections for different scenarios, failure modes, or capability variants:
```ts theme={null}
const newTestCases = [
{
input: {
// Real production input that failed
subject: 'Refund request for order #12345',
content: 'I need a refund because the product arrived damaged.'
},
expected: {
category: 'refund_request',
priority: 'high'
},
},
];
```
## Experiment with changes
Use [flags](/ai-engineering/evaluate/flags-experiments) to test different approaches without changing your code:
```bash theme={null}
# Test with a more capable model
axiom eval ticket-classification --flag.model=gpt-4o
# Try a different temperature
axiom eval ticket-classification --flag.temperature=0.3
# Experiment with prompt variations
axiom eval ticket-classification --flag.promptStrategy=detailed
```
Run multiple experiments to understand the tradeoffs between accuracy, cost, and latency.
## Validate improvements
Before deploying any change, validate it against your full test collection using baseline comparison:
```bash theme={null}
# Run baseline evaluation
axiom eval ticket-classification
# Note the run ID: run_abc123xyz
# Make your changes (update prompt, adjust config, etc.)
# Run again with baseline comparison
axiom eval ticket-classification --baseline run_abc123xyz
```
The Console shows you exactly how your changes impact:
* **Accuracy**: Did scores improve or regress?
* **Cost**: Is it more or less expensive?
* **Latency**: Is it faster or slower?
Only deploy changes that show clear improvements without unacceptable tradeoffs.
## Deploy with confidence
Once your evaluations confirm an improvement, deploy the change to production. Because you've validated against ground truth data, you can ship with confidence that the new version handles both existing cases and the new failures you discovered.
After deployment, return to the **Observe** stage to monitor performance and identify the next opportunity for improvement.
## Best practices
* **Build your collections over time.** Your evaluation collections should grow as you discover new failure modes. Each production issue that makes it through is an opportunity to strengthen your test coverage.
* **Track improvements systematically.** Use baseline comparisons for every change. This creates a clear history of how your capability has improved and prevents regressions.
* **Prioritize high-impact changes.** Focus on failures that affect many users or high-value interactions. Not every edge case deserves immediate attention.
* **Experiment before committing.** Flags let you test multiple approaches quickly. Run several experiments to understand the solution space before making code changes.
* **Close the loop.** The improvement cycle never ends. Each deployment generates new production data that reveals the next set of improvements to make.
## What's next?
To learn more about the evaluation framework that powers this improvement loop, see [Evaluate](/ai-engineering/evaluate/overview).
To understand how to capture rich telemetry from production, see [Observe](/ai-engineering/observe).
# Measure
Source: https://axiom.co/docs/ai-engineering/measure
Learn how to measure the quality of your AI capabilities by running evaluations against ground truth data.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
export const Badge = ({children}) => {
return
{children}
;
};
The evaluation framework described here is in active development. Axiom is working with design partners to shape what’s built. [Contact Axiom](https://www.axiom.co/contact) to get early access and join a focused group of teams shaping these tools.
The **Measure** stage is where you quantify the quality and effectiveness of your AI capability. Instead of relying on anecdotal checks, this stage uses a systematic process called an eval to score your capability’s performance against a known set of correct examples (ground truth). This provides a data-driven benchmark to ensure a capability is ready for production and to track its quality over time.
Evaluations (evals) are systematic tests that measure how well your AI features perform. Instead of manually testing AI outputs, evals automatically run your AI code against test datasets and score the results using custom metrics. This lets you catch regressions, compare different approaches, and confidently improve your AI features over time.
## Prerequisites
Follow the [Quickstart](/ai-engineering/quickstart):
* To run evals within the context of an existing AI app, follow the instrumentation setup in the [Quickstart](/ai-engineering/quickstart).
* To run evals without an existing AI app, skip the part in the Quickstart about instrumentalising your app.
## Write evalulation function
The `Eval` function provides a simple, declarative way to define a test suite for your capability directly in your codebase.
The key parameters of the `Eval` function:
* `data`: An async function that returns your collection of `{ input, expected }` pairs, which serve as your ground truth.
* `task`: The function that executes your AI capability, taking an `input` and producing an `output`.
* `scorers`: An array of scorer functions that score the `output` against the `expected` value.
* `metadata`: Optional metadata for the evaluation, such as a description.
The example below creates an evaluation for a support ticket classification system in the file `/src/evals/ticket-classification.eval.ts`.
```ts /src/evals/ticket-classification.eval.ts expandable theme={null}
import { Eval, Scorer } from 'axiom/ai/evals';
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
import { flag, pickFlags } from '../lib/app-scope';
import { z } from 'zod';
// The function you want to evaluate
async function classifyTicket({ subject, content }: { subject?: string; content: string }) {
const model = flag('ticketClassification.model');
const result = await generateObject({
model: wrapAISDKModel(openai(model)),
messages: [
{
role: 'system',
content: `You are a customer support engineer classifying tickets as: spam, question, feature_request, or bug_report.
If spam, return a polite auto-close message. Otherwise, say a team member will respond shortly.`,
},
{
role: 'user',
content: subject ? `Subject: ${subject}\n\n${content}` : content,
},
],
schema: z.object({
category: z.enum(['spam', 'question', 'feature_request', 'bug_report']),
response: z.string()
}),
});
return result.object;
}
// Custom exact-match scorer that returns score and metadata
const ExactMatchScorer = Scorer(
'Exact-Match',
({ output, expected }: { output: { response: string }; expected: { response: string } }) => {
const normalizedOutput = output.response.trim().toLowerCase();
const normalizedExpected = expected.response.trim().toLowerCase();
return {
score: normalizedOutput === normalizedExpected,
metadata: {
details: 'A scorer that checks for exact match',
},
};
});
}
);
// Custom spam classification scorer
const SpamClassificationScorer = Scorer(
"Spam-Classification",
({ output, expected }: {
output: { category: string };
expected: { category: string };
}) => {
const isSpam = (item: { category: string }) => item.category === "spam";
return isSpam(output) === isSpam(expected) ? 1 : 0;
}
);
// Define the evaluation
Eval('spam-classification', {
// Specify which flags this eval uses
configFlags: pickFlags('ticketClassification'),
// Test data with input/expected pairs
data: [
{
input: {
subject: "Congratulations! You've Been Selected for an Exclusive Reward",
content: 'Claim your $500 gift card now by clicking this link!',
},
expected: {
category: 'spam',
response: "We're sorry, but your message has been automatically closed.",
},
},
{
input: {
subject: 'FREE CA$H',
content: 'BUY NOW ON WWW.BEST-DEALS.COM!',
},
expected: {
category: 'spam',
response: "We're sorry, but your message has been automatically closed.",
},
},
],
// The task to run for each test case
task: async ({ input }) => {
return await classifyTicket(input);
},
// Scorers to measure performance
scorers: [SpamClassificationScorer, ExactMatchScorer],
// Optional metadata
metadata: {
description: 'Classify support tickets as spam or not spam',
},
});
```
## Set up flags
Create the file `src/lib/app-scope.ts`:
```ts /src/lib/app-scope.ts theme={null}
import { createAppScope } from 'axiom/ai';
import { z } from 'zod';
export const flagSchema = z.object({
ticketClassification: z.object({
model: z.string().default('gpt-4o-mini'),
}),
});
const { flag, pickFlags } = createAppScope({ flagSchema });
export { flag, pickFlags };
```
## Run evaluations
To run your evaluation suites from your terminal, [install the Axiom CLI](/reference/cli) and use the following commands.
| Description | Command |
| ------------------------------------ | ---------------------------------------------------- |
| Run all evals | `axiom eval` |
| Run specific eval file | `axiom eval src/evals/ticket-classification.eval.ts` |
| Run evals matching a glob pattern | `axiom eval "**/*spam*.eval.ts"` |
| Run eval by name | `axiom eval "spam-classification"` |
| List available evals without running | `axiom eval --list` |
## Analyze results in Console
When you run an eval, Axiom AI SDK captures a detailed OpenTelemetry trace for the entire run. This includes parent spans for the evaluation suite and child spans for each individual test case, task execution, and scorer result. Axiom enriches the traces with `eval.*` attributes, allowing you to deeply analyze results in the Axiom Console.
The results of evals:
* Pass/fail status for each test case
* Scores from each scorer
* Comparison to baseline (if available)
* Links to view detailed traces in Axiom
The Console features leaderboards and comparison views to track score progression across different versions of a capability, helping you verify that your changes are leading to measurable improvements.
## Additional configuration options
### Custom scorers
A scorer is a function that scores a capability’s output. Scorers receive the `input`, the generated `output`, and the `expected` value, and return a score.
The example above uses two custom scorers. Scorers can return metadata alongside the score.
You can use the [`autoevals` library](https://github.com/braintrustdata/autoevals) instead of custom scorers. `autoevals` provides prebuilt scorers for common tasks like semantic similarity, factual correctness, and text matching.
### Run experiments
Flags let you parameterize your AI behavior (like model choice or prompting strategies) and run experiments with different configurations. They’re type-safe via Zod schemas, and you can override them at runtime.
The example above uses the `ticketClassification` flag to test different language models. Flags have a default value that you can override at runtime in one of the following ways:
* Override flags directly when you run the eval:
```bash theme={null}
axiom eval --flag.ticketClassification.model=gpt-4o
```
* Alternatively, specify the flag overrides in a JSON file.
```json experiment.json theme={null}
{
"ticketClassification": {
"model": "gpt-4o"
}
}
```
And then specify the JSON file as the value of the `flags-config` parameter when you run the eval:
```bash theme={null}
axiom eval --flags-config=experiment.json
```
## What’s next?
A capability is ready to be deployed when it meets your quality benchmarks. After deployment, the next steps can be the following:
* **Baseline comparisons**: Run evals multiple times to track regression over time.
* **Experiment with flags**: Test different models or strategies using flag overrides.
* **Advanced scorers**: Build custom scorers for domain-specific metrics.
* **CI/CD integration**: Add `axiom eval` to your CI pipeline to catch regressions.
The next step is to monitor your capability’s performance with real-world traffic. To learn more about this step of the AI engineering workflow, see [Observe](/ai-engineering/observe).
# Overview of Observe stage
Source: https://axiom.co/docs/ai-engineering/observe
Learn how to observe your deployed AI capabilities in production using Axiom AI SDK to capture telemetry.
export const Badge = ({children}) => {
return
{children}
;
};
In the Observe stage of the AI engineering lifecycle, the focus is on understanding how your deployed generative AI capabilities perform in the real world. After creating and evaluating a capability, observing its production behavior is crucial for identifying unexpected issues, tracking costs, and gathering the data needed for future improvements.
## Instrument your app
Axiom offers the following approaches to capture generative AI telemetry:
| Instrumentation approach | Language support | Characteristics |
| :------------------------------------------------------------------- | :--------------- | :----------------------------------------------------------- |
| [Axiom AI SDK](/ai-engineering/observe/axiom-ai-sdk-instrumentation) | TypeScript | Quick setup.
Minimal code changes. |
| [Manual](/ai-engineering/observe/manual-instrumentation) | Any | More involved setup.
Full control over instrumentation. |
**Instrumentation with Axiom AI SDK** is the right choice for you if you have a TypeScript app and you want the SDK to capture and send traces with the correct semantic conventions.
**Manual instrumentation** is the right choice for you if you want to use your own tooling or if you use a language other than TypeScript. You need to instrument your app manually to emit traces compatible with Axiom’s AI engineering features.
Both approaches emit identical attributes. This means that all the telemetry analysis features work the same way.
## Visualize traces in Console
Visualizing and making sense of this telemetry data is a core part of the Axiom Console experience:
* A dedicated **AI traces waterfall view** visualizes single and multi-step LLM workflows, with clear input/output inspection at each stage.
* A pre-built **GenAI OTel dashboard** automatically appears for any dataset receiving AI telemetry. It features elements for tracking cost per invocation, time-to-first-token, call counts by model, and error rates.
### Access AI traces waterfall view
1. Click the Query tab.
2. Create an APL query about your GenAI dataset. For example:
```kusto theme={null}
['gen-ai-traces']
| where ['attributes.gen_ai.operation.name'] == "chat"
```
3. In the list of trace IDs, click the trace you want to explore.
4. Explore how spans within the trace are related to each other in the waterfall view. To only display AI spans, click **AI spans** in the top left.
### Access GenAI dashboard
Axiom automatically creates the GenAI dashboard if the field `attributes.gen_ai.operation.name` is present in your data.
To access the GenAI dashboard:
1. Click the Dashboards tab.
2. Click the dashboard **Generative AI Overview (DATASET\_NAME)** where `DATASET_NAME` is the name of your GenAI dataset.
The GenAI dashboard provides you with important insights about your GenAI app such as:
* Vitals about requests, broken down by operation, capability, and step.
* Token usage and cost analysis
* Error analysis
* Comparison of performance and reliability of different AI models
## What’s next?
After capturing and analyzing production telemetry, use these insights to improve your capability. Learn more in [Iterate](/ai-engineering/iterate).
# Instrumentation with Axiom AI SDK
Source: https://axiom.co/docs/ai-engineering/observe/axiom-ai-sdk-instrumentation
Learn how to instrument your TypeScript app with Axiom AI SDK for AI telemetry capture.
export const Badge = ({children}) => {
return
{children}
;
};
This page explains how to set up instrumentation in your TypeScript generative AI app using Axiom AI SDK.
Axiom AI SDK is an open-source project and welcomes your contributions. For more information, see the [GitHub repository](https://github.com/axiomhq/ai).
Alternatively, [instrument your app manually](/ai-engineering/observe/manual-instrumentation). For more information on instrumentation approaches, see [Introduction to Observe](/ai-engineering/observe).
## Prerequisite
Follow the procedure in [Quickstart](/ai-engineering/quickstart) to set up Axiom AI SDK in your TypeScript project.
## Instrument AI SDK calls
Axiom AI SDK provides helper functions for [Vercel AI SDK](https://ai-sdk.dev/docs) to wrap your existing AI model client. The `wrapAISDKModel` function takes an existing AI model object and returns an instrumented version that automatically generates trace data for every call.
Choose one of the following common Vercel AI SDK providers. For the full list of providers, see the [Vercel documentation](https://ai-sdk.dev/providers/ai-sdk-providers).
1. Run the following in your terminal to install the Vercel AI SDK and the OpenAI provider.
```sh theme={null}
npm i ai @ai-sdk/openai
```
2. Create the file `src/shared/openai.ts` with the following content:
```ts /src/shared/openai.ts theme={null}
import { createOpenAI } from '@ai-sdk/openai';
import { wrapAISDKModel } from 'axiom/ai';
const openaiProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Wrap the model to enable automatic tracing
export const gpt4o = wrapAISDKModel(openaiProvider('gpt-4o'));
export const gpt4oMini = wrapAISDKModel(openaiProvider('gpt-4o-mini'));
```
1. Run the following in your terminal to install the Vercel AI SDK and the Anthropic provider.
```sh theme={null}
npm i ai @ai-sdk/anthropic
```
2. Create the file `src/shared/anthropic.ts` with the following content:
```ts /src/shared/anthropic.ts theme={null}
import { createAnthropic } from '@ai-sdk/anthropic';
import { wrapAISDKModel } from 'axiom/ai';
const anthropicProvider = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Wrap the model to enable automatic tracing
export const claude35Sonnet = wrapAISDKModel(anthropicProvider('claude-3-5-sonnet-20241022'));
export const claude35Haiku = wrapAISDKModel(anthropicProvider('claude-3-5-haiku-20241022'));
```
1. Run the following in your terminal to install the Vercel AI SDK and the Gemini provider.
```sh theme={null}
npm i ai @ai-sdk/google
```
2. Create the file `src/shared/gemini.ts` with the following content:
```ts /src/shared/gemini.ts theme={null}
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { wrapAISDKModel } from 'axiom/ai';
const geminiProvider = createGoogleGenerativeAI({
apiKey: process.env.GEMINI_API_KEY,
});
// Wrap the model to enable automatic tracing
export const gemini20Flash = wrapAISDKModel(geminiProvider('gemini-2.0-flash-exp'));
export const gemini15Pro = wrapAISDKModel(geminiProvider('gemini-1.5-pro'));
```
1. Run the following in your terminal to install the Vercel AI SDK and the Grok provider.
```sh theme={null}
npm i ai @ai-sdk/xai
```
2. Create the file `src/shared/grok.ts` with the following content:
```ts /src/shared/grok.ts theme={null}
import { createXai } from '@ai-sdk/xai';
import { wrapAISDKModel } from 'axiom/ai';
const grokProvider = createXai({
apiKey: process.env.XAI_API_KEY,
});
// Wrap the model to enable automatic tracing
export const grokBeta = wrapAISDKModel(grokProvider('grok-beta'));
export const grok2Mini = wrapAISDKModel(grokProvider('grok-2-mini'));
```
To instrument calls without a Vercel AI SDK provider, use the generic Vercel AI Gateway provider.
To instrument calls without a Vercel AI SDK provider, use the generic Vercel AI Gateway provider. For more information, see the [Vercel documentation](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway).
1. Run the following in your terminal to install the Vercel AI SDK:
```sh theme={null}
npm i ai
```
2. Create the file `src/shared/openai.ts` with the following content:
```ts /src/shared/openai.ts theme={null}
import { createGateway } from 'ai';
import { wrapAISDKModel } from 'axiom';
const gateway = createGateway({
apiKey: process.env.OPENAI_API_KEY,
});
// Wrap the model to enable automatic tracing
export const gpt4o = wrapAISDKModel(gateway('openai/gpt-4o'));
```
The rest of the page explains how to work with OpenAI. The process is similar for other LLMs.
## Add context
The `withSpan` function allows you to add crucial business context to your traces. It creates a parent span around your LLM call and attaches metadata about the `capability` and `step` that you execute.
```ts /src/app/page.tsx theme={null}
import { withSpan } from 'axiom/ai';
import { generateText } from 'ai';
import { gpt4o } from '@/shared/openai';
export default async function Page() {
const userId = 123;
// Use withSpan to define the capability and step
const res = await withSpan({ capability: 'get_capital', step: 'generate_answer' }, (span) => {
// You have access to the OTel span to add custom attributes
span.setAttribute('user_id', userId);
return generateText({
model: gpt4o, // Use the wrapped model
messages: [
{
role: 'user',
content: 'What is the capital of Spain?',
},
],
});
});
return {res.text}
;
}
```
## Instrument tool calls
For many AI capabilities, the LLM call is only part of the story. If your capability uses tools to interact with external data or services, observing the performance and outcome of those tools is critical. Axiom AI SDK provides the `wrapTool` and `wrapTools` functions to automatically instrument your Vercel AI SDK tool definitions.
The `wrapTool` helper takes your tool’s name and its definition and returns an instrumented version. This wrapper creates a dedicated child span for every tool execution, capturing its arguments, output, and any errors.
```ts /src/app/generate-text/page.tsx theme={null}
import { tool } from 'ai';
import { z } from 'zod';
import { wrapTool } from 'axiom/ai';
import { generateText } from 'ai';
import { gpt4o } from '@/shared/openai';
// In your generateText call, provide wrapped tools
const { text, toolResults } = await generateText({
model: gpt4o,
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'How do I get from Paris to Berlin?' },
],
tools: {
// Wrap each tool with its name
findDirections: wrapTool(
'findDirections', // The name of the tool
tool({
description: 'Find directions to a location',
inputSchema: z.object({
from: z.string(),
to: z.string(),
}),
execute: async (params) => {
// Your tool logic here...
return { directions: `To get from ${params.from} to ${params.to}, use a teleporter.` };
},
})
)
}
});
```
## Complete example
Example of how all three instrumentation functions work together in a single, real-world example:
```ts /src/app/page.tsx expandable theme={null}
import { withSpan, wrapAISDKModel, wrapTool } from 'axiom/ai';
import { generateText, tool } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { z } from 'zod';
// 1. Create and wrap the AI model client
const openaiProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const gpt4o = wrapAISDKModel(openaiProvider('gpt-4o'));
// 2. Define and wrap your tool(s)
const findDirectionsTool = wrapTool(
'findDirections', // The tool name must be passed to the wrapper
tool({
description: 'Find directions to a location',
inputSchema: z.object({ from: z.string(), to: z.string() }),
execute: async ({ from, to }) => ({
directions: `To get from ${from} to ${to}, use a teleporter.`,
}),
})
);
// 3. In your application logic, use `withSpan` to add context
// and call the AI model with your wrapped tools.
export default async function Page() {
const userId = 123;
const { text } = await withSpan({ capability: 'get_directions', step: 'generate_ai_response' }, async (span) => {
// You have access to the OTel span to add custom attributes
span.setAttribute('user_id', userId);
return generateText({
model: gpt4o, // Use the wrapped model
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'How do I get from Paris to Berlin?' },
],
tools: {
findDirections: findDirectionsTool, // Use the wrapped tool
},
});
});
return {text}
;
}
```
This demonstrates the three key steps to rich observability:
1. **`wrapAISDKModel`**: Automatically captures telemetry for the LLM provider call
2. **`wrapTool`**: Instruments the tool execution with detailed spans
3. **`withSpan`**: Creates a parent span that ties everything together under a business capability
## What’s next?
After sending traces to Axiom:
* View your [traces](/query-data/traces) in Console
* Set up [monitors and alerts](/monitor-data/monitors) based on your AI telemetry data
* Learn about [developing AI features](/ai-engineering/create) with confidence using Axiom
# Generative AI attributes
Source: https://axiom.co/docs/ai-engineering/observe/gen-ai-attributes
Understand the key attributes that your generative AI app sends to Axiom.
After you instrument your app, every LLM call sends a detailed span to your Axiom dataset. The spans are enriched with standardized `gen_ai.*` attributes that make your AI interactions easy to query and analyze.
Key attributes include the following:
* `gen_ai.capability.name`: The high-level capability name you defined in `withSpan`.
* `gen_ai.step.name`: The specific step within the capability.
* `gen_ai.request.model`: The model requested for the completion.
* `gen_ai.response.model`: The model that actually fulfilled the request.
* `gen_ai.usage.input_tokens`: The number of tokens in the prompt.
* `gen_ai.usage.output_tokens`: The number of tokens in the generated response.
* `gen_ai.prompt`: The full, rendered prompt or message history sent to the model (as a JSON string).
* `gen_ai.completion`: The full response from the model, including tool calls (as a JSON string).
* `gen_ai.response.finish_reasons`: The reason the model stopped generating tokens. For example: `stop`, `tool-calls`.
* `gen_ai.tool.name`: The name of the executed tool.
* `gen_ai.tool.arguments`: The arguments passed to the tool (as a JSON string).
* `gen_ai.tool.message`: The result returned by the tool (as a JSON string).
## What’s next?
After capturing and analyzing production telemetry:
* [Visualize traces](/query-data/traces) in Console.
* Use the new insights to [iterate](/ai-engineering/iterate) on your capability.
# Manual instrumentation
Source: https://axiom.co/docs/ai-engineering/observe/manual-instrumentation
Learn how to manually instrument your generative AI apps using OpenTelemetry tooling.
Manually instrumenting your generative AI apps using language-agnostic OpenTelemetry tooling gives you full control over your instrumentation while ensuring compatibility with Axiom’s AI engineering features.
Alternatively, instrument your app with Axiom AI SDK. For more information on instrumentation approaches, see [Introduction to Observe](/ai-engineering/observe).
For more information on sending OpenTelemetry data to Axiom, see [Send OpenTelemetry data to Axiom](/send-data/opentelemetry#send-opentelemetry-data-to-axiom) for examples in multiple languages.
## Required attributes
Axiom’s conventions for AI spans are based on version 1.37 of the [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/) for generative client AI spans.
Axiom requires the following attributes in your data to properly recognize your spans:
* `gen_ai.operation.name` identifies AI spans. It’s also a required attribute in the OpenTelemetry specification.
* `gen_ai.capability.name` provides context about the specific capability being used within the AI operation.
* `gen_ai.step.name` allows you to break down the AI operation into individual steps for more granular tracking.
### `gen_ai.operation.name`
Axiom currently provides custom UI for the following operations related to the `gen_ai.operation.name` attribute:
* `chat`: Chat completion
* `execute_tool`: Tool execution
Other possible values include:
* `generate_content`: Multimodal content generation
* `embeddings`: Vector embeddings
* `create_agent`: Create AI agents
* `invoke_agent`: Invoke existing agents
* `text_completion`: Text completion. This is a legacy value and has been deprecated by OpenAI and many other providers.
For more information, see the OpenTelemetry documentation on [GenAI Attributes](https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/#gen-ai-operation-name).
## Recommended attributes
Axiom recommends the following attributes to get the most out of Axiom’s AI telemetry features:
### `axiom.gen_ai` attributes
* `axiom.gen_ai.schema_url`: Schema URL for the Axiom AI conventions. For example: `https://axiom.co/ai/schemas/0.0.2`
* `axiom.gen_ai.sdk.name`: Name of the SDK. For example: `my-ai-instrumentation-sdk`
* `axiom.gen_ai.sdk.version`: Version of the SDK. For example: `1.2.3`
### Chat spans
| Attribute | Type | Required | Description |
| :------------------------------- | :------------------------ | :------------- | :---------------------------------------------- |
| `gen_ai.provider.name` | string | Required | Provider (openai, anthropic, aws.bedrock, etc.) |
| `gen_ai.request.model` | string | When available | Model requested (gpt-4, claude-3, etc.) |
| `gen_ai.response.model` | string | When available | Model that fulfilled the request |
| `gen_ai.input.messages` | Messages\[] (stringified) | Recommended | Input conversation history |
| `gen_ai.output.messages` | Messages\[] (stringified) | Recommended | Model response messages |
| `gen_ai.usage.input_tokens` | integer | Recommended | Input token count |
| `gen_ai.usage.output_tokens` | integer | Recommended | Output token count |
| `gen_ai.request.choice_count` | integer | When >1 | Number of completion choices requested |
| `gen_ai.response.id` | string | Recommended | Unique response identifier |
| `gen_ai.response.finish_reasons` | string\[] | Recommended | Why generation stopped |
| `gen_ai.conversation.id` | string | When available | Conversation/session identifier |
### Tool spans
For tool operations (`execute_tool`), include these additional attributes:
| Attribute | Type | Required | Description |
| :------------------------ | :----- | :------------- | :----------------------------------------- |
| `gen_ai.tool.name` | string | Required | Name of the executed tool |
| `gen_ai.tool.call.id` | string | When available | Tool call identifier |
| `gen_ai.tool.type` | string | When available | Tool type (function, extension, datastore) |
| `gen_ai.tool.description` | string | When available | Tool description |
| `gen_ai.tool.arguments` | string | When available | Tool arguments |
| `gen_ai.tool.message` | string | When available | Tool message |
For more information, see [GenAI Attributes](https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/#genai-attributes).
### Agent spans
For agent operations (`create_agent`, `invoke_agent`), include these additional attributes:
| Attribute | Type | Required | Description |
| :------------------------- | :----- | :------------- | :------------------------------ |
| `gen_ai.agent.id` | string | When available | Unique agent identifier |
| `gen_ai.agent.name` | string | When available | Human-readable agent name |
| `gen_ai.agent.description` | string | When available | Agent description/purpose |
| `gen_ai.conversation.id` | string | When available | Conversation/session identifier |
## Span naming
Ensure span names follow the OpenTelemetry conventions for generative AI spans. For example, the suggested span names for common values of `gen_ai.operation.name` are the following:
* `chat {gen_ai.request.model}`
* `execute_tool {gen_ai.tool.name}`
* `embeddings {gen_ai.request.model}`
* `generate_content {gen_ai.request.model}`
* `text_completion {gen_ai.request.model}`
* `create_agent {gen_ai.agent.name}`
* `invoke_agent {gen_ai.agent.name}`
For more information, see the OpenTelemetry documentation on span naming:
* [AI spans](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#spans)
* [Agent spans](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/)
* [Tool span](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#execute-tool-span)
## Messages
Messages support four different roles, each with specific content formats. They follow OpenTelemetry’s structured format:
### System messages
System messages are messages that the system adds to set the behavior of the assistant. They typically contain instructions or context for the AI model.
```json theme={null}
{
"role": "system",
"parts": [
{"type": "text", "content": "You are a helpful assistant"}
]
}
```
### User messages
User messages are messages that users send to the AI model. They typically contain questions, commands, or other input from the user.
```json theme={null}
{
"role": "user",
"parts": [
{"type": "text", "content": "Weather in Paris?"}
]
}
```
### Assistant messages
Assistant messages are messages that the AI model sends back to the user. They typically contain responses, answers, or other output from the model.
```json theme={null}
{
"role": "assistant",
"parts": [
{"type": "text", "content": "Hi there!"},
{"type": "tool_call", "id": "call_123", "name": "get_weather", "arguments": {"location": "Paris"}}
],
"finish_reason": "stop"
}
```
### Tool messages
Tool messages are messages that contain the results of tool calls made by the AI model. They typically contain the output or response from the tool.
```json theme={null}
{
"role": "tool",
"parts": [
{"type": "tool_call_response", "id": "call_123", "response": "rainy, 57°F"}
]
}
```
### Content part types
* `text`: Text content with `content` field
* `tool_call`: Tool invocation with `id`, `name`, `arguments`
* `tool_call_response`: Tool result with `id`, `response`
For more information, see the OpenTelemetry documentation:
* [Recording content on attributes](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#recording-content-on-attributes)
* JSON schema for [inputs](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-input-messages.json) and [outputs](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-output-messages.json)
## Example trace structure
### Chat completion
Example of a properly structured chat completion trace:
```typescript TypeScript expandable theme={null}
import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('my-app');
// Create a span for the AI operation
return tracer.startActiveSpan('chat gpt-4', {
kind: SpanKind.CLIENT
}, (span) => {
try {
// (Your AI operation logic here...)
span.setAttributes({
// Set operation name
'gen_ai.operation.name': 'chat',
// Set capability and step
'gen_ai.capability.name': 'customer_support',
'gen_ai.step.name': 'respond_to_greeting',
// Set other attributes
'gen_ai.provider.name': 'openai',
'gen_ai.request.model': 'gpt-4',
'gen_ai.response.model': 'gpt-4',
'gen_ai.usage.input_tokens': 150,
'gen_ai.usage.output_tokens': 75,
'gen_ai.input.messages': JSON.stringify([
{ role: 'user', parts: [{ type: 'text', content: 'Hello, how are you?' }] }
]),
'gen_ai.output.messages': JSON.stringify([
{ role: 'assistant', parts: [{ type: 'text', content: 'I\'m doing well, thank you!' }], finish_reason: 'stop' }
])
});
return /* your result */;
} catch (error) {
span.recordException(error);
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
throw error; // rethrow if you want upstream to see it
} finally {
span.end();
}
});
```
```python Python expandable theme={null}
from opentelemetry import trace
from opentelemetry.trace import SpanKind
import json
tracer = trace.get_tracer("my-app")
# Create a span for the AI operation
with tracer.start_as_current_span("chat gpt-4", kind=SpanKind.CLIENT) as span:
# (Your AI operation logic here...)
span.set_attributes({
# Set operation name
"gen_ai.operation.name": "chat",
# Set capability and step
"gen_ai.capability.name": "customer_support",
"gen_ai.step.name": "respond_to_greeting",
# Set other attributes
"gen_ai.provider.name": "openai",
"gen_ai.request.model": "gpt-4",
"gen_ai.response.model": "gpt-4",
"gen_ai.usage.input_tokens": 150,
"gen_ai.usage.output_tokens": 75,
"gen_ai.input.messages": json.dumps([
{"role": "user", "parts": [{"type": "text", "content": "Hello, how are you?"}]}
]),
"gen_ai.output.messages": json.dumps([
{"role": "assistant", "parts": [{"type": "text", "content": "I'm doing well, thank you!"}], "finish_reason": "stop"}
])
})
```
### Tool execution
Example of a tool execution within an agent:
```typescript TypeScript expandable theme={null}
import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('my-agent-app');
// Create a span for tool execution
return tracer.startActiveSpan('execute_tool get_weather', {
kind: SpanKind.CLIENT
}, (span) => {
try {
// (Your tool call logic here...)
span.setAttributes({
// Set operation name
'gen_ai.operation.name': 'execute_tool',
// Set capability and step
'gen_ai.capability.name': 'weather_assistance',
'gen_ai.step.name': 'fetch_current_weather',
// Set other attributes
'gen_ai.tool.name': 'get_weather',
'gen_ai.tool.type': 'function',
'gen_ai.tool.call.id': 'call_abc123',
'gen_ai.tool.arguments': JSON.stringify({ location: 'New York', units: 'celsius' }),
'gen_ai.tool.message': JSON.stringify({ temperature: 22, condition: 'sunny', humidity: 65 }),
});
return /* your result */;
} catch (error) {
span.recordException(error);
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
throw error; // rethrow if you want upstream to see it
} finally {
span.end();
}
});
```
```python Python expandable theme={null}
from opentelemetry import trace
from opentelemetry.trace import SpanKind
import json
tracer = trace.get_tracer("my-agent-app")
# Create a span for tool execution
with tracer.start_as_current_span("execute_tool get_weather", kind=SpanKind.CLIENT) as span:
span.set_attributes({
# Set operation name
"gen_ai.operation.name": "execute_tool",
# Set capability and step
"gen_ai.step.name": "fetch_current_weather",
"gen_ai.capability.name": "weather_assistance",
# Set other attributes
"gen_ai.tool.name": "get_weather",
"gen_ai.tool.type": "function",
"gen_ai.tool.call.id": "call_abc123",
"gen_ai.tool.arguments": json.dumps({"location": "New York", "units": "celsius"}),
"gen_ai.tool.message": json.dumps({"temperature": 22, "condition": "sunny", "humidity": 65}),
})
```
## What’s next?
After sending traces with the proper semantic conventions:
* View your [traces](/query-data/traces) in Console
* Set up [monitors and alerts](/monitor-data/monitors) based on your AI telemetry data
* Learn about [developing AI features](/ai-engineering/create) with confidence using Axiom
# AI engineering overview
Source: https://axiom.co/docs/ai-engineering/overview
Build increasingly sophisticated AI capabilities with confidence using systematic evaluation and observability.
export const definitions = {
'Capability': 'A generative AI capability is a system that uses large language models to perform a specific task.',
'Collection': 'A curated set of reference records that are used for the development, testing, and evaluation of a capability.',
'Console': "Axiom’s intuitive web app built for exploration, visualization, and monitoring of your data.",
'Eval': 'The process of testing a capability against a collection of ground truth references using one or more graders.',
'GroundTruth': 'The validated, expert-approved correct output for a given input.',
'EventDB': "Axiom’s robust, cost-effective, and scalable datastore specifically optimized for timestamped event data.",
'OnlineEval': 'The process of applying a grader to a capability’s live production traffic.',
'Scorer': 'A function that measures a capability’s output.'
};
Generative AI development is fundamentally different from traditional software engineering. Its outputs are probabilistic, not deterministic. This variability makes it challenging to guarantee quality and predict failure modes without the right infrastructure.
Axiom's AI engineering capabilities build on the foundational EventDB and Console to provide systematic evaluation and observability for AI systems. Whether you're building single-turn model interactions, multi-step workflows, or complex multi-agent systems, Axiom helps you push boundaries and ship with confidence.
### Axiom AI engineering workflow
Axiom provides a structured, iterative workflow for developing AI capabilities. The workflow builds statistical confidence in systems that aren’t entirely predictable through systematic evaluation and continuous improvement, from initial prototype to production monitoring.
The core stages are:
* **Create**: Prototype your AI capability using any framework. TypeScript-based frameworks like Vercel AI SDK integrate most seamlessly with Axiom's tooling. As you build, gather reference examples to establish ground truth for evaluation.
* **Evaluate**: Systematically test your capability’s performance against reference data using custom scorers to measure accuracy, quality, and cost. Use Axiom's evaluation framework to run experiments with different configurations and track improvements over time.
* **Observe**: Deploy your capability and collect rich telemetry on every LLM call and tool execution. Use online evaluations to monitor for performance degradation and discover edge cases in production.
* **Iterate**: Use insights from production monitoring and evaluation results to refine prompts, augment reference datasets, and improve the capability. Run new evaluations to verify improvements before deploying changes.
### What’s next?
* To understand the key terms used in AI engineering, see the [Concepts](/ai-engineering/concepts) page.
* To start building, follow the [Quickstart](/ai-engineering/quickstart) page.
# Quickstart
Source: https://axiom.co/docs/ai-engineering/quickstart
Install and configure Axiom AI SDK to begin capturing telemetry from your generative AI applications.
Quickly start capturing telemetry data from your generative AI capabilities. After installation and configuration, follow the Axiom AI engineering workflow to create, evaluate, observe, and iterate.
This page explains how to set up instrumentation with Axiom AI SDK. Expand the section below to choose the right instrumentation approach for your needs.
Axiom offers the following approaches to capture generative AI telemetry:
| Instrumentation approach | Language support | Characteristics |
| :------------------------------------------------------------------- | :--------------- | :----------------------------------------------------------- |
| [Axiom AI SDK](/ai-engineering/observe/axiom-ai-sdk-instrumentation) | TypeScript | Quick setup.
Minimal code changes. |
| [Manual](/ai-engineering/observe/manual-instrumentation) | Any | More involved setup.
Full control over instrumentation. |
**Instrumentation with Axiom AI SDK** is the right choice for you if you have a TypeScript app and you want the SDK to capture and send traces with the correct semantic conventions.
**Manual instrumentation** is the right choice for you if you want to use your own tooling or if you use a language other than TypeScript. You need to instrument your app manually to emit traces compatible with Axiom’s AI engineering features.
Both approaches emit identical attributes. This means that all the telemetry analysis features work the same way.
## Prerequisites
* [Create an Axiom account](https://app.axiom.co/register).
* [Create a dataset in Axiom](/reference/datasets#create-dataset) where you send your data.
* [Create an API token in Axiom](/reference/tokens) with permissions to ingest data to the dataset you have created.
## Install
Install Axiom AI SDK into your TypeScript project:
```bash pnpm theme={null}
pnpm i axiom
```
```bash npm theme={null}
npm i axiom
```
```bash yarn theme={null}
yarn add axiom
```
```bash bun theme={null}
bun add axiom
```
The `axiom` package includes the `axiom` command-line interface (CLI) for running evaluations, which you'll use to systematically test and improve your AI capabilities.
## Configure tracer
To send data to Axiom, configure a tracer. For example, use a dedicated instrumentation file and load it before the rest of your app. An example configuration for a Node.js environment:
1. Install dependencies:
```bash pnpm theme={null}
pnpm i \
dotenv \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources \
@opentelemetry/sdk-trace-node \
@opentelemetry/semantic-conventions \
@opentelemetry/api
```
```bash npm theme={null}
npm i \
dotenv \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources \
@opentelemetry/sdk-trace-node \
@opentelemetry/semantic-conventions \
@opentelemetry/api
```
```bash yarn theme={null}
yarn add \
dotenv \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources \
@opentelemetry/sdk-trace-node \
@opentelemetry/semantic-conventions \
@opentelemetry/api
```
```bash bun theme={null}
bun add \
dotenv \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources \
@opentelemetry/sdk-trace-node \
@opentelemetry/semantic-conventions \
@opentelemetry/api
```
2. Create an instrumentation file:
```ts /src/instrumentation.ts expandable theme={null}
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { trace } from "@opentelemetry/api";
import { initAxiomAI, RedactionPolicy } from 'axiom/ai';
import type { AxiomEvalInstrumentationHook } from 'axiom/ai/config';
const tracer = trace.getTracer("my-tracer");
let provider: NodeTracerProvider | undefined;
// Wrap your logic in the AxiomEvalInstrumentationHook function
export const setupAppInstrumentation: AxiomEvalInstrumentationHook = async ({
dataset,
url,
token,
}) => {
if (provider) {
return { provider };
}
if (!dataset || !url || !token) {
throw new Error('Missing environment variables');
}
// Replace the environment variables with the parameters passed to the function
const exporter = new OTLPTraceExporter({
url: `${url}/v1/traces`,
headers: {
Authorization: `Bearer ${token}`,
'X-Axiom-Dataset': dataset,
},
})
// Configure the provider to export traces to your Axiom dataset
provider = new NodeTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'my-app', // Replace with your service name
},
{
// Use the latest schema version
// Info: https://opentelemetry.io/docs/specs/semconv/
schemaUrl: 'https://opentelemetry.io/schemas/1.37.0',
}),
spanProcessor: new BatchSpanProcessor(exporter),
});
// Register the provider
provider.register();
// Initialize Axiom AI SDK with the configured tracer
initAxiomAI({ tracer, redactionPolicy: RedactionPolicy.AxiomDefault });
return { provider };
};
```
For more information on specifying redaction policies, see [Redaction policies](/ai-engineering/redaction-policies).
### Create Axiom configuration file
The Axiom configuration file enables the evaluation framework, allowing you to run systematic tests against your AI capabilities and track improvements over time.
At the root of your project, create the Axiom configuration file `/axiom.config.ts`:
```ts /axiom.config.ts theme={null}
import { defineConfig } from 'axiom/ai/config';
import { setupAppInstrumentation } from './src/instrumentation';
export default defineConfig({
eval: {
url: process.env.AXIOM_URL,
token: process.env.AXIOM_TOKEN,
dataset: process.env.AXIOM_DATASET,
// Optional: customize which files to run
include: ['**/*.eval.{ts,js}'],
// Optional: exclude patterns
exclude: [],
// Optional: timeout for eval execution
timeoutMs: 60_000,
// Optional: instrumentation hook for OpenTelemetry
// (created this in the "Create instrumentation setup" step)
instrumentation: ({ url, token, dataset }) =>
setupAppInstrumentation({ url, token, dataset }),
},
});
```
## Store environment variables
Store environment variables in an `.env` file in the root of your project:
```bash .env theme={null}
AXIOM_URL="AXIOM_DOMAIN"
AXIOM_TOKEN="API_TOKEN"
AXIOM_DATASET="DATASET_NAME"
OPENAI_API_KEY=""
GEMINI_API_KEY=""
XAI_API_KEY=""
ANTHROPIC_API_KEY=""
```
Replace `API_TOKEN` with the Axiom API token you have generated. For added security, store the API token in an environment variable.
Replace `DATASET_NAME` with the name of the Axiom dataset where you send your data.
Replace `AXIOM_DOMAIN` with the base domain of your edge deployment. For more information, see [Edge deployments](/reference/edge-deployments).
Enter the API keys for the LLMs you want to work with.
To run evaluations, you’ll need to authenticate the Axiom CLI. See [Setup and authentication](/ai-engineering/evaluate/setup) for details on using `axiom auth login`.
## What’s next?
* **Build your first capability**: Start prototyping with [Create](/ai-engineering/create).
* **Set up evaluations**: Learn how to systematically test your capabilities with [Evaluate](/ai-engineering/evaluate/overview).
* **Capture production telemetry**: Instrument your AI calls for observability with [Observe](/ai-engineering/observe).
# Redaction policies
Source: https://axiom.co/docs/ai-engineering/redaction-policies
This page explains how to use redaction policies in the Axiom AI SDK to control what data is captured in AI spans
Axiom AI SDK provides flexible redaction policies to control what data is captured in OpenTelemetry spans. This allows you to balance observability needs with privacy and compliance requirements.
## Built-in redaction policies
Axiom AI SDK provides two built-in redaction policies:
| Policy | What gets captured | What gets excluded | When to use |
| :--------------------------------------------------- | :-------------------------------------- | :----------------------------------------------- | :----------------- |
| [AxiomDefault](#axiomdefault-policy) | Full data | – | Full observability |
| [OpenTelemetryDefault](#opentelemetrydefault-policy) | Model metadata, token usage, error info | Prompt text, AI responses, tool args and results | Privacy-first |
If you don’t specify a redaction policy, Axiom AI SDK applies `AxiomDefault`.
To determine which redaction policy fits your needs, see the following comparison:
### AxiomDefault policy
By default, Axiom AI SDK captures all data for maximum observability.
**What gets captured:**
* Full prompt text and AI responses in chat spans
* Complete tool arguments and return values on tool spans
* All standard OpenTelemetry attributes (model name, token usage, etc.)
Capturing full message content increases span size and storage costs.
**When to use:**
* You need full visibility into AI interactions
* Data privacy isn’t a concern
* Debugging complex AI workflows
### OpenTelemetryDefault policy
The OpenTelemetry default policy excludes sensitive content.
**What gets captured:**
* Model metadata (name, provider, version)
* Token usage and performance metrics
* Error information and status codes
**What gets excluded:**
* Prompt text and AI responses
* Tool arguments and return values
**When to use:**
* Handling sensitive or personal data
* Compliance requirements restrict data capture
* You only need performance and error metrics
### What gets captured
To determine which redaction policy fits your needs, see the following examples about what gets captured with each defaultpolicy:
```json theme={null}
{
"gen_ai.operation.name": "chat",
"gen_ai.request.model": "gpt-4o-mini",
"gen_ai.input.messages": "[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Hello, how are you?\"}]}]",
"gen_ai.output.messages": "[{\"role\":\"assistant\",\"content\":\"I'm doing well, thank you for asking!\"}]",
"gen_ai.usage.input_tokens": 12,
"gen_ai.usage.output_tokens": 15,
"gen_ai.usage.total_tokens": 27
}
```
```json theme={null}
{
"gen_ai.tool.name": "weather_lookup",
"gen_ai.tool.description": "Get current weather for a location",
"gen_ai.tool.arguments": "{\"location\":\"San Francisco\",\"units\":\"celsius\"}",
"gen_ai.tool.message": "{\"temperature\":18,\"condition\":\"partly cloudy\"}"
}
```
```json theme={null}
{
"gen_ai.operation.name": "chat",
"gen_ai.request.model": "gpt-4o-mini",
"gen_ai.usage.input_tokens": 12,
"gen_ai.usage.output_tokens": 15,
"gen_ai.usage.total_tokens": 27
}
```
Message content (`gen_ai.input.messages` and `gen_ai.output.messages`) is excluded for privacy.
```json theme={null}
{
"gen_ai.tool.name": "weather_lookup",
"gen_ai.tool.description": "Get current weather for a location"
}
```
Tool arguments and results (`gen_ai.tool.arguments` and `gen_ai.tool.message`) are excluded for privacy.
## Global configuration
Set a default redaction policy for your entire application using `initAxiomAI`:
```ts Full data capture theme={null}
import { trace } from '@opentelemetry/api';
import { initAxiomAI, RedactionPolicy } from 'axiom/ai';
const tracer = trace.getTracer("my-tracer");
initAxiomAI({ tracer, redactionPolicy: RedactionPolicy.AxiomDefault });
```
```ts Privacy-first theme={null}
import { trace } from '@opentelemetry/api';
import { initAxiomAI, RedactionPolicy } from 'axiom/ai';
const tracer = trace.getTracer("my-tracer");
initAxiomAI({ tracer, redactionPolicy: RedactionPolicy.OpenTelemetryDefault });
```
In [Quickstart](/ai-engineering/quickstart), `initAxiomAI` is called in your instrumentation file (`/src/instrumentation.ts`).
## Per-operation override
You can configure different policies for each operation. Axiom resolves redaction policies in the following order (from highest to lowest precedence):
1. Per-operation policy
2. Global policy
3. Default policy
Override the global or default policy for specific operations by passing a `redactionPolicy` to `withSpan`:
```ts theme={null}
import { withSpan, RedactionPolicy } from 'axiom/ai';
import { generateText } from 'ai';
const result = await withSpan(
{ capability: 'customer_support', step: 'handle_sensitive_query' },
async (span) => {
span.setAttribute('user.id', userId);
return generateText({
model: wrappedModel,
prompt: 'Process this sensitive customer data...'
});
},
{ redactionPolicy: RedactionPolicy.OpenTelemetryDefault }
);
```
## Custom redaction policies
Create custom policies by defining an `AxiomAIRedactionPolicy` object:
```ts theme={null}
import { trace } from '@opentelemetry/api';
import { initAxiomAI, AxiomAIRedactionPolicy } from 'axiom/ai';
const tracer = trace.getTracer("my-tracer");
// Custom policy: capture messages but not tool payloads
const customPolicy: AxiomAIRedactionPolicy = {
captureMessageContent: 'full',
mirrorToolPayloadOnToolSpan: false
};
initAxiomAI({ tracer, redactionPolicy: customPolicy });
```
The `AxiomAIRedactionPolicy` object has two properties:
Controls whether prompt and response text is included in chat spans.
* `'full'`: Include complete message content
* `'off'`: Exclude all message content
Controls whether tool arguments and results are duplicated on tool spans.
* `true`: Mirror tool data for easier querying
* `false`: Only capture tool metadata (name, description)
The [built-in policies](#built-in-redaction-policies) configure the `AxiomAIRedactionPolicy` object in the following way:
| Default policy | captureMessageContent | mirrorToolPayloadOnToolSpan |
| :------------------- | :-------------------- | :-------------------------- |
| AxiomDefault | `'full'` | `true` |
| OpenTelemetryDefault | `'off'` | `false` |
## Related documentation
Learn how to instrument your AI applications with Axiom AI SDK
Understand the OpenTelemetry attributes captured by Axiom AI SDK
# arg_max
Source: https://axiom.co/docs/apl/aggregation-function/arg-max
This page explains how to use the arg_max aggregation in APL.
The `arg_max` aggregation in APL helps you identify the row with the maximum value for an expression and return additional fields from that record. Use `arg_max` when you want to determine key details associated with a row where the expression evaluates to the maximum value. If you group your data, `arg_max` finds the row within each group where a particular expression evaluates to the maximum value.
This aggregation is particularly useful in scenarios like the following:
* Pinpoint the slowest HTTP requests in log data and retrieve associated details (like URL, status code, and user agent) for the same row.
* Identify the longest span durations in OpenTelemetry traces with additional context (like span name, trace ID, and attributes) for the same row.
* Highlight the highest severity security alerts in logs along with relevant metadata (such as alert type, source, and timestamp) for the same row.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have an equivalent to `arg_max`. You can use `stats` with a combination of `max` and `by` clauses to evaluate the maximum value of a single numberic field. APL provides a dedicated `arg_max` aggregation that evaluates expressions.
```sql Splunk example theme={null}
| stats max(req_duration_ms) as max_duration by id, uri
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize arg_max(req_duration_ms, id, uri)
```
In ANSI SQL, you typically use a subquery to find the maximum value and then join it back to the original table to retrieve additional fields. APL’s `arg_max` provides a more concise and efficient alternative.
```sql SQL example theme={null}
WITH MaxValues AS (
SELECT id, MAX(req_duration_ms) as max_duration
FROM sample_http_logs
GROUP BY id
)
SELECT logs.id, logs.uri, MaxValues.max_duration
FROM sample_http_logs logs
JOIN MaxValues
ON logs.id = MaxValues.id;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize arg_max(req_duration_ms, id, uri)
```
## Usage
### Syntax
```kusto theme={null}
| summarize arg_max(expression, field1[, field2, ...])
```
### Parameters
| Parameter | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `expression` | The expression whose maximum value determines the selected record. |
| `field1, field2` | The additional fields to retrieve from the record with the maximum numeric value. Use `*` as a wildcard to return all fields from the row. |
The wildcard `*` is useful to return all fields from the row with the maximum value, but it increases query complexity and decreases performance.
### Returns
Returns a row where the expression evaluates to the maximum value for each group (or the entire dataset if no grouping is specified), containing the fields specified in the query.
### Name fields
You can name fields in the parameters of `arg_max` using the syntax `arg_max(name1=expression, name2=field1)`. This specifies the field names that appear in the output.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize arg_max(duration, longestSpan=span_id), arg_max(numEvents=array_length(events), spanWithMostEvents=span_id)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20arg_max\(duration%2C%20longestSpan%3Dspan_id\)%2C%20arg_max\(numEvents%3Darray_length\(events\)%2C%20spanWithMostEvents%3Dspan_id\)%22%7D)
**Output**
| duration | longestSpan | numEvents | spanWithMostEvents |
| -------------- | ---------------- | --------- | ------------------ |
| 5m0.004487127s | 1a9f979bb25f6bbd | 408 | db13ffc3394905b5 |
## Use case examples
Find the slowest path for each HTTP method in the `['sample-http-logs']` dataset.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize arg_max(req_duration_ms, uri) by method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20arg_max\(req_duration_ms%2C%20uri\)%20by%20method%22%7D)
**Output**
| uri | method | req\_duration\_ms |
| ------------- | ------ | ----------------- |
| /home | GET | 1200 |
| /api/products | POST | 2500 |
This query identifies the slowest path for each HTTP method.
Identify the span with the longest duration for each service in the `['otel-demo-traces']` dataset.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize arg_max(duration, span_id, trace_id) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20arg_max\(duration%2C%20span_id%2C%20trace_id\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | span\_id | trace\_id | duration |
| --------------- | -------- | --------- | -------- |
| frontend | span123 | trace456 | 3s |
| checkoutservice | span789 | trace012 | 5s |
This query identifies the span with the longest duration for each service, returning the `span_id`, `trace_id`, and `duration`.
Find the highest status code for each country in the `['sample-http-logs']` dataset.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize arg_max(toint(status), uri) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20arg_max\(toint\(status\)%2C%20uri\)%20by%20%5B'geo.country'%5D%22%7D)
**Output**
| geo.country | uri | status |
| ----------- | ---------- | ------ |
| USA | /admin | 500 |
| Canada | /dashboard | 503 |
This query identifies the URI with the highest status code for each country.
## List of related aggregations
* [arg\_min](/apl/aggregation-function/arg-min): Retrieves the record with the minimum value for a numeric field.
* [max](/apl/aggregation-function/max): Retrieves the maximum value for a numeric field but doesn’t return additional fields.
* [percentile](/apl/aggregation-function/percentile): Provides the value at a specific percentile of a numeric field.
# arg_min
Source: https://axiom.co/docs/apl/aggregation-function/arg-min
This page explains how to use the arg_min aggregation in APL.
The `arg_min` aggregation in APL allows you to identify the row in a dataset where an expression evaluates to the minimum value. You can use this to retrieve other associated fields in the same row, making it particularly useful for pinpointing details about the smallest value in large datasets. If you group your data, `arg_min` finds the row within each group where a particular expression evaluates to the minimum value.
This aggregation is particularly useful in scenarios like the following:
* Pinpoint the shortest HTTP requests in log data and retrieve associated details (like URL, status code, and user agent) for the same row.
* Identify the fastest span durations in OpenTelemetry traces with additional context (like span name, trace ID, and attributes) for the same row.
* Highlight the lowest severity security alerts in logs along with relevant metadata (such as alert type, source, and timestamp) for the same row.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have an equivalent to `arg_min`. You can use `stats` with a combination of `values` and `first` clauses to evaluate the minimum value of a single numberic field. APL provides a dedicated `arg_min` aggregation that evaluates expressions.
```sql Splunk example theme={null}
| stats min(req_duration_ms) as minDuration by id
| where req_duration_ms=minDuration
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize arg_min(req_duration_ms, id, uri)
```
In ANSI SQL, achieving similar functionality often requires a combination of `MIN`, `GROUP BY`, and `JOIN` to retrieve the associated fields. APL's `arg_min` eliminates the need for multiple steps by directly returning the row with the minimum value.
```sql SQL example theme={null}
SELECT id, uri
FROM sample_http_logs
WHERE req_duration_ms = (
SELECT MIN(req_duration_ms)
FROM sample_http_logs
);
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize arg_min(req_duration_ms, id, uri)
```
## Usage
### Syntax
```kusto theme={null}
| summarize arg_min(expression, field1, ..., fieldN)
```
### Parameters
* `expression`: The expression to evaluate for the minimum value.
* `field1, ..., fieldN`: Additional fields to return from the row with the minimum value. Use `*` as a wildcard to return all fields from the row.
The wildcard `*` is useful to return all fields from the row with the minimum value, but it increases query complexity and decreases performance.
### Returns
Returns a row where the expression evaluates to the minimum value for each group (or the entire dataset if no grouping is specified), containing the fields specified in the query.
### Name fields
You can name fields in the parameters of `arg_min` using the syntax `arg_min(name1=expression, name2=field1)`. This specifies the field names that appear in the output.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize arg_min(duration, longestSpan=span_id), arg_min(numEvents=array_length(events), spanWithMostEvents=span_id)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20arg_min\(duration%2C%20longestSpan%3Dspan_id\)%2C%20arg_min\(numEvents%3Darray_length\(events\)%2C%20spanWithMostEvents%3Dspan_id\)%22%7D)
**Output**
| duration | longestSpan | numEvents | spanWithMostEvents |
| -------- | ---------------- | --------- | ------------------ |
| 190ns | 1a9f979bb25f6bbd | 1 | db13ffc3394905b5 |
## Use case examples
You can use `arg_min` to identify the path with the shortest duration and its associated details for each method.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize arg_min(req_duration_ms, uri) by method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20arg_min\(req_duration_ms%2C%20uri\)%20by%20method%22%7D)
**Output**
| req\_duration\_ms | uri | method |
| ----------------- | ---------- | ------ |
| 0.1 | /api/login | POST |
This query identifies the paths with the shortest duration for each method and provides details about the path.
Use `arg_min` to find the span with the shortest duration for each service and retrieve its associated details.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize arg_min(duration, trace_id, span_id, kind) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20arg_min\(duration%2C%20trace_id%2C%20span_id%2C%20kind\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| duration | trace\_id | span\_id | service.name | kind |
| -------- | --------- | -------- | ------------ | ------ |
| 00:00:01 | abc123 | span456 | frontend | server |
This query identifies the span with the shortest duration for each service along with its metadata.
Find the lowest status code for each country in the `['sample-http-logs']` dataset.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize arg_min(toint(status), uri) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20arg_min\(toint\(status\)%2C%20uri\)%20by%20%5B'geo.country'%5D%22%7D)
**Output**
| geo.country | uri | status |
| ----------- | ---------- | ------ |
| USA | /admin | 200 |
| Canada | /dashboard | 201 |
This query identifies the URI with the lowest status code for each country.
## List of related aggregations
* [arg\_max](/apl/aggregation-function/arg-max): Returns the row with the maximum value for a numeric field, useful for finding peak metrics.
* [min](/apl/aggregation-function/min): Returns only the minimum value of a numeric field without additional fields.
* [percentile](/apl/aggregation-function/percentile): Provides the value at a specific percentile of a numeric field.
# avg
Source: https://axiom.co/docs/apl/aggregation-function/avg
This page explains how to use the avg aggregation function in APL.
The `avg` aggregation in APL calculates the average value of a numeric field across a set of records. You can use this aggregation when you need to determine the mean value of numerical data, such as request durations, response times, or other performance metrics. It’s useful in scenarios such as performance analysis, trend identification, and general statistical analysis.
When to use `avg`:
* When you want to analyze the average of numeric values over a specific time range or set of data.
* For comparing trends, like average request duration or latency across HTTP requests.
* To provide insight into system or user performance, such as the average duration of transactions in a service.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `avg` function works similarly, but the syntax differs slightly. Here’s how to write the equivalent query in APL.
```sql Splunk example theme={null}
| stats avg(req_duration_ms) by status
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize avg(req_duration_ms) by status
```
In ANSI SQL, the `avg` aggregation is used similarly, but APL has a different syntax for structuring the query.
```sql SQL example theme={null}
SELECT status, AVG(req_duration_ms)
FROM sample_http_logs
GROUP BY status
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize avg(req_duration_ms) by status
```
## Usage
### Syntax
```kusto theme={null}
summarize avg(ColumnName) [by GroupingColumn]
```
### Parameters
* **ColumnName**: The numeric field you want to calculate the average of.
* **GroupingColumn** (optional): A column to group the results by. If not specified, the average is calculated over all records.
### Returns
* A table with the average value for the specified field, optionally grouped by another column.
## Use case examples
This example calculates the average request duration for HTTP requests, grouped by status.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize avg(req_duration_ms) by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20summarize%20avg\(req_duration_ms\)%20by%20status%22%7D)
**Output**
| status | avg\_req\_duration\_ms |
| ------ | ---------------------- |
| 200 | 350.4 |
| 404 | 150.2 |
This query calculates the average request duration (in milliseconds) for each HTTP status code.
This example calculates the average span duration for each service to analyze performance across services.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize avg(duration) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%5Cn%7C%20summarize%20avg\(duration\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | avg\_duration |
| ------------ | ------------- |
| frontend | 500ms |
| cartservice | 250ms |
This query calculates the average duration of spans for each service.
In security logs, you can calculate the average request duration by country to analyze regional performance trends.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize avg(req_duration_ms) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20summarize%20avg\(req_duration_ms\)%20by%20%5B'geo.country'%5D%22%7D)
**Output**
| geo.country | avg\_req\_duration\_ms |
| ----------- | ---------------------- |
| US | 400.5 |
| DE | 250.3 |
This query calculates the average request duration for each country from where the requests originated.
## List of related aggregations
* [**sum**](/apl/aggregation-function/sum): Use `sum` to calculate the total of a numeric field. This is useful when you want the total of values rather than their average.
* [**count**](/apl/aggregation-function/count): The `count` function returns the total number of records. It’s useful when you want to count occurrences rather than averaging numerical values.
* [**min**](/apl/aggregation-function/min): The `min` function returns the minimum value of a numeric field. Use this when you’re interested in the smallest value in your dataset.
* [**max**](/apl/aggregation-function/max): The `max` function returns the maximum value of a numeric field. This is useful for finding the largest value in the data.
* [**stdev**](/apl/aggregation-function/stdev): This function calculates the standard deviation of a numeric field, providing insight into how spread out the data is around the mean.
# avgif
Source: https://axiom.co/docs/apl/aggregation-function/avgif
This page explains how to use the avgif aggregation function in APL.
The `avgif` aggregation function in APL allows you to calculate the average value of a field, but only for records that satisfy a given condition. This function is particularly useful when you need to perform a filtered aggregation, such as finding the average response time for requests that returned a specific status code or filtering by geographic regions. The `avgif` function is highly valuable in scenarios like log analysis, performance monitoring, and anomaly detection, where focusing on subsets of data can provide more accurate insights.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you achieve similar functionality using the combination of a `stats` function with conditional filtering. In APL, `avgif` provides this filtering inline as part of the aggregation function, which can simplify your queries.
```sql Splunk example theme={null}
| stats avg(req_duration_ms) by id where status = "200"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize avgif(req_duration_ms, status == "200") by id
```
In ANSI SQL, you can use a `CASE` statement inside an `AVG` function to achieve similar behavior. APL simplifies this with `avgif`, allowing you to specify the condition directly.
```sql SQL example theme={null}
SELECT id, AVG(CASE WHEN status = '200' THEN req_duration_ms ELSE NULL END)
FROM sample_http_logs
GROUP BY id
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize avgif(req_duration_ms, status == "200") by id
```
## Usage
### Syntax
```kusto theme={null}
summarize avgif(expr, predicate) by grouping_field
```
### Parameters
* **`expr`**: The field for which you want to calculate the average.
* **`predicate`**: A boolean condition that filters which records are included in the calculation.
* **`grouping_field`**: (Optional) A field by which you want to group the results.
### Returns
The function returns the average of the values from the `expr` field for the records that satisfy the `predicate`. If no records match the condition, the result is `null`.
## Use case examples
In this example, you calculate the average request duration for HTTP status 200 in different cities.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize avgif(req_duration_ms, status == "200") by ['geo.city']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20avgif%28req_duration_ms%2C%20status%20%3D%3D%20%22200%22%29%20by%20%5B%27geo.city%27%5D%22%7D)
**Output**
| geo.city | avg\_req\_duration\_ms |
| -------- | ---------------------- |
| New York | 325 |
| London | 400 |
| Tokyo | 275 |
This query calculates the average request duration (`req_duration_ms`) for HTTP requests that returned a status of 200 (`status == "200"`), grouped by the city where the request originated (`geo.city`).
In this example, you calculate the average span duration for traces that ended with HTTP status 500.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize avgif(duration, status == "500") by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20avgif%28duration%2C%20status%20%3D%3D%20%22500%22%29%20by%20%5B%27service.name%27%5D%22%7D)
**Output**
| service.name | avg\_duration |
| --------------- | ------------- |
| checkoutservice | 500ms |
| frontend | 600ms |
| cartservice | 475ms |
This query calculates the average span duration (`duration`) for traces where the status code is 500 (`status == "500"`), grouped by the service name (`service.name`).
In this example, you calculate the average request duration for failed HTTP requests (status code 400 or higher) by country.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize avgif(req_duration_ms, toint(status) >= 400) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20avgif%28req_duration_ms%2C%20toint%28status%29%20%3E%3D%20400%29%20by%20%5B%27geo.country%27%5D%22%7D)
**Output**
| geo.country | avg\_req\_duration\_ms |
| ----------- | ---------------------- |
| USA | 450 |
| Canada | 500 |
| Germany | 425 |
This query calculates the average request duration (`req_duration_ms`) for failed HTTP requests (`status >= 400`), grouped by the country of origin (`geo.country`).
## List of related aggregations
* [**minif**](/apl/aggregation-function/minif): Returns the minimum value of an expression, filtered by a predicate. Use when you want to find the smallest value for a subset of data.
* [**maxif**](/apl/aggregation-function/maxif): Returns the maximum value of an expression, filtered by a predicate. Use when you are looking for the largest value within specific conditions.
* [**countif**](/apl/aggregation-function/countif): Counts the number of records that match a condition. Use when you want to know how many records meet a specific criterion.
* [**sumif**](/apl/aggregation-function/sumif): Sums the values of a field that match a given condition. Ideal for calculating the total of a subset of data.
# count
Source: https://axiom.co/docs/apl/aggregation-function/count
This page explains how to use the count aggregation function in APL.
The `count` aggregation in APL returns the total number of records in a dataset or the total number of records that match specific criteria. This function is useful when you need to quantify occurrences, such as counting log entries, user actions, or security events.
When to use `count`:
* To count the total number of events in log analysis, such as the number of HTTP requests or errors.
* To monitor system usage, such as the number of transactions or API calls.
* To identify security incidents by counting failed login attempts or suspicious activities.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `count` function works similarly to APL, but the syntax differs slightly.
```sql Splunk example theme={null}
| stats count by status
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize count() by status
```
In ANSI SQL, the `count` function works similarly, but APL uses different syntax for querying.
```sql SQL example theme={null}
SELECT status, COUNT(*)
FROM sample_http_logs
GROUP BY status
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize count() by status
```
## Usage
### Syntax
```kusto theme={null}
summarize count() [by GroupingColumn]
```
### Parameters
* **GroupingColumn** (optional): A column to group the count results by. If not specified, the total number of records across the dataset is returned.
### Returns
* A table with the count of records for the entire dataset or grouped by the specified column.
## Use case examples
In log analysis, you can count the number of HTTP requests by status to get a sense of how many requests result in different HTTP status codes.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize count() by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20summarize%20count\(\)%20by%20status%22%7D)
**Output**
| status | count |
| ------ | ----- |
| 200 | 1500 |
| 404 | 200 |
This query counts the total number of HTTP requests for each status code in the logs.
For OpenTelemetry traces, you can count the total number of spans for each service, which helps you monitor the distribution of requests across services.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize count() by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%5Cn%7C%20summarize%20count\(\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | count |
| ------------ | ----- |
| frontend | 1000 |
| cartservice | 500 |
This query counts the number of spans for each service in the OpenTelemetry traces dataset.
In security logs, you can count the number of requests by country to identify where the majority of traffic or suspicious activity originates.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize count() by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20summarize%20count\(\)%20by%20%5B'geo.country'%5D%22%7D)
**Output**
| geo.country | count |
| ----------- | ----- |
| US | 3000 |
| DE | 500 |
This query counts the number of requests originating from each country.
## List of related aggregations
* [**sum**](/apl/aggregation-function/sum): Use `sum` to calculate the total sum of a numeric field, as opposed to counting the number of records.
* [**avg**](/apl/aggregation-function/avg): The `avg` function calculates the average of a numeric field. Use it when you want to determine the mean value of data instead of the count.
* [**min**](/apl/aggregation-function/min): The `min` function returns the minimum value of a numeric field, helping to identify the smallest value in a dataset.
* [**max**](/apl/aggregation-function/max): The `max` function returns the maximum value of a numeric field, useful for identifying the largest value.
* [**countif**](/apl/aggregation-function/countif): The `countif` function allows you to count only records that meet specific conditions, giving you more flexibility in your count queries.
# countif
Source: https://axiom.co/docs/apl/aggregation-function/countif
This page explains how to use the countif aggregation function in APL.
The `countif` aggregation function in Axiom Processing Language (APL) counts the number of records that meet a specified condition. You can use this aggregation to filter records based on a specific condition and return a count of matching records. This is particularly useful for log analysis, security audits, and tracing events when you need to isolate and count specific data subsets.
Use `countif` when you want to count occurrences of certain conditions, such as HTTP status codes, errors, or actions in telemetry traces.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, conditional counting is typically done using the `eval` function combined with `stats`. APL provides a more streamlined approach with the `countif` function, which performs conditional counting directly.
```sql Splunk example theme={null}
| stats count(eval(status="500")) AS error_count
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize countif(status == '500')
```
In ANSI SQL, conditional counting is achieved by using the `COUNT` function with a `CASE` statement. In APL, `countif` simplifies this process by offering a direct approach to conditional counting.
```sql SQL example theme={null}
SELECT COUNT(CASE WHEN status = '500' THEN 1 END) AS error_count
FROM sample_http_logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize countif(status == '500')
```
## Usage
### Syntax
```kusto theme={null}
countif(condition)
```
### Parameters
* **condition**: A boolean expression that filters the records based on a condition. Only records where the condition evaluates to `true` are counted.
### Returns
The function returns the number of records that match the specified condition.
## Use case examples
In log analysis, you might want to count how many HTTP requests returned a 500 status code to detect server errors.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize countif(status == '500')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20countif\(status%20%3D%3D%20'500'\)%22%7D)
**Output**
| count\_errors |
| ------------- |
| 72 |
This query counts the number of HTTP requests with a `500` status, helping you identify how many server errors occurred.
In OpenTelemetry traces, you might want to count how many requests were initiated by the client service kind.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize countif(kind == 'client')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20countif\(kind%20%3D%3D%20'client'\)%22%7D)
**Output**
| count\_client\_kind |
| ------------------- |
| 345 |
This query counts how many requests were initiated by the `client` service kind, providing insight into the volume of client-side traffic.
In security logs, you might want to count how many HTTP requests originated from a specific city, such as New York.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize countif(['geo.city'] == 'New York')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20countif\(%5B'geo.city'%5D%20%3D%3D%20'New%20York'\)%22%7D)
**Output**
| count\_nyc\_requests |
| -------------------- |
| 87 |
This query counts how many HTTP requests originated from New York, which can help detect traffic from a particular location for security analysis.
## List of related aggregations
* [**count**](/apl/aggregation-function/count): Counts all records in a dataset without applying a condition. Use this when you need the total count of records, regardless of any specific condition.
* [**sumif**](/apl/aggregation-function/sumif): Adds up the values of a field for records that meet a specific condition. Use `sumif` when you want to sum values based on a filter.
* [**dcountif**](/apl/aggregation-function/dcountif): Counts distinct values of a field for records that meet a condition. This is helpful when you need to count unique occurrences.
* [**avgif**](/apl/aggregation-function/avgif): Calculates the average value of a field for records that match a condition, useful for performance monitoring.
* [**maxif**](/apl/aggregation-function/maxif): Returns the maximum value of a field for records that meet a condition. Use this when you want to find the highest value in filtered data.
# dcount
Source: https://axiom.co/docs/apl/aggregation-function/dcount
This page explains how to use the dcount aggregation function in APL.
The `dcount` aggregation function in Axiom Processing Language (APL) counts the distinct values in a column. This function is essential when you need to know the number of unique values, such as counting distinct users, unique requests, or distinct error codes in log files.
Use `dcount` for analyzing datasets where it’s important to identify the number of distinct occurrences, such as unique IP addresses in security logs, unique user IDs in app logs, or unique trace IDs in OpenTelemetry traces.
The `dcount` aggregation in APL is a statistical aggregation that returns estimated results. The estimation comes with the benefit of speed at the expense of accuracy. This means that `dcount` is fast and light on resources even on a large or high-cardinality dataset, but it doesn’t provide precise results.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you can count distinct values using the `dc` function within the `stats` command. In APL, the `dcount` function offers similar functionality.
```sql Splunk example theme={null}
| stats dc(user_id) AS distinct_users
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize dcount(id)
```
In ANSI SQL, distinct counting is typically done using `COUNT` with the `DISTINCT` keyword. In APL, `dcount` provides a direct and efficient way to count distinct values.
```sql SQL example theme={null}
SELECT COUNT(DISTINCT user_id) AS distinct_users
FROM sample_http_logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize dcount(id)
```
## Usage
### Syntax
```kusto theme={null}
dcount(column_name)
```
### Parameters
* **column\_name**: The name of the column for which you want to count distinct values.
### Returns
The function returns the count of distinct values found in the specified column.
## Use case examples
In log analysis, you can count how many distinct users accessed the service.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize dcount(id)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20dcount\(id\)%22%7D)
**Output**
| distinct\_users |
| --------------- |
| 45 |
This query counts the distinct values in the `id` field, representing the number of unique users who accessed the system.
In OpenTelemetry traces, you can count how many unique trace IDs are recorded.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize dcount(trace_id)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20dcount\(trace_id\)%22%7D)
**Output**
| distinct\_traces |
| ---------------- |
| 321 |
This query counts the distinct trace IDs in the dataset, helping you determine how many unique traces are being captured.
In security logs, you can count how many distinct IP addresses were logged.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize dcount(['geo.city'])
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20dcount\(%5B'geo.city'%5D\)%22%7D)
**Output**
| distinct\_cities |
| ---------------- |
| 35 |
This query counts the number of distinct cities recorded in the logs, which helps analyze the geographic distribution of traffic.
## List of related aggregations
* [**count**](/apl/aggregation-function/count): Counts the total number of records in the dataset, including duplicates. Use it when you need to know the overall number of records.
* [**countif**](/apl/aggregation-function/countif): Counts records that match a specific condition. Use `countif` when you want to count records based on a filter or condition.
* [**dcountif**](/apl/aggregation-function/dcountif): Counts the distinct values in a column but only for records that meet a condition. It’s useful when you need a filtered distinct count.
* [**sum**](/apl/aggregation-function/sum): Sums the values in a column. Use this when you need to add up values rather than counting distinct occurrences.
* [**avg**](/apl/aggregation-function/avg): Calculates the average value for a column. Use this when you want to find the average of a specific numeric field.
# dcountif
Source: https://axiom.co/docs/apl/aggregation-function/dcountif
This page explains how to use the dcountif aggregation function in APL.
The `dcountif` aggregation function in Axiom Processing Language (APL) counts the distinct values in a column that meet a specific condition. This is useful when you want to filter records and count only the unique occurrences that satisfy a given criterion.
Use `dcountif` in scenarios where you need a distinct count but only for a subset of the data, such as counting unique users from a specific region, unique error codes for specific HTTP statuses, or distinct traces that match a particular service type.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, counting distinct values conditionally is typically achieved using a combination of `eval` and `dc` in the `stats` function. APL simplifies this with the `dcountif` function, which handles both filtering and distinct counting in a single step.
```sql Splunk example theme={null}
| stats dc(eval(status="200")) AS distinct_successful_users
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize dcountif(id, status == '200')
```
In ANSI SQL, conditional distinct counting can be done using a combination of `COUNT(DISTINCT)` and `CASE`. APL's `dcountif` function provides a more concise and readable way to handle conditional distinct counting.
```sql SQL example theme={null}
SELECT COUNT(DISTINCT CASE WHEN status = '200' THEN user_id END) AS distinct_successful_users
FROM sample_http_logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize dcountif(id, status == '200')
```
## Usage
### Syntax
```kusto theme={null}
dcountif(column_name, condition)
```
### Parameters
* **column\_name**: The name of the column for which you want to count distinct values.
* **condition**: A boolean expression that filters the records. Only records that meet the condition will be included in the distinct count.
### Returns
The function returns the count of distinct values that meet the specified condition.
## Use case examples
In log analysis, you might want to count how many distinct users accessed the service and received a successful response (HTTP status 200).
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize dcountif(id, status == '200')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20dcountif\(id%2C%20status%20%3D%3D%20'200'\)%22%7D)
**Output**
| distinct\_successful\_users |
| --------------------------- |
| 50 |
This query counts the distinct users (`id` field) who received a successful HTTP response (status 200), helping you understand how many unique users had successful requests.
In OpenTelemetry traces, you might want to count how many unique trace IDs are recorded for a specific service, such as `frontend`.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize dcountif(trace_id, ['service.name'] == 'frontend')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20dcountif\(trace_id%2C%20%5B'service.name'%5D%20%3D%3D%20'frontend'\)%22%7D)
**Output**
| distinct\_frontend\_traces |
| -------------------------- |
| 123 |
This query counts the number of distinct trace IDs that belong to the `frontend` service, providing insight into the volume of unique traces for that service.
In security logs, you might want to count how many unique IP addresses were logged for requests that resulted in a 403 status (forbidden access).
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize dcountif(['geo.city'], status == '403')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20dcountif\(%5B'geo.city'%5D%2C%20status%20%3D%3D%20'403'\)%22%7D)
**Output**
| distinct\_cities\_forbidden |
| --------------------------- |
| 20 |
This query counts the number of distinct cities (`geo.city` field) where requests resulted in a `403` status, helping you identify potential unauthorized access attempts from different regions.
## List of related aggregations
* [**dcount**](/apl/aggregation-function/dcount): Counts distinct values without applying any condition. Use this when you need to count unique values across the entire dataset.
* [**countif**](/apl/aggregation-function/countif): Counts records that match a specific condition, without focusing on distinct values. Use this when you need to count records based on a filter.
* [**dcountif**](/apl/aggregation-function/dcountif): Use this function to get a distinct count for records that meet a condition. It combines both filtering and distinct counting.
* [**sumif**](/apl/aggregation-function/sumif): Sums values in a column for records that meet a condition. This is useful when you need to sum data points after filtering.
* [**avgif**](/apl/aggregation-function/avgif): Calculates the average value of a column for records that match a condition. Use this when you need to find the average based on a filter.
# histogram
Source: https://axiom.co/docs/apl/aggregation-function/histogram
This page explains how to use the histogram aggregation function in APL.
The `histogram` aggregation in APL allows you to create a histogram that groups numeric values into intervals or “bins.” This is useful for visualizing the distribution of data, such as the frequency of response times, request durations, or other continuous numerical fields. You can use it to analyze patterns and trends in datasets like logs, traces, or metrics. It’s especially helpful when you need to summarize a large volume of data into a digestible form, providing insights on the distribution of values.
The `histogram` aggregation is ideal for identifying peaks, valleys, and outliers in your data. For example, you can analyze the distribution of request durations in web server logs or span durations in OpenTelemetry traces to understand performance bottlenecks.
The `histogram` aggregation in APL is a statistical aggregation that returns estimated results. The estimation comes with the benefit of speed at the expense of accuracy. This means that `histogram` is fast and light on resources even on a large or high-cardinality dataset, but it doesn’t provide precise results.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, a similar operation to APL's `histogram` is the `timechart` or `histogram` command, which groups events into time buckets. However, in APL, the `histogram` function focuses on numeric values, allowing you to control the number of bins precisely.
```splunk Splunk example theme={null}
| stats count by duration | timechart span=10 count
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize count() by histogram(req_duration_ms, 10)
```
In ANSI SQL, you can use the `GROUP BY` clause combined with range calculations to achieve a similar result to APL’s `histogram`. However, APL’s `histogram` function simplifies the process by automatically calculating bin intervals.
```sql SQL example theme={null}
SELECT COUNT(*), FLOOR(req_duration_ms/10)*10 as duration_bin
FROM sample_http_logs
GROUP BY duration_bin
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize count() by histogram(req_duration_ms, 10)
```
## Usage
### Syntax
```kusto theme={null}
histogram(numeric_field, number_of_bins)
```
### Parameters
* `numeric_field`: The numeric field to create a histogram for. For example, request duration or span duration.
* `number_of_bins`: The number of bins (intervals) to use for grouping the numeric values.
### Returns
The `histogram` aggregation returns a table where each row represents a bin, along with the number of occurrences (counts) that fall within each bin.
## Use case examples
You can use the `histogram` aggregation to analyze the distribution of request durations in web server logs.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize histogram(req_duration_ms, 100) by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20histogram\(req_duration_ms%2C%20100\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| req\_duration\_ms\_bin | count |
| ---------------------- | ----- |
| 0 | 50 |
| 100 | 200 |
| 200 | 120 |
This query creates a histogram that groups request durations into bins of 100 milliseconds and shows the count of requests in each bin. It helps you visualize how frequently requests fall within certain duration ranges.
In OpenTelemetry traces, you can use the `histogram` aggregation to analyze the distribution of span durations.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize histogram(duration, 100) by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20histogram\(duration%2C%20100\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| duration\_bin | count |
| ------------- | ----- |
| 0.1s | 30 |
| 0.2s | 120 |
| 0.3s | 50 |
This query groups the span durations into 100ms intervals, making it easier to spot latency issues in your traces.
In security logs, the `histogram` aggregation helps you understand the frequency distribution of request durations to detect anomalies or attacks.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '200'
| summarize histogram(req_duration_ms, 50) by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'200'%20%7C%20summarize%20histogram\(req_duration_ms%2C%2050\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| req\_duration\_ms\_bin | count |
| ---------------------- | ----- |
| 0 | 150 |
| 50 | 400 |
| 100 | 100 |
This query analyzes the request durations for HTTP 200 (Success) responses, helping you identify patterns in security-related events.
## List of related aggregations
* [**percentile**](/apl/aggregation-function/percentile): Use `percentile` when you need to find the specific value below which a percentage of observations fall, which can provide more precise distribution analysis.
* [**avg**](/apl/aggregation-function/avg): Use `avg` for calculating the average value of a numeric field, useful when you are more interested in the central tendency rather than distribution.
* [**sum**](/apl/aggregation-function/sum): The `sum` function adds up the total values in a numeric field, helpful for determining overall totals.
* [**count**](/apl/aggregation-function/count): Use `count` when you need a simple tally of rows or events, often in conjunction with `histogram` for more basic summarization.
# histogramif
Source: https://axiom.co/docs/apl/aggregation-function/histogramif
This page explains how to use the histogramif aggregation function in APL.
The `histogramif` aggregation in APL creates a histogram that groups numeric values into intervals (bins) for rows where a specified condition evaluates to true. This is useful when you want to visualize the distribution of data conditionally—for example, analyzing response times only for successful requests or examining span durations only for specific services.
You use `histogramif` when you need to combine filtering and distribution analysis in a single operation, making your queries more efficient and expressive.
Like the `histogram` aggregation, `histogramif` returns estimated results. The estimation provides speed benefits at the expense of precision, making it fast and resource-efficient even on large or high-cardinality datasets.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically combine filtering with histogram operations using separate commands. APL's `histogramif` consolidates this into a single aggregation, simplifying your query logic.
```sql Splunk example theme={null}
| where status='200'
| timechart span=10 count by duration
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize histogramif(req_duration_ms, 10, status == '200')
```
In ANSI SQL, you combine `WHERE` clauses with `CASE` statements and `GROUP BY` to achieve conditional histograms. APL's `histogramif` provides a more concise syntax for this pattern.
```sql SQL example theme={null}
SELECT FLOOR(req_duration_ms/10)*10 as duration_bin, COUNT(*)
FROM sample_http_logs
WHERE status = '200'
GROUP BY duration_bin
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize histogramif(req_duration_ms, 10, status == '200')
```
## Usage
### Syntax
```kusto theme={null}
histogramif(numeric_field, number_of_bins, condition)
```
### Parameters
| Name | Type | Description |
| ---------------- | ------ | --------------------------------------------------------------------------------------- |
| `numeric_field` | `real` | The numeric field to create a histogram for, such as request duration or response size. |
| `number_of_bins` | `long` | The number of intervals (bins) to use for grouping the numeric values. |
| `condition` | `bool` | A boolean expression that determines which rows to include in the histogram. |
### Returns
A table where each row represents a bin, along with the number of occurrences (counts) that fall within each bin for rows where the condition evaluates to true.
## Use case examples
Use `histogramif` to analyze the distribution of request durations only for successful HTTP requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize histogramif(req_duration_ms, 100, status == '200') by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20histogramif\(req_duration_ms%2C%20100%2C%20status%20%3D%3D%20'200'\)%20by%20bin_auto\(_time\)%22%7D)
This query creates a histogram of request durations grouped into 100ms bins, but only includes requests with a `200` HTTP status code. This helps you understand the performance characteristics of successful requests.
Use `histogramif` to analyze span duration distributions for specific services in your OpenTelemetry traces.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize histogramif(duration, 50, ['service.name'] == 'frontend') by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20histogramif\(duration%2C%2050%2C%20%5B'service.name'%5D%20%3D%3D%20'frontend'\)%20by%20bin_auto\(_time\)%22%7D)
This query groups span durations into 50ms intervals, focusing only on the frontend service. This helps you identify performance patterns specific to that service.
Use `histogramif` to examine the distribution of request durations for specific geographic regions, helping you identify regional performance issues or anomalies.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize histogramif(req_duration_ms, 50, ['geo.country'] == 'United States') by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20histogramif\(req_duration_ms%2C%2050%2C%20%5B'geo.country'%5D%20%3D%3D%20'United%20States'\)%20by%20bin_auto\(_time\)%22%7D)
This query analyzes request duration patterns for traffic originating from the US, helping you identify geographic performance variations or security patterns.
## List of related aggregations
* [histogram](/apl/aggregation-function/histogram): Use `histogram` when you want to create a distribution without a condition. Use `histogramif` when you need to filter rows first.
* [countif](/apl/aggregation-function/countif): Use `countif` for simple conditional counting. Use `histogramif` when you need distribution analysis with a condition.
* [avgif](/apl/aggregation-function/avgif): Use `avgif` when you need the average of values matching a condition. Use `histogramif` for full distribution analysis.
* [percentileif](/apl/aggregation-function/percentileif): Use `percentileif` to find specific percentile values conditionally. Use `histogramif` for a complete distribution overview.
* [sumif](/apl/aggregation-function/sumif): Use `sumif` for conditional sums. Use `histogramif` when you need to understand the distribution of conditional values.
# make_list
Source: https://axiom.co/docs/apl/aggregation-function/make-list
This page explains how to use the make_list aggregation function in APL.
The `make_list` aggregation function in Axiom Processing Language (APL) collects all values from a specified column into a dynamic array for each group of rows in a dataset. This aggregation is particularly useful when you want to consolidate multiple values from distinct rows into a single grouped result.
For example, if you have multiple log entries for a particular user, you can use `make_list` to gather all request URIs accessed by that user into a single list. You can also apply `make_list` to various contexts, such as trace aggregation, log analysis, or security monitoring, where collating related events into a compact form is needed.
Key uses of `make_list`:
* Consolidating values from multiple rows into a list per group.
* Summarizing activity (for example, list all HTTP requests by a user).
* Generating traces or timelines from distributed logs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `make_list` equivalent is `values` or `mvlist`, which gathers multiple values into a multivalue field. In APL, `make_list` behaves similarly by collecting values from rows into a dynamic array.
```sql Splunk example theme={null}
index=logs | stats values(uri) by user
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize uris=make_list(uri) by id
```
In ANSI SQL, the `make_list` function is similar to `ARRAY_AGG`, which aggregates column values into an array for each group. In APL, `make_list` performs the same role, grouping the column values into a dynamic array.
```sql SQL example theme={null}
SELECT ARRAY_AGG(uri) AS uris FROM sample_http_logs GROUP BY id;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize uris=make_list(uri) by id
```
## Usage
### Syntax
```kusto theme={null}
make_list(column)
```
### Parameters
* `column`: The name of the column to collect into a list.
### Returns
The `make_list` function returns a dynamic array that contains all values of the specified column for each group of rows.
## Use case examples
In log analysis, `make_list` is useful for collecting all URIs a user has accessed in a session. This can help in identifying browsing patterns or tracking user activity.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize uris=make_list(uri) by id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20uris%3Dmake_list%28uri%29%20by%20id%22%7D)
**Output**
| id | uris |
| ------- | --------------------------------- |
| user123 | \[‘/home’, ‘/profile’, ‘/cart’] |
| user456 | \[‘/search’, ‘/checkout’, ‘/pay’] |
This query collects all URIs accessed by each user, providing a compact view of user activity in the logs.
In OpenTelemetry traces, `make_list` can help in gathering the list of services involved in a trace by consolidating all service names related to a trace ID.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize services=make_list(['service.name']) by trace_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20services%3Dmake_list%28%5B%27service.name%27%5D%29%20by%20trace_id%22%7D)
**Output**
| trace\_id | services |
| --------- | ----------------------------------------------- |
| trace\_a | \[‘frontend’, ‘cartservice’, ‘checkoutservice’] |
| trace\_b | \[‘productcatalogservice’, ‘loadgenerator’] |
This query aggregates all service names associated with a particular trace, helping trace spans across different services.
In security logs, `make_list` is useful for collecting all IPs or cities from where a user has initiated requests, aiding in detecting anomalies or patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize cities=make_list(['geo.city']) by id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20cities%3Dmake_list%28%5B%27geo.city%27%5D%29%20by%20id%22%7D)
**Output**
| id | cities |
| ------- | ---------------------------- |
| user123 | \[‘New York’, ‘Los Angeles’] |
| user456 | \[‘Berlin’, ‘London’] |
This query collects the cities from which each user has made HTTP requests, useful for geographical analysis or anomaly detection.
## List of related aggregations
* [**make\_set**](/apl/aggregation-function/make-set): Similar to `make_list`, but only unique values are collected in the set. Use `make_set` when duplicates aren’t relevant.
* [**count**](/apl/aggregation-function/count): Returns the count of rows in each group. Use this instead of `make_list` when you’re interested in row totals rather than individual values.
* [**max**](/apl/aggregation-function/max): Aggregates values by returning the maximum value from each group. Useful for numeric comparison across rows.
* [**dcount**](/apl/aggregation-function/dcount): Returns the distinct count of values for each group. Use this when you need unique value counts instead of listing them.
# make_list_if
Source: https://axiom.co/docs/apl/aggregation-function/make-list-if
This page explains how to use the make_list_if aggregation function in APL.
The `make_list_if` aggregation function in APL creates a list of values from a given field, conditioned on a Boolean expression. This function is useful when you need to gather values from a column that meet specific criteria into a single array. By using `make_list_if`, you can aggregate data based on dynamic conditions, making it easier to perform detailed analysis.
This aggregation is ideal in scenarios where filtering at the aggregation level is required, such as gathering only the successful requests or collecting trace spans of a specific service in OpenTelemetry data. It’s particularly useful when analyzing logs, tracing information, or security events, where conditional aggregation is essential for understanding trends or identifying issues.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you would typically use the `eval` and `stats` commands to create conditional lists. In APL, the `make_list_if` function serves a similar purpose by allowing you to aggregate data into a list based on a condition.
```sql Splunk example theme={null}
| stats list(field) as field_list by condition
```
```kusto APL equivalent theme={null}
summarize make_list_if(field, condition)
```
In ANSI SQL, conditional aggregation often involves the use of `CASE` statements combined with aggregation functions such as `ARRAY_AGG`. In APL, `make_list_if` directly applies a condition to the aggregation.
```sql SQL example theme={null}
SELECT ARRAY_AGG(CASE WHEN condition THEN field END) FROM table
```
```kusto APL equivalent theme={null}
summarize make_list_if(field, condition)
```
## Usage
### Syntax
```kusto theme={null}
summarize make_list_if(expression, condition)
```
### Parameters
* `expression`: The field or expression whose values will be included in the list.
* `condition`: A Boolean condition that determines which values from `expression` are included in the result.
### Returns
The function returns an array containing all values from `expression` that meet the specified `condition`.
## Use case examples
In this example, we will gather a list of request durations for successful HTTP requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_list_if(req_duration_ms, status == '200') by id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D+%7C+summarize+make_list_if%28req_duration_ms%2C+status+%3D%3D+%27200%27%29+by+id%22%7D)
**Output**
| id | req\_duration\_ms\_list |
| --- | ----------------------- |
| 123 | \[100, 150, 200] |
| 456 | \[300, 350, 400] |
This query aggregates request durations for HTTP requests that returned a status of ‘200’ for each user ID.
Here, we will aggregate the span durations for `cartservice` where the status code indicates success.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize make_list_if(duration, status_code == '200' and ['service.name'] == 'cartservice') by trace_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D+%7C+summarize+make_list_if%28duration%2C+status_code+%3D%3D+%27200%27+and+%5B%27service.name%27%5D+%3D%3D+%27cartservice%27%29+by+trace_id%22%7D)
**Output**
| trace\_id | duration\_list |
| --------- | --------------------- |
| abc123 | \[00:01:23, 00:01:45] |
| def456 | \[00:02:12, 00:03:15] |
This query collects span durations for successful requests to the `cartservice` by `trace_id`.
In this case, we gather a list of IP addresses from security logs where the HTTP status is `403` (Forbidden) and group them by the country of origin.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_list_if(uri, status == '403') by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D+%7C+summarize+make_list_if%28uri%2C+status+%3D%3D+%27403%27%29+by+%5B%27geo.country%27%5D%22%7D)
**Output**
| geo.country | uri\_list |
| ----------- | ---------------------- |
| USA | \['/login', '/admin'] |
| Canada | \['/admin', '/secure'] |
This query collects a list of URIs that resulted in a `403` error, grouped by the country where the request originated.
## List of related aggregations
* [**make\_list**](/apl/aggregation-function/make-list): Aggregates all values into a list without any conditions. Use `make_list` when you don’t need to filter the values based on a condition.
* [**countif**](/apl/aggregation-function/countif): Counts the number of records that satisfy a specific condition. Use `countif` when you need a count of occurrences rather than a list of values.
* [**avgif**](/apl/aggregation-function/avgif): Calculates the average of values that meet a specified condition. Use `avgif` for numerical aggregations where you want a conditional average instead of a list.
# make_set
Source: https://axiom.co/docs/apl/aggregation-function/make-set
This page explains how to use the make_set aggregation function in APL.
The `make_set` aggregation in APL (Axiom Processing Language) is used to collect unique values from a specific column into an array. It’s useful when you want to reduce your data by grouping it and then retrieving all unique values for each group. This aggregation is valuable for tasks such as grouping logs, traces, or events by a common attribute and retrieving the unique values of a specific field for further analysis.
You can use `make_set` when you need to collect non-repeating values across rows within a group, such as finding all the unique HTTP methods in web server logs or unique trace IDs in telemetry data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `values` function is similar to `make_set` in APL. The main difference is that while `values` returns all non-null values, `make_set` specifically returns only unique values and stores them in an array.
```sql Splunk example theme={null}
| stats values(method) by id
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize make_set(method) by id
```
In ANSI SQL, the `GROUP_CONCAT` or `ARRAY_AGG(DISTINCT)` functions are commonly used to aggregate unique values in a column. `make_set` in APL works similarly by aggregating distinct values from a specific column into an array, but it offers better performance for large datasets.
```sql SQL example theme={null}
SELECT id, ARRAY_AGG(DISTINCT method)
FROM sample_http_logs
GROUP BY id;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize make_set(method) by id
```
## Usage
### Syntax
```kusto theme={null}
make_set(column, [limit])
```
### Parameters
* `column`: The column from which unique values are aggregated.
* `limit`: (Optional) The maximum number of unique values to return. Defaults to 128 if not specified.
### Returns
An array of unique values from the specified column.
## Use case examples
In this use case, you want to collect all unique HTTP methods used by each user in the log data.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_set(method) by id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D+%7C+summarize+make_set%28method%29+by+id%22%7D)
**Output**
| id | make\_set\_method |
| ------- | ----------------- |
| user123 | \['GET', 'POST'] |
| user456 | \['GET'] |
This query groups the log entries by `id` and returns all unique HTTP methods used by each user.
In this use case, you want to gather the unique service names involved in a trace.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize make_set(['service.name']) by trace_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D+%7C+summarize+make_set%28%5B%27service.name%27%5D%29+by+trace_id%22%7D)
**Output**
| trace\_id | make\_set\_service.name |
| --------- | -------------------------------- |
| traceA | \['frontend', 'checkoutservice'] |
| traceB | \['cartservice'] |
This query groups the telemetry data by `trace_id` and collects the unique services involved in each trace.
In this use case, you want to collect all unique HTTP status codes for each country where the requests originated.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_set(status) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D+%7C+summarize+make_set%28status%29+by+%5B%27geo.country%27%5D%22%7D)
**Output**
| geo.country | make\_set\_status |
| ----------- | ----------------- |
| USA | \['200', '404'] |
| UK | \['200'] |
This query collects all unique HTTP status codes returned for each country from which requests were made.
## List of related aggregations
* [**make\_list**](/apl/aggregation-function/make-list): Similar to `make_set`, but returns all values, including duplicates, in a list. Use `make_list` if you want to preserve duplicates.
* [**count**](/apl/aggregation-function/count): Counts the number of records in each group. Use `count` when you need the total count rather than the unique values.
* [**dcount**](/apl/aggregation-function/dcount): Returns the distinct count of values in a column. Use `dcount` when you need the number of unique values, rather than an array of them.
* [**max**](/apl/aggregation-function/max): Finds the maximum value in a group. Use `max` when you are interested in the largest value rather than collecting values.
# make_set_if
Source: https://axiom.co/docs/apl/aggregation-function/make-set-if
This page explains how to use the make_set_if aggregation function in APL.
The `make_set_if` aggregation function in APL allows you to create a set of distinct values from a column based on a condition. You can use this function to aggregate values that meet specific criteria, helping you filter and reduce data to unique entries while applying a conditional filter. This is especially useful when analyzing large datasets to extract relevant, distinct information without duplicates.
You can use `make_set_if` in scenarios where you need to aggregate conditional data points, such as log analysis, tracing information, or security logs, to summarize distinct occurrences based on particular conditions.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you may use `values` with a `where` condition to achieve similar functionality to `make_set_if`. However, in APL, the `make_set_if` function is explicitly designed to create a distinct set of values based on a conditional filter within the aggregation step itself.
```sql Splunk example theme={null}
| stats values(field) by another_field where condition
```
```kusto APL equivalent theme={null}
summarize make_set_if(field, condition) by another_field
```
In ANSI SQL, you would typically use `GROUP BY` in combination with conditional aggregation, such as using `CASE WHEN` inside aggregate functions. In APL, the `make_set_if` function directly aggregates distinct values conditionally without requiring a `CASE WHEN`.
```sql SQL example theme={null}
SELECT DISTINCT CASE WHEN condition THEN field END
FROM table
GROUP BY another_field
```
```kusto APL equivalent theme={null}
summarize make_set_if(field, condition) by another_field
```
## Usage
### Syntax
```kusto theme={null}
make_set_if(column, predicate, [max_size])
```
### Parameters
* `column`: The column from which distinct values will be aggregated.
* `predicate`: A condition that filters the values to be aggregated.
* `[max_size]`: (Optional) Specifies the maximum number of elements in the resulting set. If omitted, the default is 1048576.
### Returns
The `make_set_if` function returns a dynamic array of distinct values from the specified column that satisfy the given condition.
## Use case examples
In this use case, you’re analyzing HTTP logs and want to get the distinct cities from which requests originated, but only for requests that took longer than 500 ms.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_set_if(['geo.city'], req_duration_ms > 500) by ['method']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20make_set_if%28%5B%27geo.city%27%5D%2C%20req_duration_ms%20%3E%20500%29%20by%20%5B%27method%27%5D%22%7D)
**Output**
| method | make\_set\_if\_geo.city |
| ------ | ------------------------------ |
| GET | \[‘New York’, ‘San Francisco’] |
| POST | \[‘Berlin’, ‘Tokyo’] |
This query returns the distinct cities from which requests took more than 500 ms, grouped by HTTP request method.
Here, you’re analyzing OpenTelemetry traces and want to identify the distinct services that processed spans with a duration greater than 1 second, grouped by trace ID.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize make_set_if(['service.name'], duration > 1s) by ['trace_id']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20make_set_if%28%5B%27service.name%27%5D%2C%20duration%20%3E%201s%29%20by%20%5B%27trace_id%27%5D%22%7D)
**Output**
| trace\_id | make\_set\_if\_service.name |
| --------- | ------------------------------------- |
| abc123 | \[‘frontend’, ‘cartservice’] |
| def456 | \[‘checkoutservice’, ‘loadgenerator’] |
This query extracts distinct services that have processed spans longer than 1 second for each trace.
In security log analysis, you may want to find out which HTTP status codes were encountered for each city, but only for POST requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize make_set_if(status, method == 'POST') by ['geo.city']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20make_set_if%28status%2C%20method%20%3D%3D%20%27POST%27%29%20by%20%5B%27geo.city%27%5D%22%7D)
**Output**
| geo.city | make\_set\_if\_status |
| -------- | --------------------- |
| Berlin | \[‘200’, ‘404’] |
| Tokyo | \[‘500’, ‘403’] |
This query identifies the distinct HTTP status codes for POST requests grouped by the originating city.
## List of related aggregations
* [**make\_list\_if**](/apl/aggregation-function/make-list-if): Similar to `make_set_if`, but returns a list that can include duplicates instead of a distinct set.
* [**make\_set**](/apl/aggregation-function/make-set): Aggregates distinct values without a conditional filter.
* [**countif**](/apl/aggregation-function/countif): Counts rows that satisfy a specific condition, useful for when you need to count rather than aggregate distinct values.
# max
Source: https://axiom.co/docs/apl/aggregation-function/max
This page explains how to use the max aggregation function in APL.
The `max` aggregation in APL allows you to find the highest value in a specific column of your dataset. This is useful when you need to identify the maximum value of numerical data, such as the longest request duration, highest sales figures, or the latest timestamp in logs. The `max` function is ideal when you are working with large datasets and need to quickly retrieve the largest value, ensuring you’re focusing on the most critical or recent data point.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `max` function works similarly, used to find the maximum value in a given field. The syntax in APL, however, requires you to specify the column to aggregate within a query and make use of APL's structured flow.
```sql Splunk example theme={null}
| stats max(req_duration_ms)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize max(req_duration_ms)
```
In ANSI SQL, `MAX` works similarly to APL’s `max`. In SQL, you aggregate over a column using the `MAX` function in a `SELECT` statement. In APL, you achieve the same result using the `summarize` operator followed by the `max` function.
```sql SQL example theme={null}
SELECT MAX(req_duration_ms) FROM sample_http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize max(req_duration_ms)
```
## Usage
### Syntax
```kusto theme={null}
summarize max(ColumnName)
```
### Parameters
* `ColumnName`: The column or field from which you want to retrieve the maximum value. The column should contain numerical data, timespans, or dates.
### Returns
The maximum value from the specified column.
## Use case examples
In log analysis, you might want to find the longest request duration to diagnose performance issues.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize max(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20max\(req_duration_ms\)%22%7D)
**Output**
| max\_req\_duration\_ms |
| ---------------------- |
| 5400 |
This query returns the highest request duration from the `req_duration_ms` field, which helps you identify the slowest requests.
When analyzing OpenTelemetry traces, you can find the longest span duration to determine performance bottlenecks in distributed services.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize max(duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20max\(duration\)%22%7D)
**Output**
| max\_duration |
| ------------- |
| 00:00:07.234 |
This query returns the longest trace span from the `duration` field, helping you pinpoint the most time-consuming operations.
In security log analysis, you may want to identify the most recent event for monitoring threats or auditing activities.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize max(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20max\(_time\)%22%7D)
**Output**
| max\_time |
| ------------------- |
| 2024-09-25 12:45:01 |
This query returns the most recent timestamp from your logs, allowing you to monitor the latest security events.
## List of related aggregations
* [**min**](/apl/aggregation-function/min): Retrieves the minimum value from a column, which is useful when you need to find the smallest or earliest value, such as the lowest request duration or first event in a log.
* [**avg**](/apl/aggregation-function/avg): Calculates the average value of a column. This function helps when you want to understand the central tendency, such as the average response time for requests.
* [**sum**](/apl/aggregation-function/sum): Sums all values in a column, making it useful when calculating totals, such as total sales or total number of requests over a period.
* [**count**](/apl/aggregation-function/count): Counts the number of records or non-null values in a column. It’s useful for finding the total number of log entries or transactions.
* [**percentile**](/apl/aggregation-function/percentile): Finds a value below which a specified percentage of data falls. This aggregation is helpful when you need to analyze performance metrics like latency at the 95th percentile.
# maxif
Source: https://axiom.co/docs/apl/aggregation-function/maxif
This page explains how to use the maxif aggregation function in APL.
# maxif aggregation in APL
## Introduction
The `maxif` aggregation function in APL is useful when you want to return the maximum value from a dataset based on a conditional expression. This allows you to filter the dataset dynamically and only return the maximum for rows that satisfy the given condition. It’s particularly helpful for scenarios where you want to find the highest value of a specific metric, like response time or duration, but only for a subset of the data (for example, successful responses, specific users, or requests from a particular geographic location).
You can use the `maxif` function when analyzing logs, monitoring system traces, or inspecting security-related data to get insights into the maximum value under certain conditions.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use the `stats max()` function alongside a conditional filtering step to achieve a similar result. APL’s `maxif` function combines both operations into one, streamlining the query.
```splunk theme={null}
| stats max(req_duration_ms) as max_duration where status="200"
```
```kusto theme={null}
['sample-http-logs']
| summarize maxif(req_duration_ms, status == "200")
```
In ANSI SQL, you typically use the `MAX` function in conjunction with a `WHERE` clause. APL’s `maxif` allows you to perform the same operation with a single aggregation function.
```sql theme={null}
SELECT MAX(req_duration_ms)
FROM logs
WHERE status = '200';
```
```kusto theme={null}
['sample-http-logs']
| summarize maxif(req_duration_ms, status == "200")
```
## Usage
### Syntax
```kusto theme={null}
summarize maxif(column, condition)
```
### Parameters
* `column`: The column containing the values to aggregate.
* `condition`: The condition that must be true for the values to be considered in the aggregation.
### Returns
The maximum value from `column` for rows that meet the `condition`. If no rows match the condition, it returns `null`.
## Use case examples
In log analysis, you might want to find the maximum request duration, but only for successful requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize maxif(req_duration_ms, status == "200")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20maxif\(req_duration_ms,%20status%20%3D%3D%20'200'\)%22%7D)
**Output**
| max\_req\_duration |
| ------------------ |
| 1250 |
This query returns the maximum request duration (`req_duration_ms`) for HTTP requests with a `200` status.
In OpenTelemetry traces, you might want to find the longest span duration for a specific service type.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize maxif(duration, ['service.name'] == "checkoutservice" and kind == "server")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20maxif\(duration,%20%5B'service.name'%5D%20%3D%3D%20'checkoutservice'%20and%20kind%20%3D%3D%20'server'\)%22%7D)
**Output**
| max\_duration |
| ------------- |
| 2.05s |
This query returns the maximum span duration (`duration`) for server spans in the `checkoutservice`.
For security logs, you might want to identify the longest request duration for any requests originating from a specific country, such as the United States.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize maxif(req_duration_ms, ['geo.country'] == "United States")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20maxif\(req_duration_ms,%20%5B'geo.country'%5D%20%3D%3D%20'United%20States'\)%22%7D)
**Output**
| max\_req\_duration |
| ------------------ |
| 980 |
This query returns the maximum request duration for requests coming from the United States (`geo.country`).
## List of related aggregations
* [**minif**](/apl/aggregation-function/minif): Returns the minimum value from a column for rows that satisfy a condition. Use `minif` when you’re interested in the lowest value under specific conditions.
* [**max**](/apl/aggregation-function/max): Returns the maximum value from a column without filtering. Use `max` when you want the highest value across the entire dataset without conditions.
* [**sumif**](/apl/aggregation-function/sumif): Returns the sum of values for rows that satisfy a condition. Use `sumif` when you want the total value of a column under specific conditions.
* [**avgif**](/apl/aggregation-function/avgif): Returns the average of values for rows that satisfy a condition. Use `avgif` when you want to calculate the mean value based on a filter.
* [**countif**](/apl/aggregation-function/countif): Returns the count of rows that satisfy a condition. Use `countif` when you want to count occurrences that meet certain criteria.
# min
Source: https://axiom.co/docs/apl/aggregation-function/min
This page explains how to use the min aggregation function in APL.
The `min` aggregation function in APL returns the minimum value from a set of input values. You can use this function to identify the smallest numeric or comparable value in a column of data. This is useful when you want to find the quickest response time, the lowest transaction amount, or the earliest date in log data. It’s ideal for analyzing performance metrics, filtering out abnormal low points in your data, or discovering outliers.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, the `min` function works similarly to APL's `min` aggregation, allowing you to find the minimum value in a field across your dataset. The main difference is in the query structure and syntax between the two.
```sql Splunk example theme={null}
| stats min(duration) by id
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize min(req_duration_ms) by id
```
In ANSI SQL, the `MIN` function works almost identically to the APL `min` aggregation. You use it to return the smallest value in a column of data, grouped by one or more fields.
```sql SQL example theme={null}
SELECT MIN(duration), id FROM sample_http_logs GROUP BY id;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize min(req_duration_ms) by id
```
## Usage
### Syntax
```kusto theme={null}
summarize min(Expression)
```
### Parameters
* `Expression`: The expression from which to calculate the minimum value. Typically, this is a numeric or date/time field.
### Returns
The function returns the smallest value found in the specified column or expression.
## Use case examples
In this use case, you analyze HTTP logs to find the minimum request duration for each unique user.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize min(req_duration_ms) by id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20min\(req_duration_ms\)%20by%20id%22%7D)
**Output**
| id | min\_req\_duration\_ms |
| --------- | ---------------------- |
| user\_123 | 32 |
| user\_456 | 45 |
This query returns the minimum request duration for each user, helping you identify the fastest responses.
Here, you analyze OpenTelemetry trace data to find the minimum span duration per service.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize min(duration) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20min\(duration\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | min\_duration |
| --------------- | ------------- |
| frontend | 2ms |
| checkoutservice | 5ms |
This query returns the minimum span duration for each service in the trace logs.
In this example, you analyze security logs to find the minimum request duration for each HTTP status code.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize min(req_duration_ms) by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20min\(req_duration_ms\)%20by%20status%22%7D)
**Output**
| status | min\_req\_duration\_ms |
| ------ | ---------------------- |
| 200 | 10 |
| 404 | 40 |
This query returns the minimum request duration for each HTTP status code, helping you identify if certain statuses are associated with faster or slower response times.
## List of related aggregations
* [**max**](/apl/aggregation-function/max): Returns the maximum value from a set of values. Use `max` when you need to find the highest value instead of the lowest.
* [**avg**](/apl/aggregation-function/avg): Calculates the average of a set of values. Use `avg` to find the mean value instead of the minimum.
* [**count**](/apl/aggregation-function/count): Counts the number of records or distinct values. Use `count` when you need to know how many records or unique values exist, rather than calculating the minimum.
* [**sum**](/apl/aggregation-function/sum): Adds all values together. Use `sum` when you need the total of a set of values rather than the minimum.
* [**percentile**](/apl/aggregation-function/percentile): Returns the value at a specified percentile. Use `percentile` if you need a value that falls at a certain point in the distribution of your data, rather than the minimum.
# minif
Source: https://axiom.co/docs/apl/aggregation-function/minif
This page explains how to use the minif aggregation function in APL.
## Introduction
The `minif` aggregation in Axiom Processing Language (APL) allows you to calculate the minimum value of a numeric expression, but only for records that meet a specific condition. This aggregation is useful when you want to find the smallest value in a subset of data that satisfies a given predicate. For example, you can use `minif` to find the shortest request duration for successful HTTP requests, or the minimum span duration for a specific service in your OpenTelemetry traces.
The `minif` aggregation is especially useful in scenarios where you need conditional aggregations, such as log analysis, monitoring distributed systems, or examining security-related events.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you might use the `min` function in combination with `where` to filter results. In APL, the `minif` function combines both the filtering condition and the minimum calculation into one step.
```sql Splunk example theme={null}
| stats min(req_duration_ms) as min_duration where status="200"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize minif(req_duration_ms, status == "200") by id
```
In ANSI SQL, you would typically use a `CASE` statement with `MIN` to apply conditional logic for aggregation. In APL, the `minif` function simplifies this by combining both the condition and the aggregation.
```sql SQL example theme={null}
SELECT MIN(CASE WHEN status = '200' THEN req_duration_ms ELSE NULL END) as min_duration
FROM sample_http_logs
GROUP BY id;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize minif(req_duration_ms, status == "200") by id
```
## Usage
### Syntax
```kusto theme={null}
summarize minif(Expression, Predicate)
```
### Parameters
| Parameter | Description |
| ------------ | ------------------------------------------------------------ |
| `Expression` | The numeric expression whose minimum value you want to find. |
| `Predicate` | The condition that determines which records to include. |
### Returns
The `minif` aggregation returns the minimum value of the specified `Expression` for the records that satisfy the `Predicate`.
## Use case examples
In log analysis, you might want to find the minimum request duration for successful HTTP requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize minif(req_duration_ms, status == '200') by ['geo.city']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20minif\(req_duration_ms,%20status%20%3D%3D%20'200'\)%20by%20%5B'geo.city'%5D%22%7D)
**Output**
| geo.city | min\_duration |
| --------- | ------------- |
| San Diego | 120 |
| New York | 95 |
This query finds the minimum request duration for HTTP requests with a `200` status code, grouped by city.
For distributed tracing, you can use `minif` to find the minimum span duration for a specific service.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize minif(duration, ['service.name'] == 'frontend') by trace_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20minif\(duration,%20%5B'service.name'%5D%20%3D%3D%20'frontend'\)%20by%20trace_id%22%7D)
**Output**
| trace\_id | min\_duration |
| --------- | ------------- |
| abc123 | 50ms |
| def456 | 40ms |
This query returns the minimum span duration for traces from the `frontend` service, grouped by `trace_id`.
In security logs, you can use `minif` to find the minimum request duration for HTTP requests from a specific country.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize minif(req_duration_ms, ['geo.country'] == 'US') by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20minif\(req_duration_ms,%20%5B'geo.country'%5D%20%3D%3D%20'US'\)%20by%20status%22%7D)
**Output**
| status | min\_duration |
| ------ | ------------- |
| 200 | 95 |
| 404 | 120 |
This query returns the minimum request duration for HTTP requests originating from the United States, grouped by HTTP status code.
## List of related aggregations
* [**maxif**](/apl/aggregation-function/maxif): Finds the maximum value of an expression that satisfies a condition. Use `maxif` when you need the maximum value under a condition, rather than the minimum.
* [**avgif**](/apl/aggregation-function/avgif): Calculates the average value of an expression that meets a specified condition. Useful when you want an average instead of a minimum.
* [**countif**](/apl/aggregation-function/countif): Counts the number of records that satisfy a given condition. Use this for counting records rather than calculating a minimum.
* [**sumif**](/apl/aggregation-function/sumif): Sums the values of an expression for records that meet a condition. Helpful when you’re interested in the total rather than the minimum.
# percentile
Source: https://axiom.co/docs/apl/aggregation-function/percentile
This page explains how to use the percentile aggregation function in APL.
The `percentile` aggregation function in Axiom Processing Language (APL) allows you to calculate the value below which a given percentage of data points fall. It’s particularly useful when you need to analyze distributions and want to summarize the data using specific thresholds, such as the 90th or 95th percentile. This function can be valuable in performance analysis, trend detection, or identifying outliers across large datasets.
You can apply the `percentile` function to various use cases, such as analyzing log data for request durations, OpenTelemetry traces for service latencies, or security logs to assess risk patterns.
The `percentile` aggregation in APL is a statistical aggregation that returns estimated results. The estimation comes with the benefit of speed at the expense of accuracy. This means that `percentile` is fast and light on resources even on a large or high-cardinality dataset, but it doesn’t provide precise results.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `percentile` function is referred to as `perc` or `percentile`. APL's `percentile` function works similarly, but the syntax is different. The main difference is that APL requires you to explicitly define the column on which you want to apply the percentile and the target percentile value.
```sql Splunk example theme={null}
| stats perc95(req_duration_ms)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize percentile(req_duration_ms, 95)
```
In ANSI SQL, you might use the `PERCENTILE_CONT` or `PERCENTILE_DISC` functions to compute percentiles. In APL, the `percentile` function provides a simpler syntax while offering similar functionality.
```sql SQL example theme={null}
SELECT PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY req_duration_ms) FROM sample_http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize percentile(req_duration_ms, 95)
```
## Usage
### Syntax
```kusto theme={null}
percentile(column, percentile)
```
### Parameters
* **column**: The name of the column to calculate the percentile on. This must be a numeric field.
* **percentile**: The target percentile value (between 0 and 100).
### Returns
The function returns the value from the specified column that corresponds to the given percentile.
## Use case examples
In log analysis, you can use the `percentile` function to identify the 95th percentile of request durations, which gives you an idea of the tail-end latencies of requests in your system.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentile(req_duration_ms, 95)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20percentile%28req_duration_ms%2C%2095%29%22%7D)
**Output**
| percentile\_req\_duration\_ms |
| ----------------------------- |
| 1200 |
This query calculates the 95th percentile of request durations, showing that 95% of requests take less than or equal to 1200ms.
For OpenTelemetry traces, you can use the `percentile` function to identify the 90th percentile of span durations for specific services, which helps to understand the performance of different services.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where ['service.name'] == 'checkoutservice'
| summarize percentile(duration, 90)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20where%20%5B%27service.name%27%5D%20%3D%3D%20%27checkoutservice%27%20%7C%20summarize%20percentile%28duration%2C%2090%29%22%7D)
**Output**
| percentile\_duration |
| -------------------- |
| 300ms |
This query calculates the 90th percentile of span durations for the `checkoutservice`, helping to assess high-latency spans.
In security logs, you can use the `percentile` function to calculate the 99th percentile of response times for a specific set of status codes, helping you focus on outliers.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '500'
| summarize percentile(req_duration_ms, 99)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20status%20%3D%3D%20%27500%27%20%7C%20summarize%20percentile%28req_duration_ms%2C%2099%29%22%7D)
**Output**
| percentile\_req\_duration\_ms |
| ----------------------------- |
| 2500 |
This query identifies that 99% of requests resulting in HTTP 500 errors take less than or equal to 2500ms.
## List of related aggregations
* [**avg**](/apl/aggregation-function/avg): Use `avg` to calculate the average of a column, which gives you the central tendency of your data. In contrast, `percentile` provides more insight into the distribution and tail values.
* [**min**](/apl/aggregation-function/min): The `min` function returns the smallest value in a column. Use this when you need the absolute lowest value instead of a specific percentile.
* [**max**](/apl/aggregation-function/max): The `max` function returns the highest value in a column. It’s useful for finding the upper bound, while `percentile` allows you to focus on a specific point in the data distribution.
* [**stdev**](/apl/aggregation-function/stdev): `stdev` calculates the standard deviation of a column, which helps measure data variability. While `stdev` provides insight into overall data spread, `percentile` focuses on specific distribution points.
# percentileif
Source: https://axiom.co/docs/apl/aggregation-function/percentileif
This page explains how to use the percentileif aggregation function in APL.
The `percentileif` aggregation function calculates the percentile of a numeric column, conditional on a specified boolean predicate. This function is useful for filtering data dynamically and determining percentile values based only on relevant subsets of data.
You can use `percentileif` to gain insights in various scenarios, such as:
* Identifying response time percentiles for HTTP requests from specific regions.
* Calculating percentiles of span durations for specific service types in OpenTelemetry traces.
* Analyzing security events by percentile within defined risk categories.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
The `percentileif` aggregation in APL works similarly to `percentile` combined with conditional filtering in SPL. However, APL integrates the condition directly into the aggregation for simplicity.
```sql Splunk example theme={null}
stats perc95(req_duration_ms) as p95 where geo.country="US"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize percentileif(req_duration_ms, 95, geo.country == 'US')
```
In SQL, you typically calculate percentiles using window functions or aggregate functions combined with a `WHERE` clause. APL simplifies this by embedding the condition directly in the `percentileif` aggregation.
```sql SQL example theme={null}
SELECT PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY req_duration_ms)
FROM sample_http_logs
WHERE geo_country = 'US'
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize percentileif(req_duration_ms, 95, geo.country == 'US')
```
## Usage
### Syntax
```kusto theme={null}
summarize percentileif(Field, Percentile, Predicate)
```
### Parameters
| Parameter | Description |
| ------------ | ---------------------------------------------------------------------- |
| `Field` | The numeric field from which to calculate the percentile. |
| `Percentile` | A number between 0 and 100 that specifies the percentile to calculate. |
| `Predicate` | A Boolean expression that filters rows to include in the calculation. |
### Returns
The function returns a single numeric value representing the specified percentile of the `Field` for rows where the `Predicate` evaluates to `true`.
## Use case examples
You can use `percentileif` to analyze request durations for specific HTTP methods.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize post_p90 = percentileif(req_duration_ms, 90, method == "POST"), get_p90 = percentileif(req_duration_ms, 90, method == "GET") by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20post_p90%20%3D%20percentileif\(req_duration_ms%2C%2090%2C%20method%20%3D%3D%20'POST'\)%2C%20get_p90%20%3D%20percentileif\(req_duration_ms%2C%2090%2C%20method%20%3D%3D%20'GET'\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| post\_p90 | get\_p90 |
| --------- | -------- |
| 1.691 ms | 1.453 ms |
This query calculates the 90th percentile of request durations for HTTP POST and GET methods.
You can use `percentileif` to measure span durations for specific services and operation kinds.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize percentileif(duration, 95, ['service.name'] == 'frontend' and kind == 'server')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20percentileif%28duration%2C%2095%2C%20%5B%27service.name%27%5D%20%3D%3D%20%27frontend%27%20and%20kind%20%3D%3D%20%27server%27%29%22%7D)
**Output**
| Percentile95 |
| ------------ |
| 1.2s |
This query calculates the 95th percentile of span durations for server spans in the `frontend` service.
You can use `percentileif` to calculate response time percentiles for specific HTTP status codes.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentileif(req_duration_ms, 75, status == '404')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20percentileif%28req_duration_ms%2C%2075%2C%20status%20%3D%3D%20%27404%27%29%22%7D)
**Output**
| Percentile75 |
| ------------ |
| 350 |
This query calculates the 75th percentile of request durations for HTTP 404 errors.
## List of related aggregations
* [percentile](/apl/aggregation-function/percentile): Calculates the percentile for all rows without any filtering. Use `percentile` when you don’t need conditional filtering.
* [avgif](/apl/aggregation-function/avgif): Calculates the average of a numeric column based on a condition. Use `avgif` for mean calculations instead of percentiles.
* [minif](/apl/aggregation-function/minif): Returns the minimum value of a numeric column where a condition is true. Use `minif` for identifying the lowest values within subsets.
* [maxif](/apl/aggregation-function/maxif): Returns the maximum value of a numeric column where a condition is true. Use `maxif` for identifying the highest values within subsets.
* [sumif](/apl/aggregation-function/sumif): Sums a numeric column based on a condition. Use `sumif` for conditional total calculations.
# percentiles_array
Source: https://axiom.co/docs/apl/aggregation-function/percentiles-array
This page explains how to use the percentiles_array function in APL.
Use the `percentiles_array` aggregation function in APL to calculate multiple percentile values over a numeric expression in one pass. This function is useful when you want to understand the distribution of numeric data points, such as response times or durations, by summarizing them at several key percentiles like the 25th, 50th, and 95th.
You can use `percentiles_array` to:
* Analyze latency or duration metrics across requests or operations.
* Identify performance outliers.
* Visualize percentile distributions in dashboards.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you typically calculate percentiles one at a time using the `perc` function. To get multiple percentiles, you repeat the function with different percentile values. In APL, `percentiles_array` lets you specify multiple percentiles in a single function call and returns them as an array.
```sql Splunk example theme={null}
... | stats perc95(duration), perc50(duration), perc25(duration) by service
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| summarize percentiles_array(duration, 25, 50, 95) by ['service.name']
```
Standard SQL typically lacks a built-in function to calculate multiple percentiles in a single operation. Instead, you use `PERCENTILE_CONT` or `PERCENTILE_DISC` with `WITHIN GROUP`, repeated for each desired percentile. In APL, `percentiles_array` simplifies this with a single function call that returns all requested percentiles as an array.
```sql SQL example theme={null}
SELECT
service,
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY duration) AS p25,
PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY duration) AS p50,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration) AS p95
FROM traces
GROUP BY service
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| summarize percentiles_array(duration, 25, 50, 95) by ['service.name']
```
## Usage
### Syntax
```kusto theme={null}
percentiles_array(Field, Percentile1, Percentile2, ...)
```
### Parameters
* `Field` is the name of the field for which you want to compute percentile values.
* `Percentile1`, `Percentile2`, ... are numeric percentile values between 0 and 100.
### Returns
An array of numbers where each element is the value at the corresponding percentile.
## Use case examples
Use `percentiles_array` to understand the spread of request durations per HTTP method, highlighting performance variability.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentiles_array(req_duration_ms, 25, 50, 95) by method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20percentiles_array\(req_duration_ms%2C%2025%2C%2050%2C%2095\)%20by%20method%22%7D)
**Output**
| method | P25 | P50 | P95 |
| ------ | --------- | --------- | -------- |
| GET | 0.3981 ms | 0.7352 ms | 1.981 ms |
| POST | 0.3261 ms | 0.7162 ms | 2.341 ms |
| PUT | 0.3324 ms | 0.7772 ms | 1.341 ms |
| DELETE | 0.2332 ms | 0.4652 ms | 1.121 ms |
This query calculates the 25th, 50th, and 95th percentiles of request durations for each HTTP method. It helps identify performance differences between different methods.
Use `percentiles_array` to analyze the distribution of span durations by service to detect potential bottlenecks.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize percentiles_array(duration, 50, 90, 99) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20percentiles_array\(duration%2C%2050%2C%2090%2C%2099\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | P50 | P90 | P99 | P99 |
| --------------------- | -------- | --------- | --------- | --------- |
| recommendationservice | 1.96 ms | 2.965 ms | 3.477 ms | 3.477 ms |
| frontendproxy | 3.767 ms | 13.101 ms | 39.735 ms | 39.735 ms |
| shippingservice | 2.119 ms | 3.085 ms | 9.739 ms | 9.739 ms |
| checkoutservice | 1.454 ms | 12.342 ms | 29.542 ms | 29.542 ms |
This query shows latency patterns across services by computing the median, 90th, and 99th percentile of span durations.
Use `percentiles_array` to assess outlier response times per status code, which can reveal abnormal activity or service issues.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentiles_array(req_duration_ms, 50, 95, 99) by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20percentiles_array\(req_duration_ms%2C%2050%2C%2095%2C%2099\)%20by%20status%22%7D)
**Output**
| status | P50 | P95 | P99 |
| ------ | --------- | -------- | -------- |
| 200 | 0.7352 ms | 1.981 ms | 2.612 ms |
| 201 | 0.7856 ms | 1.356 ms | 2.234 ms |
| 301 | 0.8956 ms | 1.547 ms | 2.546 ms |
| 500 | 0.6587 ms | 1.856 ms | 2.856 ms |
This query helps identify whether requests resulting in errors (like 500) are significantly slower than successful ones.
## List of related functions
* [avg](/apl/aggregation-function/avg): Returns the average value. Use it when a single central tendency is sufficient.
* [percentile](/apl/aggregation-function/percentile): Returns a single percentile value. Use it when you only need one percentile.
* [percentile\_if](/apl/aggregation-function/percentileif): Returns a single percentile value for the records that satisfy a condition.
* [percentiles\_arrayif](/apl/aggregation-function/percentiles-arrayif): Returns an array of percentile values for the records that satisfy a condition.
* [sum](/apl/aggregation-function/sum): Returns the sum of a numeric column.
# percentiles_arrayif
Source: https://axiom.co/docs/apl/aggregation-function/percentiles-arrayif
This page explains how to use the percentiles_array function in APL.
Use `percentiles_arrayif` to calculate approximate percentile values for a numeric expression when a certain condition evaluates to true. This function is useful when you want an array of percentiles instead of a single percentile. You can use it to understand data distributions in scenarios such as request durations, event processing times, or security alert severities, while filtering on specific criteria.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you often use statistical functions such as `perc` or `percN()` to compute percentile estimates. In APL, you use `percentiles_arrayif` and provide a predicate to define which records to include in the computation.
```sql Splunk example theme={null}
index=main sourcetype=access_combined
| stats perc90(req_duration_ms) AS p90, perc99(req_duration_ms) AS p99
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize Dist=percentiles_arrayif(req_duration_ms, dynamic([90, 99]), status == '200')
```
In ANSI SQL, you often use window functions like `PERCENTILE_DISC` or `PERCENTILE_CONT` or write multiple `CASE` expressions for conditional aggregation. In APL, you can achieve similar functionality with `percentiles_arrayif` by passing the numeric field and condition to the function.
```sql SQL example theme={null}
SELECT
PERCENTILE_DISC(0.90) WITHIN GROUP (ORDER BY req_duration_ms) AS p90,
PERCENTILE_DISC(0.99) WITHIN GROUP (ORDER BY req_duration_ms) AS p99
FROM sample_http_logs
WHERE status = '200';
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize Dist=percentiles_arrayif(req_duration_ms, dynamic([90, 99]), status == '200')
```
# Usage
## Syntax
```kusto theme={null}
percentiles_arrayif(Field, Array, Condition)
```
## Parameters
* `Field` is the name of the field for which you want to compute percentile values.
* `Array` is a dynamic array of one or more numeric percentile values (between 0 and 100).
* `Condition` is a Boolean expression that indicates which records to include in the calculation.
## Returns
The function returns an array of percentile values for the records that satisfy the condition. The position of each returned percentile in the array matches the order in which it appears in the function call.
## Use case examples
You can use `percentiles_arrayif` to analyze request durations in HTTP logs while filtering for specific criteria, such as certain HTTP statuses or geographic locations.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentiles_arrayif(req_duration_ms, dynamic([50, 90, 95, 99]), status == '200') by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20percentiles_arrayif\(req_duration_ms%2C%20dynamic\(%5B50%2C%2090%2C%2095%2C%2099%5D\)%2C%20status%20%3D%3D%20'200'\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| percentiles\_req\_duration\_ms |
| ------------------------------ |
| 0.7352 ms |
| 1.691 ms |
| 1.981 ms |
| 2.612 ms |
This query filters records to those with a status of 200 and returns the percentile values for the request durations.
Use `percentiles_arrayif` to track performance of spans and filter on a specific service operation. This lets you quickly gauge how request durations differ for incoming traffic.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize percentiles_arrayif(duration, dynamic([50, 90, 99, 99]), ['method'] == "POST") by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20percentiles_arrayif\(duration%2C%20dynamic\(%5B50%2C%2090%2C%2099%2C%2099%5D\)%2C%20%5B'method'%5D%20%3D%3D%20'POST'\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| percentiles\_duration |
| --------------------- |
| 5.166 ms |
| 25.18 ms |
| 71.996 ms |
This query returns the percentile values for span durations for requests with the POST method.
You can focus on server issues by filtering for specific status codes, then see how request durations are distributed in those scenarios.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize percentiles_arrayif(req_duration_ms, dynamic([50, 90, 95, 99]), status startswith '5') by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20percentiles_arrayif\(req_duration_ms%2C%20dynamic\(%5B50%2C%2090%2C%2095%2C%2099%5D\)%2C%20status%20startswith%20'5'\)%20by%20bin_auto\(_time\)%22%7D)
**Output**
| percentiles\_req\_duration\_ms |
| ------------------------------ |
| 0.7352 ms |
| 1.691 ms |
| 1.981 ms |
| 2.612 ms |
This query calculates percentile values for request durations that return a status code starting with 5 which means server error.
## List of related functions
* [avg](/apl/aggregation-function/avg): Returns the average of a numeric column.
* [percentile](/apl/aggregation-function/percentile): Returns a single percentile value.
* [percentile\_if](/apl/aggregation-function/percentileif): Returns a single percentile value for the records that satisfy a condition.
* [percentiles\_array](/apl/aggregation-function/percentiles-array): Returns an array of percentile values for all rows.
* [sum](/apl/aggregation-function/sum): Returns the sum of a numeric column.
# phrases
Source: https://axiom.co/docs/apl/aggregation-function/phrases
This page explains how to use the phrases aggregation function in APL.
The `phrases` aggregation extracts and counts common phrases or word sequences from text fields across a dataset. It analyzes text content to identify frequently occurring phrases, helping you discover patterns, trends, and common topics in your data.
You can use this aggregation to identify common user queries, discover trending topics, extract key phrases from logs, or analyze conversation patterns in AI applications.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, there’s no built-in phrases function, but you might use the `rare` or `top` commands on tokenized text.
```sql Splunk example theme={null}
| rex field=message "(?\w+)"
| top words
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize phrases(uri, 10)
```
In ANSI SQL, you would need complex string manipulation and grouping to extract common phrases.
```sql SQL example theme={null}
SELECT
phrase,
COUNT(*) as frequency
FROM (
SELECT UNNEST(SPLIT(message, ' ')) as phrase
FROM logs
)
GROUP BY phrase
ORDER BY frequency DESC
LIMIT 10
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize phrases(uri, 10)
```
## Usage
### Syntax
```kusto theme={null}
summarize phrases(column, max_phrases)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | -------------------------------------------------------------- |
| `column` | string | Yes | The column containing text data from which to extract phrases. |
| `max_phrases` | long | No | The maximum number of top phrases to return. Default is 10. |
### Returns
Returns a dynamic array containing the most common phrases found in the specified column, ordered by frequency.
## Example
Extract common phrases from GenAI conversation text to identify trending topics and patterns.
**Query**
```kusto theme={null}
['genai-traces']
| extend conversation_text = genai_concat_contents(['attributes.gen_ai.input.messages'], ' | ')
| summarize common_phrases = phrases(conversation_text, 20)
```
**Output**
| Count | common\_phrases |
| ----- | ---------------------------- |
| 3 | first query |
| 3 | cover related future queries |
| 3 | use title case |
This query identifies the most common phrases in GenAI conversations, helping you discover trending topics and user needs.
## List of related functions
* [make\_list](/apl/aggregation-function/make-list): Creates an array of all values. Use this when you need all occurrences rather than common phrases.
* [make\_set](/apl/aggregation-function/make-set): Creates an array of unique values. Use this for distinct values without frequency analysis.
* [topk](/apl/aggregation-function/topk): Returns top K values by a specific aggregation. Use this for numerical top values rather than phrase extraction.
* [count](/apl/aggregation-function/count): Counts occurrences. Combine with group by for manual phrase counting if you need more control.
* [dcount](/apl/aggregation-function/dcount): Counts distinct values. Use this to understand the variety of phrases before extracting top ones.
# rate
Source: https://axiom.co/docs/apl/aggregation-function/rate
This page explains how to use the rate aggregation function in APL.
The `rate` aggregation function in APL (Axiom Processing Language) helps you calculate the rate of change over a specific time interval. This is especially useful for scenarios where you need to monitor how frequently an event occurs or how a value changes over time. For example, you can use the `rate` function to track request rates in web logs or changes in metrics like CPU usage or memory consumption.
The `rate` function is useful for analyzing trends in time series data and identifying unusual spikes or drops in activity. It can help you understand patterns in logs, metrics, and traces over specific intervals, such as per minute, per second, or per hour.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the equivalent of the `rate` function can be achieved using the `timechart` command with a `per_second` option or by calculating the difference between successive values over time. In APL, the `rate` function simplifies this process by directly calculating the rate over a specified time interval.
```splunk Splunk example theme={null}
| timechart per_second count by resp_body_size_bytes
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize rate(resp_body_size_bytes) by bin(_time, 1s)
```
In ANSI SQL, calculating rates typically involves using window functions like `LAG` or `LEAD` to calculate the difference between successive rows in a time series. In APL, the `rate` function abstracts this complexity by allowing you to directly compute the rate over time without needing window functions.
```sql SQL example theme={null}
SELECT resp_body_size_bytes, COUNT(*) / TIMESTAMPDIFF(SECOND, MIN(_time), MAX(_time)) AS rate
FROM http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize rate(resp_body_size_bytes) by bin(_time, 1s)
```
## Usage
### Syntax
```kusto theme={null}
rate(field)
```
### Parameters
* `field`: The numeric field for which you want to calculate the rate.
### Returns
Returns the rate of change or occurrence of the specified `field` over the time interval specified in the query.
Specify the time interval in the query in the following way:
* `| summarize rate(field)` calculates the rate value of the field over the entire query window.
* `| summarize rate(field) by bin(_time, 1h)` calculates the rate value of the field over a one-hour time window.
* `| summarize rate(field) by bin_auto(_time)` calculates the rate value of the field bucketed by an automatic time window computed by `bin_auto()`.
Use two `summarize` statements to visualize the average rate over one minute per hour. For example:
```kusto theme={null}
['sample-http-logs']
| summarize respBodyRate = rate(resp_body_size_bytes) by bin(_time, 1m)
| summarize avg(respBodyRate) by bin(_time, 1h)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20respBodyRate%20%3D%20rate\(resp_body_size_bytes\)%20by%20bin\(_time%2C%201m\)%20%7C%20summarize%20avg\(respBodyRate\)%20by%20bin\(_time%2C%201h\)%22%2C%20%22queryOptions%22%3A%7B%22quickRange%22%3A%226h%22%7D%7D)
## Use case examples
In this example, the `rate` aggregation calculates the rate of HTTP response sizes per second.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize rate(resp_body_size_bytes) by bin(_time, 1s)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20rate\(resp_body_size_bytes\)%20by%20bin\(_time%2C%201s\)%22%7D)
**Output**
| rate | \_time |
| ------ | ------------------- |
| 854 kB | 2024-01-01 12:00:00 |
| 635 kB | 2024-01-01 12:00:01 |
This query calculates the rate of HTTP response sizes per second.
This example calculates the rate of span duration per second.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize rate(toint(duration)) by bin(_time, 1s)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20rate\(toint\(duration\)\)%20by%20bin\(_time%2C%201s\)%22%7D)
**Output**
| rate | \_time |
| ---------- | ------------------- |
| 26,393,768 | 2024-01-01 12:00:00 |
| 19,303,456 | 2024-01-01 12:00:01 |
This query calculates the rate of span duration per second.
In this example, the `rate` aggregation calculates the rate of HTTP request duration per second which can be useful to detect an increate in malicious requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize rate(req_duration_ms) by bin(_time, 1s)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20rate\(req_duration_ms\)%20by%20bin\(_time%2C%201s\)%22%7D)
**Output**
| rate | \_time |
| ---------- | ------------------- |
| 240.668 ms | 2024-01-01 12:00:00 |
| 264.17 ms | 2024-01-01 12:00:01 |
This query calculates the rate of HTTP request duration per second.
## List of related aggregations
* [**count**](/apl/aggregation-function/count): Returns the total number of records. Use `count` when you want an absolute total instead of a rate over time.
* [**sum**](/apl/aggregation-function/sum): Returns the sum of values in a field. Use `sum` when you want to aggregate the total value, not its rate of change.
* [**avg**](/apl/aggregation-function/avg): Returns the average value of a field. Use `avg` when you want to know the mean value rather than how it changes over time.
* [**max**](/apl/aggregation-function/max): Returns the maximum value of a field. Use `max` when you need to find the peak value instead of how often or quickly something occurs.
* [**min**](/apl/aggregation-function/min): Returns the minimum value of a field. Use `min` when you’re looking for the lowest value rather than a rate.
# spotlight
Source: https://axiom.co/docs/apl/aggregation-function/spotlight
This page explains how to use the spotlight function in APL to compare a selected set of events against a baseline and surface the most significant differences.
Spotlight lets you set up an analysis inside a query. You define a comparison set of events and compare it to the implicit baseline (the rest of the events in scope). Spotlight evaluates every field you pass in, scores differences, and returns the most informative contrasts. You use it when you want fast root-cause analysis, anomaly investigation, or pattern discovery without hand-rolling many ad-hoc aggregations.
Spotlight is useful when you:
* Investigate spikes or dips in a time series and want to know what changed
* Explain why a subset of traces is slow or error-prone
* Find which attributes distinguish suspicious requests from normal traffic
This page explains the Spotlight APL function. For more information about how Spotlight works in the Axiom Console, see [Spotlight](/console/intelligence/spotlight).
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, there is no one operator that compares a selected cohort to the baseline across many fields at once. You often create a flag with `eval`, run separate `stats`/`eventstats` for each field, and then `appendpipe` or `join` to compare rates. In APL, `spotlight` is an aggregation you call once inside `summarize`. You pass a Boolean predicate to define the cohort and a list of fields to inspect, and APL returns a scored table of differences.
```sql Splunk example theme={null}
index=web earliest=-15m
| eval is_error = if(status IN ("500","502","503"), 1, 0)
| stats count by method uri status geo_country geo_city
| eventstats sum(eval(is_error)) AS sel, sum(eval(1-is_error)) AS base
| ... (manual rate/lift calculations and sorting) ...
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where _time >= now(-15m)
| summarize spotlight(status in ('500','502','503'),
['geo.country'], ['geo.city'], method, uri, status)
```
Standard SQL does not include a built-in cohort-vs-baseline comparator. You typically `CASE` a selection flag, aggregate twice (selected vs baseline), compute proportions, deltas, and significance, then union and sort. In APL, you express the selection as a predicate and let `spotlight` compute proportions, lift, and scores for each field/value.
```sql SQL example theme={null}
WITH scoped AS (
SELECT *,
CASE WHEN status IN ('500','502','503') THEN 1 ELSE 0 END AS is_sel
FROM sample_http_logs
WHERE _time >= NOW() - INTERVAL '15' MINUTE
),
per_field AS (
SELECT 'status' AS field, status AS value,
AVG(is_sel) AS sel_rate,
AVG(1 - is_sel) AS base_rate
FROM scoped
GROUP BY status
)
SELECT field, value, sel_rate, base_rate,
sel_rate / NULLIF(base_rate,0) AS lift
FROM per_field
ORDER BY lift DESC
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where _time >= now(-15m)
| summarize spotlight(status in ('500','502','503'),
status, method, uri, ['geo.country'], ['geo.city'])
```
## Usage
### Syntax
```kusto theme={null}
summarize spotlight(SelectionPredicate, Field1, Field2, ..., FieldN)
```
You use `spotlight` inside `summarize`. The first argument defines the comparison set. The remaining arguments list the fields to analyze.
### Parameters
| Name | Type | Description |
| -------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SelectionPredicate` | Boolean expression | Defines the comparison set (selected cohort). Spotlight compares events where the predicate evaluates to `true` against the baseline (events where it evaluates to `false`) within the current query scope. |
| `Field1 ... FieldN` | field references | One or more fields to analyze. Include string or categorical fields (for proportions) and numeric or timespan fields (for distributional differences). Use `*` as a wildcard to analyze all fields. |
The wildcard `*` is useful to analyze all fields in the current row, but it increases query complexity and decreases performance. Specify only relevant fields when possible.
### Returns
* Bar charts for categorical fields (strings, Booleans)
* Boxplots for numeric fields (integers, floats, timespans) with many distinct values
## Use case examples
Find what distinguishes error responses from normal traffic in the last 15 minutes.
**Query**
```kusto theme={null}
['sample-http-logs']
| where _time >= now(-15m)
| summarize spotlight(status startswith "5", ['geo.country'], ['geo.city'], method, uri, req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20spotlight\(status%20startswith%20'5'%2C%20%5B'geo.country'%5D%2C%20%5B'geo.city'%5D%2C%20method%2C%20uri%2C%20req_duration_ms\)%22%7D)
This query keeps the last 15 minutes of traffic in scope and compares error responses to everything else. Spotlight ranks the strongest differences, pointing to endpoints, regions, and latency ranges associated with the errors.
Explain why some spans are slow or erroring in the last 30 minutes.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where _time >= now(-30m)
| summarize spotlight(duration > 500ms, ['service.name'], kind, status_code, duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20_time%20%3E%3D%20now\(-30m\)%20%7C%20summarize%20spotlight\(duration%20%3E%20500ms%2C%20%5B'service.name'%5D%2C%20kind%2C%20status_code%2C%20duration\)%22%7D)
The query compares spans that ran longer than 500 ms to all other spans in the time window. Spotlight highlights the service, kind, and duration range that most distinguish the selected spans.
## Best practices
* Keep the `where` scope broad enough that the baseline remains meaningful. Over-filtering reduces contrast.
* Pass only fields that carry signal. Very high-cardinality identifiers can drown out more actionable attributes.
* Include numeric fields like `req_duration_ms` or `duration` to let Spotlight detect distribution shifts, not just categorical skews.
## List of related functions
* [where](/apl/tabular-operators/where-operator): Filters events before Spotlight runs. Use it to scope the time window or dataset; use `spotlight` to compare selected vs baseline inside that scope.
* [summarize](/apl/tabular-operators/summarize-operator): Runs aggregations over events. `spotlight` is an aggregation you call within `summarize`.
* [top](/apl/tabular-operators/top-operator): Returns the most frequent values. Use `top` for simple frequency counts; use `spotlight` to contrast a cohort against its baseline with lift and significance.
* [lookup](/apl/tabular-operators/lookup-operator): Enriches events with reference attributes. Use `lookup` to add context before running `spotlight` across enriched fields.
# Aggregation functions
Source: https://axiom.co/docs/apl/aggregation-function/statistical-functions
This section explains how to use and combine different aggregation functions in APL.
The table summarizes the aggregation functions available in APL. Use all these aggregation functions in the context of the [summarize operator](/apl/tabular-operators/summarize-operator).
| Function | Description |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| [arg\_min](/apl/aggregation-function/arg-min) | Returns the row where an expression evaluates to the minimum value. |
| [arg\_max](/apl/aggregation-function/arg-max) | Returns the row where an expression evaluates to the maximum value. |
| [avg](/apl/aggregation-function/avg) | Returns an average value across the group. |
| [avgif](/apl/aggregation-function/avgif) | Calculates the average value of an expression in records for which the predicate evaluates to true. |
| [count](/apl/aggregation-function/count) | Returns a count of the group without/with a predicate. |
| [countif](/apl/aggregation-function/countif) | Returns a count of rows for which the predicate evaluates to true. |
| [dcount](/apl/aggregation-function/dcount) | Returns an estimate for the number of distinct values that are taken by a scalar an expressionession in the summary group. |
| [dcountif](/apl/aggregation-function/dcountif) | Returns an estimate of the number of distinct values of an expression of rows for which the predicate evaluates to true. |
| [histogram](/apl/aggregation-function/histogram) | Returns a timeseries heatmap chart across the group. |
| [make\_list](/apl/aggregation-function/make-list) | Creates a dynamic JSON object (array) of all the values of an expression in the group. |
| [make\_list\_if](/apl/aggregation-function/make-list-if) | Creates a dynamic JSON object (array) of an expression values in the group for which the predicate evaluates to true. |
| [make\_set](/apl/aggregation-function/make-set) | Creates a dynamic JSON array of the set of distinct values that an expression takes in the group. |
| [make\_set\_if](/apl/aggregation-function/make-set-if) | Creates a dynamic JSON object (array) of the set of distinct values that an expression takes in records for which the predicate evaluates to true. |
| [max](/apl/aggregation-function/max) | Returns the maximum value across the group. |
| [maxif](/apl/aggregation-function/maxif) | Calculates the maximum value of an expression in records for which the predicate evaluates to true. |
| [min](/apl/aggregation-function/min) | Returns the minimum value across the group. |
| [minif](/apl/aggregation-function/minif) | Returns the minimum of an expression in records for which the predicate evaluates to true. |
| [percentile](/apl/aggregation-function/percentile) | Calculates the requested percentiles of the group and produces a timeseries chart. |
| [percentileif](/apl/aggregation-function/percentileif) | Calculates the requested percentiles of the field for the rows where the predicate evaluates to true. |
| [percentiles\_array](/apl/aggregation-function/percentiles-array) | Returns an array of numbers where each element is the value at the corresponding percentile. |
| [percentiles\_arrayif](/apl/aggregation-function/percentiles-arrayif) | Returns an array of percentile values for the records that satisfy the condition. |
| [phrases](/apl/aggregation-function/phrases) | Extracts and counts common phrases or word sequences from text fields. |
| [rate](/apl/aggregation-function/rate) | Calculates the rate of values in a group per second. |
| [spotlight](/apl/aggregation-function/spotlight) | Compares a selected set of events against a baseline and surface the most significant differences. |
| [stdev](/apl/aggregation-function/stdev) | Calculates the standard deviation of an expression across the group. |
| [stdevif](/apl/aggregation-function/stdevif) | Calculates the standard deviation of an expression in records for which the predicate evaluates to true. |
| [sum](/apl/aggregation-function/sum) | Calculates the sum of an expression across the group. |
| [sumif](/apl/aggregation-function/sumif) | Calculates the sum of an expression in records for which the predicate evaluates to true. |
| [topk](/apl/aggregation-function/topk) | Calculates the top values of an expression across the group in a dataset. |
| [topkif](/apl/aggregation-function/topkif) | Calculates the top values of an expression in records for which the predicate evaluates to true. |
| [variance](/apl/aggregation-function/variance) | Calculates the variance of an expression across the group. |
| [varianceif](/apl/aggregation-function/varianceif) | Calculates the variance of an expression in records for which the predicate evaluates to true. |
# stdev
Source: https://axiom.co/docs/apl/aggregation-function/stdev
This page explains how to use the stdev aggregation function in APL.
The `stdev` aggregation in APL computes the standard deviation of a numeric field within a dataset. This is useful for understanding the variability or dispersion of data points around the mean. You can apply this aggregation to various use cases, such as performance monitoring, anomaly detection, and statistical analysis of logs and traces.
Use the `stdev` function to determine how spread out values like request duration, span duration, or response times are. This is particularly helpful when analyzing data trends and identifying inconsistencies, outliers, or abnormal behavior.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `stdev` aggregation function works similarly but has a different syntax. While SPL uses the `stdev` command within the `stats` function, APL users find the aggregation works similarly in APL with just minor differences in syntax.
```sql Splunk example theme={null}
| stats stdev(duration) as duration_std
```
```kusto APL equivalent theme={null}
['dataset']
| summarize duration_std = stdev(duration)
```
In ANSI SQL, the standard deviation is computed using the `STDDEV` function. APL's `stdev` function is the direct equivalent of SQL’s `STDDEV`, although APL uses pipes (`|`) for chaining operations and different keyword formatting.
```sql SQL example theme={null}
SELECT STDDEV(duration) AS duration_std FROM dataset;
```
```kusto APL equivalent theme={null}
['dataset']
| summarize duration_std = stdev(duration)
```
## Usage
### Syntax
```kusto theme={null}
stdev(numeric_field)
```
### Parameters
* **`numeric_field`**: The field containing numeric values for which the standard deviation is calculated.
### Returns
The `stdev` aggregation returns a single numeric value representing the standard deviation of the specified numeric field in the dataset.
## Use case examples
You can use the `stdev` aggregation to analyze HTTP request durations and identify performance variations across different requests. For instance, you can calculate the standard deviation of request durations to identify potential anomalies.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize req_duration_std = stdev(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20req_duration_std%20%3D%20stdev\(req_duration_ms\)%22%7D)
**Output**
| req\_duration\_std |
| ------------------ |
| 345.67 |
This query calculates the standard deviation of the `req_duration_ms` field in the `sample-http-logs` dataset, helping to understand how much variability there is in request durations.
In distributed tracing, calculating the standard deviation of span durations can help identify inconsistent spans that might indicate performance issues or bottlenecks.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize span_duration_std = stdev(duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20span_duration_std%20%3D%20stdev\(duration\)%22%7D)
**Output**
| span\_duration\_std |
| ------------------- |
| 0:00:02.456 |
This query computes the standard deviation of span durations in the `otel-demo-traces` dataset, providing insight into how much variation exists between trace spans.
In security logs, the `stdev` function can help analyze the response times of various HTTP requests, potentially identifying patterns that might be related to security incidents or abnormal behavior.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize resp_time_std = stdev(req_duration_ms) by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20resp_time_std%20%3D%20stdev\(req_duration_ms\)%20by%20status%22%7D)
**Output**
| status | resp\_time\_std |
| ------ | --------------- |
| 200 | 123.45 |
| 500 | 567.89 |
This query calculates the standard deviation of request durations grouped by the HTTP status code, providing insight into the performance of different status codes.
## List of related aggregations
* [**avg**](/apl/aggregation-function/avg): Calculates the average value of a numeric field. Use `avg` to understand the central tendency of the data.
* [**min**](/apl/aggregation-function/min): Returns the smallest value in a numeric field. Use `min` when you need to find the minimum value.
* [**max**](/apl/aggregation-function/max): Returns the largest value in a numeric field. Use `max` to identify the peak value in a dataset.
* [**sum**](/apl/aggregation-function/sum): Adds up all the values in a numeric field. Use `sum` to get a total across records.
* [**count**](/apl/aggregation-function/count): Returns the number of records in a dataset. Use `count` when you need the number of occurrences or entries.
# stdevif
Source: https://axiom.co/docs/apl/aggregation-function/stdevif
This page explains how to use the stdevif aggregation function in APL.
The `stdevif` aggregation function in APL computes the standard deviation of values in a group based on a specified condition. This is useful when you want to calculate variability in data, but only for rows that meet a particular condition. For example, you can use `stdevif` to find the standard deviation of response times in an HTTP log, but only for requests that resulted in a 200 status code.
The `stdevif` function is useful when you want to analyze the spread of data values filtered by specific criteria, such as analyzing request durations in successful transactions or monitoring trace durations of specific services in OpenTelemetry data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `stdev` function is used to calculate the standard deviation, but you need to use an `if` function or a `where` clause to filter data. APL simplifies this by combining both operations in `stdevif`.
```sql Splunk example theme={null}
| stats stdev(req_duration_ms) as stdev_req where status="200"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize stdevif(req_duration_ms, status == "200") by geo.country
```
In ANSI SQL, the `STDDEV` function is used to compute the standard deviation, but it requires the use of a `CASE WHEN` expression to apply a conditional filter. APL integrates the condition directly into the `stdevif` function.
```sql SQL example theme={null}
SELECT STDDEV(CASE WHEN status = '200' THEN req_duration_ms END)
FROM sample_http_logs
GROUP BY geo.country;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize stdevif(req_duration_ms, status == "200") by geo.country
```
## Usage
### Syntax
```kusto theme={null}
summarize stdevif(column, condition)
```
### Parameters
* **column**: The column that contains the numeric values for which you want to calculate the standard deviation.
* **condition**: The condition that must be true for the values to be included in the standard deviation calculation.
### Returns
The `stdevif` function returns a floating-point number representing the standard deviation of the specified column for the rows that satisfy the condition.
## Use case examples
In this example, you calculate the standard deviation of request durations (`req_duration_ms`), but only for successful HTTP requests (status code 200).
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize stdevif(req_duration_ms, status == '200') by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20stdevif%28req_duration_ms%2C%20status%20%3D%3D%20%27200%27%29%20by%20%5B%27geo.country%27%5D%22%7D)
**Output**
| geo.country | stdev\_req\_duration\_ms |
| ----------- | ------------------------ |
| US | 120.45 |
| Canada | 98.77 |
| Germany | 134.92 |
This query calculates the standard deviation of request durations for HTTP 200 responses, grouped by country.
In this example, you calculate the standard deviation of span durations, but only for traces from the `frontend` service.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize stdevif(duration, ['service.name'] == "frontend") by kind
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20stdevif%28duration%2C%20%5B%27service.name%27%5D%20%3D%3D%20%27frontend%27%29%20by%20kind%22%7D)
**Output**
| kind | stdev\_duration |
| ------ | --------------- |
| server | 45.78 |
| client | 23.54 |
This query computes the standard deviation of span durations for the `frontend` service, grouped by span type (`kind`).
In this example, you calculate the standard deviation of request durations for security events from specific HTTP methods, filtered by `POST` requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize stdevif(req_duration_ms, method == "POST") by ['geo.city']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20stdevif%28req_duration_ms%2C%20method%20%3D%3D%20%27POST%27%29%20by%20%5B%27geo.city%27%5D%22%7D)
**Output**
| geo.city | stdev\_req\_duration\_ms |
| -------- | ------------------------ |
| New York | 150.12 |
| Berlin | 130.33 |
This query calculates the standard deviation of request durations for `POST` HTTP requests, grouped by the originating city.
## List of related aggregations
* [**avgif**](/apl/aggregation-function/avgif): Similar to `stdevif`, but instead of calculating the standard deviation, `avgif` computes the average of values that meet the condition.
* [**sumif**](/apl/aggregation-function/sumif): Computes the sum of values that meet the condition. Use `sumif` when you want to aggregate total values instead of analyzing data spread.
* [**varianceif**](/apl/aggregation-function/varianceif): Returns the variance of values that meet the condition, which is a measure of how spread out the data points are.
* [**countif**](/apl/aggregation-function/countif): Counts the number of rows that satisfy the specified condition.
* [**minif**](/apl/aggregation-function/minif): Retrieves the minimum value that satisfies the given condition, useful when finding the smallest value in filtered data.
# sum
Source: https://axiom.co/docs/apl/aggregation-function/sum
This page explains how to use the sum aggregation function in APL.
The `sum` aggregation in APL is used to compute the total sum of a specific numeric field in a dataset. This aggregation is useful when you want to find the cumulative value for a certain metric, such as the total duration of requests, total sales revenue, or any other numeric field that can be summed.
You can use the `sum` aggregation in a wide range of scenarios, such as analyzing log data, monitoring traces, or examining security logs. It’s particularly helpful when you want to get a quick overview of your data in terms of totals or cumulative statistics.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you use the `sum` function in combination with the `stats` command to aggregate data. In APL, the `sum` aggregation works similarly but is structured differently in terms of syntax.
```splunk Splunk example theme={null}
| stats sum(req_duration_ms) as total_duration
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize total_duration = sum(req_duration_ms)
```
In ANSI SQL, the `SUM` function is commonly used with the `GROUP BY` clause to aggregate data by a specific field. In APL, the `sum` function works similarly but can be used without requiring a `GROUP BY` clause for simple summations.
```sql SQL example theme={null}
SELECT SUM(req_duration_ms) AS total_duration
FROM sample_http_logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize total_duration = sum(req_duration_ms)
```
## Usage
### Syntax
```kusto theme={null}
summarize [ =] sum()
```
### Parameters
* ``: (Optional) The name you want to assign to the resulting column that contains the sum.
* ``: The field in your dataset that contains the numeric values you want to sum.
### Returns
The `sum` aggregation returns a single row with the sum of the specified numeric field. If used with a `by` clause, it returns multiple rows with the sum per group.
## Use case examples
The `sum` aggregation can be used to calculate the total request duration in an HTTP log dataset.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize total_duration = sum(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20total_duration%20%3D%20sum\(req_duration_ms\)%22%7D)
**Output**
| total\_duration |
| --------------- |
| 123456 |
This query calculates the total request duration across all HTTP requests in the dataset.
The `sum` aggregation can be applied to OpenTelemetry traces to calculate the total span duration.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize total_duration = sum(duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20total_duration%20%3D%20sum\(duration\)%22%7D)
**Output**
| total\_duration |
| --------------- |
| 7890 |
This query calculates the total duration of all spans in the dataset.
You can use the `sum` aggregation to calculate the total number of requests based on a specific HTTP status in security logs.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '200'
| summarize request_count = sum(1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'200'%20%7C%20summarize%20request_count%20%3D%20sum\(1\)%22%7D)
**Output**
| request\_count |
| -------------- |
| 500 |
This query counts the total number of successful requests (status 200) in the dataset.
## List of related aggregations
* [**count**](/apl/aggregation-function/count): Counts the number of records in a dataset. Use `count` when you want to count the number of rows, not aggregate numeric values.
* [**avg**](/apl/aggregation-function/avg): Computes the average value of a numeric field. Use `avg` when you need to find the mean instead of the total sum.
* [**min**](/apl/aggregation-function/min): Returns the minimum value of a numeric field. Use `min` when you’re interested in the lowest value.
* [**max**](/apl/aggregation-function/max): Returns the maximum value of a numeric field. Use `max` when you’re interested in the highest value.
* [**sumif**](/apl/aggregation-function/sumif): Sums a numeric field conditionally. Use `sumif` when you only want to sum values that meet a specific condition.
# sumif
Source: https://axiom.co/docs/apl/aggregation-function/sumif
This page explains how to use the sumif aggregation function in APL.
The `sumif` aggregation function in Axiom Processing Language (APL) computes the sum of a numeric expression for records that meet a specified condition. This function is useful when you want to filter data based on specific criteria and aggregate the numeric values that match the condition. Use `sumif` when you need to apply conditional logic to sums, such as calculating the total request duration for successful HTTP requests or summing the span durations in OpenTelemetry traces for a specific service.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `sumif` equivalent functionality requires using a `stats` command with a `where` clause to filter the data. In APL, you can use `sumif` to simplify this operation by combining both the condition and the summing logic into one function.
```sql Splunk example theme={null}
| stats sum(duration) as total_duration where status="200"
```
```kusto APL equivalent theme={null}
summarize total_duration = sumif(duration, status == '200')
```
In ANSI SQL, achieving a similar result typically involves using a `CASE` statement inside the `SUM` function to conditionally sum values based on a specified condition. In APL, `sumif` provides a more concise approach by allowing you to filter and sum in a single function.
```sql SQL example theme={null}
SELECT SUM(CASE WHEN status = '200' THEN duration ELSE 0 END) AS total_duration
FROM http_logs
```
```kusto APL equivalent theme={null}
summarize total_duration = sumif(duration, status == '200')
```
## Usage
### Syntax
```kusto theme={null}
sumif(numeric_expression, condition)
```
### Parameters
* `numeric_expression`: The numeric field or expression you want to sum.
* `condition`: A boolean expression that determines which records contribute to the sum. Only the records that satisfy the condition are considered.
### Returns
`sumif` returns the sum of the values in `numeric_expression` for records where the `condition` is true. If no records meet the condition, the result is 0.
## Use case examples
In this use case, we calculate the total request duration for HTTP requests that returned a `200` status code.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize total_req_duration = sumif(req_duration_ms, status == '200')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20total_req_duration%20%3D%20sumif%28req_duration_ms%2C%20status%20%3D%3D%20%27200%27%29%22%7D)
**Output**
| total\_req\_duration |
| -------------------- |
| 145000 |
This query computes the total request duration (in milliseconds) for all successful HTTP requests (those with a status code of `200`).
In this example, we sum the span durations for the `frontend` service in OpenTelemetry traces.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize total_duration = sumif(duration, ['service.name'] == 'frontend')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20summarize%20total_duration%20%3D%20sumif%28duration%2C%20%5B%27service.name%27%5D%20%3D%3D%20%27frontend%27%29%22%7D)
**Output**
| total\_duration |
| --------------- |
| 32000 |
This query sums the span durations for traces related to the `frontend` service, providing insight into how long this service has been running over time.
Here, we calculate the total request duration for failed HTTP requests (those with status codes other than `200`).
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize total_req_duration_failed = sumif(req_duration_ms, status != '200')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20summarize%20total_req_duration_failed%20%3D%20sumif%28req_duration_ms%2C%20status%20%21%3D%20%27200%27%29%22%7D)
**Output**
| total\_req\_duration\_failed |
| ---------------------------- |
| 64000 |
This query computes the total request duration for all failed HTTP requests (where the status code isn’t `200`), which can be useful for security log analysis.
## List of related aggregations
* [**avgif**](/apl/aggregation-function/avgif): Computes the average of a numeric expression for records that meet a specified condition. Use `avgif` when you’re interested in the average value, not the total sum.
* [**countif**](/apl/aggregation-function/countif): Counts the number of records that satisfy a condition. Use `countif` when you need to know how many records match a specific criterion.
* [**minif**](/apl/aggregation-function/minif): Returns the minimum value of a numeric expression for records that meet a condition. Useful when you need the smallest value under certain criteria.
* [**maxif**](/apl/aggregation-function/maxif): Returns the maximum value of a numeric expression for records that meet a condition. Use `maxif` to identify the highest values under certain conditions.
# topk
Source: https://axiom.co/docs/apl/aggregation-function/topk
This page explains how to use the topk aggregation function in APL.
The `topk` aggregation in Axiom Processing Language (APL) allows you to identify the top `k` results based on a specified field. This is especially useful when you want to quickly analyze large datasets and extract the most significant values, such as the top-performing queries, most frequent errors, or highest latency requests.
Use `topk` to find the most common or relevant entries in datasets, especially in log analysis, telemetry data, and monitoring systems. This aggregation helps you focus on the most important data points, filtering out the noise.
The `topk` aggregation in APL is a statistical aggregation that returns estimated results. The estimation comes with the benefit of speed at the expense of accuracy. This means that `topk` is fast and light on resources even on a large or high-cardinality dataset, but it doesn’t provide precise results.
For completely accurate results, use the [`top` operator](/apl/tabular-operators/top-operator).
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have the equivalent of the `topk` function. You can achieve similar results with SPL’s `top` command which is equivalent to APL’s `top` operator. The `topk` function in APL behaves similarly by returning the top `k` values of a specified field, but its syntax is unique to APL.
The main difference between `top` (supported by both SPL and APL) and `topk` (supported only by APL) is that `topk` is estimated. This means that APL’s `topk` is faster, less resource intenstive, but less accurate than SPL’s `top`.
```sql Splunk example theme={null}
| top limit=5 status by method
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize topk(status, 5) by method
```
In ANSI SQL, identifying the top `k` rows often involves using the `ORDER BY` and `LIMIT` clauses. While the logic remains similar, APL’s `topk` simplifies this process by directly returning the top `k` values of a field in an aggregation.
The main difference between SQL’s solution and APL’s `topk` is that `topk` is estimated. This means that APL’s `topk` is faster, less resource intenstive, but less accurate than SQL’s combination of `ORDER BY` and `LIMIT` clauses.
```sql SQL example theme={null}
SELECT status, COUNT(*)
FROM sample_http_logs
GROUP BY status
ORDER BY COUNT(*) DESC
LIMIT 5;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize topk(status, 5)
```
## Usage
### Syntax
```kusto theme={null}
topk(Field, k)
```
### Parameters
* `Field`: The field or expression to rank the results by.
* `k`: The number of top results to return.
### Returns
A subset of the original dataset with the top `k` values based on the specified field.
## Use case examples
When analyzing HTTP logs, you can use the `topk` function to find the top 5 most frequent HTTP status codes.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize topk(status, 5)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%20%7C%20summarize%20topk\(status%2C%205\)%22%7D)
**Output**
| status | count\_ |
| ------ | ------- |
| 200 | 1500 |
| 404 | 400 |
| 500 | 200 |
| 301 | 150 |
| 302 | 100 |
This query groups the logs by HTTP status and returns the 5 most frequent statuses.
In OpenTelemetry traces, you can use `topk` to find the top five status codes by service.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize topk(['attributes.http.status_code'], 5) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20topk\(%5B'attributes.http.status_code'%5D%2C%205\)%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | attributes.http.status\_code | \_count |
| ------------- | ---------------------------- | ---------- |
| frontendproxy | 200 | 34,862,088 |
| | 203 | 3,095,223 |
| | 404 | 154,417 |
| | 500 | 153,823 |
| | 504 | 3,497 |
This query shows the top five status codes by service.
You can use `topk` in security log analysis to find the top 5 cities generating the most HTTP requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize topk(['geo.city'], 5)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%20%7C%20summarize%20topk\(%5B'geo.city'%5D%2C%205\)%22%7D)
**Output**
| geo.city | count\_ |
| -------- | ------- |
| New York | 500 |
| London | 400 |
| Paris | 350 |
| Tokyo | 300 |
| Berlin | 250 |
This query returns the top 5 cities based on the number of HTTP requests.
## List of related aggregations
* [top](/apl/tabular-operators/top-operator): Returns the top values based on a field without requiring a specific number of results (`k`), making it useful when you’re unsure how many top values to retrieve.
* [topkif](/apl/aggregation-function/topkif): Returns the top `k` results without filtering. Use topk when you don’t need to restrict your analysis to a subset.
* [sort](/apl/tabular-operators/sort-operator): Orders the dataset based on one or more fields, which is useful if you need a complete ordered list rather than the top `k` values.
* [extend](/apl/tabular-operators/extend-operator): Adds calculated fields to your dataset, which can be useful in combination with `topk` to create custom rankings.
* [count](/apl/aggregation-function/count): Aggregates the dataset by counting occurrences, often used in conjunction with `topk` to find the most common values.
# topkif
Source: https://axiom.co/docs/apl/aggregation-function/topkif
This page explains how to use the topkif aggregation in APL.
The `topkif` aggregation in Axiom Processing Language (APL) allows you to identify the top `k` values based on a specified field, while also applying a filter on another field. Use `topkif` when you want to find the most significant entries that meet specific criteria, such as the top-performing queries from a particular service, the most frequent errors for a specific HTTP method, or the highest latency requests from a specific country.
Use `topkif` when you need to focus on the most important filtered subsets of data, especially in log analysis, telemetry data, and monitoring systems. This aggregation helps you quickly zoom in on significant values without scanning the entire dataset.
The `topkif` aggregation in APL is a statistical aggregation that returns estimated results. The estimation provides the benefit of speed at the expense of precision. This means that `topkif` is fast and light on resources even on large or high-cardinality datasets but doesn’t provide completely accurate results.
For completely accurate results, use the [top operator](/apl/tabular-operators/top-operator) together with a filter.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have a direct equivalent to the `topkif` function. You can achieve similar results by using the top command combined with a where clause, which is closer to using APL’s top operator with a filter. However, APL’s `topkif` provides a more optimized, estimated solution when you want speed and efficiency.
```sql Splunk example theme={null}
| where method="GET" | top limit=5 status
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize topkif(status, 5, method == 'GET')
```
In ANSI SQL, identifying the top `k` rows filtered by a condition often involves a WHERE clause followed by ORDER BY and LIMIT. APL’s `topkif` simplifies this by combining the filtering and top-k selection in one function.
```sql SQL example theme={null}
SELECT status, COUNT(*)
FROM sample_http_logs
WHERE method = 'GET'
GROUP BY status
ORDER BY COUNT(*) DESC
LIMIT 5;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize topkif(status, 5, method == 'GET')
```
# Usage
## Syntax
```kusto theme={null}
topkif(Field, k, Condition)
```
## Parameters
* `Field`: The field or expression to rank the results by.
* `k`: The number of top results to return.
* `Condition`: A logical expression that specifies the filtering condition.
## Returns
A subset of the original dataset containing the top `k` values based on the specified field, after applying the filter condition.
# Use case examples
Use `topkif` when analyzing HTTP logs to find the top 5 most frequent HTTP status codes for GET requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize topkif(status, 5, method == 'GET')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20topkif\(status%2C%205%2C%20method%20%3D%3D%20'GET'\)%22%7D)
**Output**
| status | count\_ |
| ------ | ------- |
| 200 | 900 |
| 404 | 250 |
| 500 | 100 |
| 301 | 90 |
| 302 | 60 |
This query groups GET requests by HTTP status and returns the 5 most frequent statuses.
Use `topkif` in OpenTelemetry traces to find the top five services for server.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize topkif(['service.name'], 5, kind == 'server')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20topkif\(%5B'service.name'%5D%2C%205%2C%20kind%20%3D%3D%20'server'\)%22%7D)
**Output**
| service.name | count\_ |
| --------------- | ------- |
| frontend-proxy | 99,573 |
| frontend | 91,800 |
| product-catalog | 29,696 |
| image-provider | 25,223 |
| flagd | 10,336 |
This query shows the top five services filtered to server.
Use `topkif` in security log analysis to find the top 5 cities generating GET HTTP requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize topkif(['geo.city'], 5, method == 'GET')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20topkif\(%5B'geo.city'%5D%2C%205%2C%20method%20%3D%3D%20'GET'\)%22%7D)
**Output**
| geo.city | count\_ |
| -------- | ------- |
| New York | 300 |
| London | 250 |
| Paris | 200 |
| Tokyo | 180 |
| Berlin | 160 |
This query returns the top 5 cities generating the most GET HTTP requests.
# List of related aggregations
* [topk](/apl/aggregation-function/topk): Returns the top `k` results without filtering. Use topk when you don’t need to restrict your analysis to a subset.
* [top](/apl/tabular-operators/top-operator): Returns the top results based on a field with accurate results. Use top when precision is important.
* [sort](/apl/tabular-operators/sort-operator): Sorts the dataset based on one or more fields. Use sort if you need full ordered results.
* [extend](/apl/tabular-operators/extend-operator): Adds calculated fields to your dataset, useful before applying topkif to create new fields to rank.
* [count](/apl/aggregation-function/count): Counts occurrences in the dataset. Use count when you only need counts without focusing on the top entries.\`
# variance
Source: https://axiom.co/docs/apl/aggregation-function/variance
This page explains how to use the variance aggregation function in APL.
The `variance` aggregation function in APL calculates the variance of a numeric expression across a set of records. Variance is a statistical measurement that represents the spread of data points in a dataset. It’s useful for understanding how much variation exists in your data. In scenarios such as performance analysis, network traffic monitoring, or anomaly detection, `variance` helps identify outliers and patterns by showing how data points deviate from the mean.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In SPL, variance is computed using the `stats` command with the `var` function, whereas in APL, you can use `variance` for the same functionality.
```sql Splunk example theme={null}
| stats var(req_duration_ms) as variance
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize variance(req_duration_ms)
```
In ANSI SQL, variance is typically calculated using `VAR_POP` or `VAR_SAMP`. APL provides a simpler approach using the `variance` function without needing to specify population or sample.
```sql SQL example theme={null}
SELECT VAR_POP(req_duration_ms) FROM sample_http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize variance(req_duration_ms)
```
## Usage
### Syntax
```kusto theme={null}
summarize variance(Expression)
```
### Parameters
* `Expression`: A numeric expression or field for which you want to compute the variance. The expression should evaluate to a numeric data type.
### Returns
The function returns the variance (a numeric value) of the specified expression across the records.
## Use case examples
You can use the `variance` function to measure the variability of request durations, which helps in identifying performance bottlenecks or anomalies in web services.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize variance(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20variance\(req_duration_ms\)%22%7D)
**Output**
| variance\_req\_duration\_ms |
| --------------------------- |
| 1024.5 |
This query calculates the variance of request durations from a dataset of HTTP logs. A high variance indicates greater variability in request durations, potentially signaling performance issues.
For OpenTelemetry traces, `variance` can be used to measure how much span durations differ across service invocations, helping in performance optimization and anomaly detection.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize variance(duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20variance\(duration\)%22%7D)
**Output**
| variance\_duration |
| ------------------ |
| 1287.3 |
This query computes the variance of span durations across traces, which helps in understanding how consistent the service performance is. A higher variance might indicate unstable or inconsistent performance.
You can use the `variance` function on security logs to detect abnormal patterns in request behavior, such as unusual fluctuations in response times, which may point to potential security threats.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize variance(req_duration_ms) by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20variance\(req_duration_ms\)%20by%20status%22%7D)
**Output**
| status | variance\_req\_duration\_ms |
| ------ | --------------------------- |
| 200 | 1534.8 |
| 404 | 2103.4 |
This query calculates the variance of request durations grouped by HTTP status codes. High variance in certain status codes (e.g., 404 errors) can indicate network or application issues.
## List of related aggregations
* [**stdev**](/apl/aggregation-function/stdev): Computes the standard deviation, which is the square root of the variance. Use `stdev` when you need the spread of data in the same units as the original dataset.
* [**avg**](/apl/aggregation-function/avg): Computes the average of a numeric field. Combine `avg` with `variance` to analyze both the central tendency and the spread of data.
* [**count**](/apl/aggregation-function/count): Counts the number of records. Use `count` alongside `variance` to get a sense of data size relative to variance.
* [**percentile**](/apl/aggregation-function/percentile): Returns a value below which a given percentage of observations fall. Use `percentile` for a more detailed distribution analysis.
* [**max**](/apl/aggregation-function/max): Returns the maximum value. Use `max` when you are looking for extreme values in addition to variance to detect anomalies.
# varianceif
Source: https://axiom.co/docs/apl/aggregation-function/varianceif
This page explains how to use the varianceif aggregation function in APL.
The `varianceif` aggregation in APL calculates the variance of values that meet a specified condition. This is useful when you want to understand the variability of a subset of data without considering all data points. For example, you can use `varianceif` to compute the variance of request durations for HTTP requests that resulted in a specific status code or to track anomalies in trace durations for a particular service.
You can use the `varianceif` aggregation when analyzing logs, telemetry data, or security events where conditions on subsets of the data are critical to your analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you would use the `eval` function to filter data and calculate variance for specific conditions. In APL, `varianceif` combines the filtering and aggregation into a single function, making your queries more concise.
```sql Splunk example theme={null}
| eval filtered_var=if(status=="200",req_duration_ms,null())
| stats var(filtered_var)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize varianceif(req_duration_ms, status == '200')
```
In ANSI SQL, you typically use a `CASE` statement to apply conditional logic and then compute the variance. In APL, `varianceif` simplifies this by combining both the condition and the aggregation.
```sql SQL example theme={null}
SELECT VARIANCE(CASE WHEN status = '200' THEN req_duration_ms END)
FROM sample_http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize varianceif(req_duration_ms, status == '200')
```
## Usage
### Syntax
```kusto theme={null}
summarize varianceif(Expr, Predicate)
```
### Parameters
* `Expr`: The expression (numeric) for which you want to calculate the variance.
* `Predicate`: A boolean condition that determines which records to include in the calculation.
### Returns
Returns the variance of `Expr` for the records where the `Predicate` is true. If no records match the condition, it returns `null`.
## Use case examples
You can use the `varianceif` function to calculate the variance of HTTP request durations for requests that succeeded (`status == '200'`).
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize varianceif(req_duration_ms, status == '200')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20varianceif%28req_duration_ms%2C%20status%20%3D%3D%20'200'%29%22%7D)
**Output**
| varianceif\_req\_duration\_ms |
| ----------------------------- |
| 15.6 |
This query calculates the variance of request durations for all HTTP requests that returned a status code of 200 (successful requests).
You can use the `varianceif` function to monitor the variance in span durations for a specific service, such as the `frontend` service.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize varianceif(duration, ['service.name'] == 'frontend')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20varianceif%28duration%2C%20%5B'service.name'%5D%20%3D%3D%20'frontend'%29%22%7D)
**Output**
| varianceif\_duration |
| -------------------- |
| 32.7 |
This query calculates the variance in the duration of spans generated by the `frontend` service.
The `varianceif` function can also be used to track the variance in request durations for requests from a specific geographic region, such as requests from `geo.country == 'United States'`.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize varianceif(req_duration_ms, ['geo.country'] == 'United States')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20varianceif%28req_duration_ms%2C%20%5B'geo.country'%5D%20%3D%3D%20'United%20States'%29%22%7D)
**Output**
| varianceif\_req\_duration\_ms |
| ----------------------------- |
| 22.9 |
This query calculates the variance in request durations for requests originating from the United States.
## List of related aggregations
* [**avgif**](/apl/aggregation-function/avgif): Computes the average value of an expression for records that match a given condition. Use `avgif` when you want the average instead of variance.
* [**sumif**](/apl/aggregation-function/sumif): Returns the sum of values that meet a specified condition. Use `sumif` when you’re interested in totals, not variance.
* [**stdevif**](/apl/aggregation-function/stdevif): Returns the standard deviation of values based on a condition. Use `stdevif` when you want to measure dispersion using standard deviation instead of variance.
# All features of Axiom Processing Language (APL)
Source: https://axiom.co/docs/apl/apl-features
This page gives an overview about all the features of Axiom Processing Language (APL).
| Category | Feature | Description |
| :-------------------- | :-------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Aggregation function | [arg\_max](/apl/aggregation-function/arg-max) | Returns the row where an expression evaluates to the maximum value. |
| Aggregation function | [arg\_min](/apl/aggregation-function/arg-min) | Returns the row where an expression evaluates to the minimum value. |
| Aggregation function | [avg](/apl/aggregation-function/avg) | Returns an average value across the group. |
| Aggregation function | [avgif](/apl/aggregation-function/avgif) | Calculates the average value of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [count](/apl/aggregation-function/count) | Returns a count of the group without/with a predicate. |
| Aggregation function | [countif](/apl/aggregation-function/countif) | Returns a count of rows for which the predicate evaluates to true. |
| Aggregation function | [dcount](/apl/aggregation-function/dcount) | Returns an estimate for the number of distinct values that are taken by a scalar expression in the summary group. |
| Aggregation function | [dcountif](/apl/aggregation-function/dcountif) | Returns an estimate of the number of distinct values of an expression of rows for which the predicate evaluates to true. |
| Aggregation function | [histogram](/apl/aggregation-function/histogram) | Returns a timeseries heatmap chart across the group. |
| Aggregation function | [histogramif](/apl/aggregation-function/histogramif) | Creates a histogram for rows where a condition evaluates to true. |
| Aggregation function | [make\_list\_if](/apl/aggregation-function/make-list-if) | Creates a dynamic JSON object (array) of an expression values in the group for which the predicate evaluates to true. |
| Aggregation function | [make\_list](/apl/aggregation-function/make-list) | Creates a dynamic JSON object (array) of all the values of an expression in the group. |
| Aggregation function | [make\_set\_if](/apl/aggregation-function/make-set-if) | Creates a dynamic JSON object (array) of the set of distinct values that an expression takes in records for which the predicate evaluates to true. |
| Aggregation function | [make\_set](/apl/aggregation-function/make-set) | Creates a dynamic JSON array of the set of distinct values that an expression takes in the group. |
| Aggregation function | [max](/apl/aggregation-function/max) | Returns the maximum value across the group. |
| Aggregation function | [maxif](/apl/aggregation-function/maxif) | Calculates the maximum value of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [min](/apl/aggregation-function/min) | Returns the minimum value across the group. |
| Aggregation function | [minif](/apl/aggregation-function/minif) | Returns the minimum of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [percentile](/apl/aggregation-function/percentile) | Calculates the requested percentiles of the group and produces a timeseries chart. |
| Aggregation function | [percentileif](/apl/aggregation-function/percentileif) | Calculates the requested percentiles of the field for the rows where the predicate evaluates to true. |
| Aggregation function | [percentiles\_array](/apl/aggregation-function/percentiles-array) | Returns an array of numbers where each element is the value at the corresponding percentile. |
| Aggregation function | [percentiles\_arrayif](/apl/aggregation-function/percentiles-arrayif) | Returns an array of percentile values for the records that satisfy the condition. |
| Aggregation function | [rate](/apl/aggregation-function/rate) | Calculates the rate of values in a group per second. |
| Aggregation function | [spotlight](/apl/aggregation-function/spotlight) | Compares a selected set of events against a baseline and surface the most significant differences. |
| Aggregation function | [stdev](/apl/aggregation-function/stdev) | Calculates the standard deviation of an expression across the group. |
| Aggregation function | [stdevif](/apl/aggregation-function/stdevif) | Calculates the standard deviation of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [sum](/apl/aggregation-function/sum) | Calculates the sum of an expression across the group. |
| Aggregation function | [sumif](/apl/aggregation-function/sumif) | Calculates the sum of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [topk](/apl/aggregation-function/topk) | Calculates the top values of an expression across the group in a dataset. |
| Aggregation function | [topkif](/apl/aggregation-function/topkif) | Calculates the top values of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [variance](/apl/aggregation-function/variance) | Calculates the variance of an expression across the group. |
| Aggregation function | [varianceif](/apl/aggregation-function/varianceif) | Calculates the variance of an expression in records for which the predicate evaluates to true. |
| Aggregation function | [phrases](/apl/aggregation-function/phrases) | Extracts and counts common phrases or word sequences from text fields. |
| Array function | [array\_concat](/apl/scalar-functions/array-functions/array-concat) | Concatenates arrays into one. |
| Array function | [array\_extract](/apl/scalar-functions/array-functions/array-extract) | Extracts values from a nested array. |
| Array function | [array\_iff](/apl/scalar-functions/array-functions/array-iff) | Filters array by condition. |
| Array function | [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of) | Returns index of item in array. |
| Array function | [array\_length](/apl/scalar-functions/array-functions/array-length) | Returns length of array. |
| Array function | [array\_reverse](/apl/scalar-functions/array-functions/array-reverse) | Reverses array elements. |
| Array function | [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left) | Rotates array values to the left. |
| Array function | [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right) | Rotates array values to the right. |
| Array function | [array\_select\_dict](/apl/scalar-functions/array-functions/array-select-dict) | Selects dictionary from array of dictionaries. |
| Array function | [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left) | Shifts array values to the left. |
| Array function | [array\_shift\_right](/apl/scalar-functions/array-functions/array-shift-right) | Shifts array values to the right. |
| Array function | [array\_slice](/apl/scalar-functions/array-functions/array-slice) | Returns slice of an array. |
| Array function | [array\_sort\_asc](/apl/scalar-functions/array-functions/array-sort-asc) | Sorts an array in ascending order. |
| Array function | [array\_sort\_desc](/apl/scalar-functions/array-functions/array-sort-desc) | Sorts an array in descending order. |
| Array function | [array\_split](/apl/scalar-functions/array-functions/array-split) | Splits array by indices. |
| Array function | [array\_sum](/apl/scalar-functions/array-functions/array-sum) | Sums array elements. |
| Array function | [bag\_has\_key](/apl/scalar-functions/array-functions/bag-has-key) | Checks if dynamic object has a specific key. |
| Array function | [bag\_keys](/apl/scalar-functions/array-functions/bag-keys) | Returns keys of a dynamic property bag. |
| Array function | [bag\_pack](/apl/scalar-functions/array-functions/bag-pack) | Creates a dynamic property bag from key-value pairs. |
| Array function | [bag\_zip](/apl/scalar-functions/array-functions/bag-zip) | Combines two arrays of keys and values into a dynamic property bag. |
| Array function | [isarray](/apl/scalar-functions/array-functions/isarray) | Checks if value is an array. |
| Array function | [len](/apl/scalar-functions/array-functions/len) | Returns array or string length. |
| Array function | [pack\_array](/apl/scalar-functions/array-functions/pack-array) | Packs input into a dynamic array. |
| Array function | [pack\_dictionary](/apl/scalar-functions/array-functions/pack-dictionary) | Returns a dictionary from key-value mappings. |
| Array function | [strcat\_array](/apl/scalar-functions/array-functions/strcat-array) | Joins array elements into a string using a delimiter. |
| Conditional function | [case](/apl/scalar-functions/conditional-function#case) | Evaluates conditions and returns the first matched result. |
| Conditional function | [iff](/apl/scalar-functions/conditional-function#iff) | Returns one of two values based on predicate. |
| Conversion function | [dynamic\_to\_json](/apl/scalar-functions/conversion-functions#dynamic-to-json) | Converts dynamic value to JSON string. |
| Conversion function | [ensure\_field](/apl/scalar-functions/conversion-functions#ensure-field) | Returns value of field or typed null. |
| Conversion function | [isbool](/apl/scalar-functions/conversion-functions#isbool) | Checks if expression evaluates to boolean. |
| Conversion function | [toarray](/apl/scalar-functions/conversion-functions/toarray) | Converts to array. |
| Conversion function | [tobool](/apl/scalar-functions/conversion-functions#tobool) | Converts to boolean. |
| Conversion function | [todatetime](/apl/scalar-functions/conversion-functions#todatetime) | Converts to datetime. |
| Conversion function | [todouble](/apl/scalar-functions/conversion-functions#todouble%2C-toreal) | Converts to real. |
| Conversion function | [todynamic](/apl/scalar-functions/conversion-functions/todynamic) | Converts to dynamic. |
| Conversion function | [tohex](/apl/scalar-functions/conversion-functions#tohex) | Converts to hexadecimal string. |
| Conversion function | [toint](/apl/scalar-functions/conversion-functions#toint) | Converts to integer. |
| Conversion function | [tolong](/apl/scalar-functions/conversion-functions#tolong) | Converts to signed 64-bit long. |
| Conversion function | [toreal](/apl/scalar-functions/conversion-functions#todouble%2C-toreal) | Converts to real. |
| Conversion function | [tostring](/apl/scalar-functions/conversion-functions#tostring) | Converts to string. |
| Conversion function | [totimespan](/apl/scalar-functions/conversion-functions#totimespan) | Converts to timespan. |
| Datetime function | [ago](/apl/scalar-functions/datetime-functions#ago) | Subtracts timespan from current time. |
| Datetime function | [datetime\_add](/apl/scalar-functions/datetime-functions#datetime-add) | Adds amount to datetime. |
| Datetime function | [datetime\_diff](/apl/scalar-functions/datetime-functions#datetime-diff) | Difference between two datetimes. |
| Datetime function | [datetime\_part](/apl/scalar-functions/datetime-functions#datetime-part) | Extracts part of a datetime. |
| Datetime function | [dayofmonth](/apl/scalar-functions/datetime-functions#dayofmonth) | Day number in month. |
| Datetime function | [dayofweek](/apl/scalar-functions/datetime-functions#dayofweek) | Days since previous Sunday. |
| Datetime function | [dayofyear](/apl/scalar-functions/datetime-functions#dayofyear) | Day number in year. |
| Datetime function | [endofday](/apl/scalar-functions/datetime-functions#endofday) | Returns end of day. |
| Datetime function | [endofmonth](/apl/scalar-functions/datetime-functions#endofmonth) | Returns end of month. |
| Datetime function | [endofweek](/apl/scalar-functions/datetime-functions#endofweek) | Returns end of week. |
| Datetime function | [endofyear](/apl/scalar-functions/datetime-functions#endofyear) | Returns end of year. |
| Datetime function | [getmonth](/apl/scalar-functions/datetime-functions#getmonth) | Month of a datetime. |
| Datetime function | [getyear](/apl/scalar-functions/datetime-functions#getyear) | Year of a datetime. |
| Datetime function | [hourofday](/apl/scalar-functions/datetime-functions#hourofday) | Hour number of the day. |
| Datetime function | [monthofyear](/apl/scalar-functions/datetime-functions#monthofyear) | Month number of year. |
| Datetime function | [now](/apl/scalar-functions/datetime-functions#now) | Returns current UTC time. |
| Datetime function | [startofday](/apl/scalar-functions/datetime-functions#startofday) | Returns start of day. |
| Datetime function | [startofmonth](/apl/scalar-functions/datetime-functions#startofmonth) | Returns start of month. |
| Datetime function | [startofweek](/apl/scalar-functions/datetime-functions#startofweek) | Returns start of week. |
| Datetime function | [startofyear](/apl/scalar-functions/datetime-functions#startofyear) | Returns start of year. |
| Datetime function | [unixtime\_microseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime) | Converts microsecond Unix timestamp to datetime. |
| Datetime function | [unixtime\_milliseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime) | Converts millisecond Unix timestamp to datetime. |
| Datetime function | [unixtime\_nanoseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime) | Converts nanosecond Unix timestamp to datetime. |
| Datetime function | [unixtime\_seconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime) | Converts second Unix timestamp to datetime. |
| Datetime function | [week\_of\_year](/apl/scalar-functions/datetime-functions/week-of-year) | Returns the ISO 8601 week number from a datetime expression. |
| GenAI function | [genai\_concat\_contents](/apl/scalar-functions/genai-functions/genai-concat-contents) | Concatenates message contents from a GenAI conversation array. |
| GenAI function | [genai\_conversation\_turns](/apl/scalar-functions/genai-functions/genai-conversation-turns) | Counts the number of conversation turns in GenAI messages. |
| GenAI function | [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost) | Calculates the total cost for input and output tokens. |
| GenAI function | [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens) | Estimates the number of tokens in a text string. |
| GenAI function | [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response) | Extracts the assistant’s response from a GenAI conversation. |
| GenAI function | [genai\_extract\_function\_results](/apl/scalar-functions/genai-functions/genai-extract-function-results) | Extracts function call results from GenAI messages. |
| GenAI function | [genai\_extract\_system\_prompt](/apl/scalar-functions/genai-functions/genai-extract-system-prompt) | Extracts the system prompt from a GenAI conversation. |
| GenAI function | [genai\_extract\_tool\_calls](/apl/scalar-functions/genai-functions/genai-extract-tool-calls) | Extracts tool calls from GenAI messages. |
| GenAI function | [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt) | Extracts the user prompt from a GenAI conversation. |
| GenAI function | [genai\_get\_content\_by\_index](/apl/scalar-functions/genai-functions/genai-get-content-by-index) | Gets message content by index position. |
| GenAI function | [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role) | Gets message content by role. |
| GenAI function | [genai\_get\_pricing](/apl/scalar-functions/genai-functions/genai-get-pricing) | Gets pricing information for a specific AI model. |
| GenAI function | [genai\_get\_role](/apl/scalar-functions/genai-functions/genai-get-role) | Gets the role of a message at a specific index. |
| GenAI function | [genai\_has\_tool\_calls](/apl/scalar-functions/genai-functions/genai-has-tool-calls) | Checks if GenAI messages contain tool calls. |
| GenAI function | [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost) | Calculates the cost for input tokens. |
| GenAI function | [genai\_is\_truncated](/apl/scalar-functions/genai-functions/genai-is-truncated) | Checks if a GenAI response was truncated. |
| GenAI function | [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles) | Extracts all message roles from a GenAI conversation. |
| GenAI function | [genai\_output\_cost](/apl/scalar-functions/genai-functions/genai-output-cost) | Calculates the cost for output tokens. |
| Hash function | [hash\_md5](/apl/scalar-functions/hash-functions#hash-md5) | Returns MD5 hash. |
| Hash function | [hash\_sha1](/apl/scalar-functions/hash-functions#hash-sha1) | Returns SHA-1 hash. |
| Hash function | [hash\_sha256](/apl/scalar-functions/hash-functions#hash-sha256) | Returns SHA256 hash. |
| Hash function | [hash\_sha512](/apl/scalar-functions/hash-functions#hash-sha512) | Returns SHA512 hash. |
| Hash function | [hash](/apl/scalar-functions/hash-functions/hash) | Returns integer hash of input. |
| IP function | [format\_ipv4\_mask](/apl/scalar-functions/ip-functions/format-ipv4-mask) | Formats IPv4 and mask to CIDR. |
| IP function | [format\_ipv4](/apl/scalar-functions/ip-functions/format-ipv4) | Formats netmask into IPv4 string. |
| IP function | [geo\_info\_from\_ip\_address](/apl/scalar-functions/ip-functions/geo-info-from-ip-address) | Extracts geolocation from IP address. |
| IP function | [has\_any\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-any-ipv4-prefix) | Checks if IPv4 starts with any prefix. |
| IP function | [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4) | Checks if any of given IPv4s exist in column. |
| IP function | [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix) | Checks if IPv4 starts with specified prefix. |
| IP function | [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4) | Checks if IPv4 is valid and in source text. |
| IP function | [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare) | Compares two IPv4 addresses. |
| IP function | [ipv4\_is\_in\_any\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-any-range) | Checks if IPv4 is in any specified range. |
| IP function | [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range) | Checks if IPv4 is in a given range. |
| IP function | [ipv4\_is\_match](/apl/scalar-functions/ip-functions/ipv4-is-match) | Matches IPv4 against a pattern. |
| IP function | [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private) | Checks if IPv4 is private. |
| IP function | [ipv4\_netmask\_suffix](/apl/scalar-functions/ip-functions/ipv4-netmask-suffix) | Extracts netmask suffix. |
| IP function | [ipv6\_compare](/apl/scalar-functions/ip-functions/ipv6-compare) | Compares two IPv6 addresses. |
| IP function | [ipv6\_is\_in\_any\_range](/apl/scalar-functions/ip-functions/ipv6-is-in-any-range) | Checks if IPv6 is in any range. |
| IP function | [ipv6\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv6-is-in-range) | Checks if IPv6 is in range. |
| IP function | [ipv6\_is\_match](/apl/scalar-functions/ip-functions/ipv6-is-match) | Checks if IPv6 matches pattern. |
| IP function | [parse\_ipv4\_mask](/apl/scalar-functions/ip-functions/parse-ipv4-mask) | Converts IPv4 and mask to long integer. |
| IP function | [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4) | Converts IPv4 to long integer. |
| Logical operator | [!=](/apl/scalar-operators/logical-operators) | Returns `true` if either one (or both) of the operands are null, or they aren’t equal to each other. Otherwise, `false`. |
| Logical operator | [==](/apl/scalar-operators/logical-operators) | Returns `true` if both operands are non-null and equal to each other. Otherwise, `false`. |
| Logical operator | [and](/apl/scalar-operators/logical-operators) | Returns `true` if both operands are `true`. |
| Logical operator | [or](/apl/scalar-operators/logical-operators) | Returns `true` if one of the operands is `true`, regardless of the other operand. |
| Mathematical function | [abs](/apl/scalar-functions/mathematical-functions#abs) | Returns absolute value. |
| Mathematical function | [acos](/apl/scalar-functions/mathematical-functions#acos) | Returns arccosine of a number. |
| Mathematical function | [asin](/apl/scalar-functions/mathematical-functions#asin) | Returns arcsine of a number. |
| Mathematical function | [atan](/apl/scalar-functions/mathematical-functions#atan) | Returns arctangent of a number. |
| Mathematical function | [atan2](/apl/scalar-functions/mathematical-functions#atan2) | Returns angle between x-axis and point (y, x). |
| Mathematical function | [cos](/apl/scalar-functions/mathematical-functions#cos) | Returns cosine of a number. |
| Mathematical function | [cot](/apl/scalar-functions/mathematical-functions#cot) | Returns cotangent of a number. |
| Mathematical function | [degrees](/apl/scalar-functions/mathematical-functions#degrees) | Converts radians to degrees. |
| Mathematical function | [exp](/apl/scalar-functions/mathematical-functions#exp) | Returns e^x. |
| Mathematical function | [exp10](/apl/scalar-functions/mathematical-functions#exp10) | Returns 10^x. |
| Mathematical function | [exp2](/apl/scalar-functions/mathematical-functions#exp2) | Returns 2^x. |
| Mathematical function | [gamma](/apl/scalar-functions/mathematical-functions#gamma) | Returns gamma function of x. |
| Mathematical function | [isinf](/apl/scalar-functions/mathematical-functions#isinf) | Returns `true` if x is infinite. |
| Mathematical function | [isint](/apl/scalar-functions/mathematical-functions#isint) | Returns `true` if x is an integer. |
| Mathematical function | [isnan](/apl/scalar-functions/mathematical-functions#isnan) | Returns `true` if x is NaN. |
| Mathematical function | [log](/apl/scalar-functions/mathematical-functions#log) | Returns natural logarithm of x. |
| Mathematical function | [log10](/apl/scalar-functions/mathematical-functions#log10) | Returns base-10 logarithm. |
| Mathematical function | [log2](/apl/scalar-functions/mathematical-functions#log2) | Returns base-2 logarithm. |
| Mathematical function | [loggamma](/apl/scalar-functions/mathematical-functions#loggamma) | Returns log of absolute gamma function. |
| Mathematical function | [max\_of](/apl/scalar-functions/mathematical-functions/max-of) | Returns largest value among arguments. |
| Mathematical function | [min\_of](/apl/scalar-functions/mathematical-functions/min-of) | Returns smallest value among arguments. |
| Mathematical function | [not](/apl/scalar-functions/mathematical-functions#not) | Reverses boolean value. |
| Mathematical function | [pi](/apl/scalar-functions/mathematical-functions#pi) | Returns value of Pi. |
| Mathematical function | [pow](/apl/scalar-functions/mathematical-functions#pow) | Returns value raised to a power. |
| Mathematical function | [radians](/apl/scalar-functions/mathematical-functions#radians) | Converts degrees to radians. |
| Mathematical function | [rand](/apl/scalar-functions/mathematical-functions/rand) | Returns pseudo-random numbers between 0 (inclusive) and 1 (exclusive). |
| Mathematical function | [range](/apl/scalar-functions/mathematical-functions/range) | Returns a dynamic array of evenly spaced values. |
| Mathematical function | [round](/apl/scalar-functions/mathematical-functions#round) | Rounds value to given precision. |
| Mathematical function | [set\_difference](/apl/scalar-functions/mathematical-functions/set-difference) | Returns array difference. |
| Mathematical function | [set\_has\_element](/apl/scalar-functions/mathematical-functions/set-has-element) | Returns `true` if set contains an element. |
| Mathematical function | [set\_intersect](/apl/scalar-functions/mathematical-functions/set-intersect) | Returns array intersection. |
| Mathematical function | [set\_union](/apl/scalar-functions/mathematical-functions/set-union) | Returns array union. |
| Mathematical function | [sign](/apl/scalar-functions/mathematical-functions#sign) | Returns sign of number. |
| Mathematical function | [sin](/apl/scalar-functions/mathematical-functions#sin) | Returns sine of a number. |
| Mathematical function | [sqrt](/apl/scalar-functions/mathematical-functions#sqrt) | Returns square root of a number. |
| Mathematical function | [tan](/apl/scalar-functions/mathematical-functions#tan) | Returns tangent of a number. |
| Metadata function | [column\_ifexists](/apl/scalar-functions/metadata-functions/column-ifexists) | Checks if a field with a given name exists in the dataset. |
| Metadata function | [cursor\_current](/apl/scalar-functions/metadata-functions/cursor-current) | Retrieves a cursor string representing the current query execution point. |
| Metadata function | [ingestion\_time](/apl/scalar-functions/metadata-functions/ingestion-time) | Retrieves the timestamp of when each record was ingested into Axiom. |
| Numerical operator | [-](/apl/scalar-operators/numerical-operators) | Subtract. Example: `0.26 - 0.23` |
| Numerical operator | [!=](/apl/scalar-operators/numerical-operators) | Not equals. Example: `2 != 1` |
| Numerical operator | [!in](/apl/scalar-operators/numerical-operators) | Not equals to any of the elements. Example: `"bca" !in ("123", "345", "abc")` |
| Numerical operator | [\*](/apl/scalar-operators/numerical-operators) | Multiply. Example: `1s * 5`, `5 * 5` |
| Numerical operator | [/](/apl/scalar-operators/numerical-operators) | Divide. Example: `10m / 1s`, `4 / 2` |
| Numerical operator | [\<](/apl/scalar-operators/numerical-operators) | Less. Example: `1 < 2`, `1 <= 1` |
| Numerical operator | [\<=](/apl/scalar-operators/numerical-operators) | Less or Equal. Example: `5 <= 6` |
| Numerical operator | [%](/apl/scalar-operators/numerical-operators) | Modulo. Example: `10 % 3`, `5 % 2` |
| Numerical operator | [+](/apl/scalar-operators/numerical-operators) | Add. Example: `3.19 + 3.19`, `ago(10m) + 10m` |
| Numerical operator | [==](/apl/scalar-operators/numerical-operators) | Equals. Example: `3 == 3` |
| Numerical operator | [>](/apl/scalar-operators/numerical-operators) | Greater. Example: `0.23 > 0.22`, `now() > ago(1d)` |
| Numerical operator | [>=](/apl/scalar-operators/numerical-operators) | Greater or Equal. Example: `7 >= 6` |
| Numerical operator | [in](/apl/scalar-operators/numerical-operators) | Equals to one of the elements. Example: `"abc" in ("123", "345", "abc")` |
| Pair function | [find\_pair](/apl/scalar-functions/pair-functions/find-pair) | Searches an array of key-value pairs for the first pair matching specified patterns. |
| Pair function | [pair](/apl/scalar-functions/pair-functions#pair) | Creates a pair from a key and value. |
| Pair function | [parse\_pair](/apl/scalar-functions/pair-functions#parse-pair) | Parses a string to form a pair. |
| Rounding function | [bin\_auto](/apl/scalar-functions/rounding-functions#bin-auto) | Rounds values down to a bin based on query-provided size and alignment. |
| Rounding function | [bin](/apl/scalar-functions/rounding-functions#bin) | Rounds values down to a bin size. |
| Rounding function | [ceiling](/apl/scalar-functions/rounding-functions#ceiling) | Returns the smallest integer greater than or equal to the specified number. |
| Rounding function | [floor](/apl/scalar-functions/rounding-functions#floor) | Returns the largest integer less than or equal to the specified number. |
| SQL function | [format\_sql](/apl/scalar-functions/sql-functions#format-sql) | Converts parsed SQL data model back into SQL statement. |
| SQL function | [parse\_sql](/apl/scalar-functions/sql-functions#parse-sql) | Parses and analyzes SQL queries. |
| String function | [base64\_decode\_toarray](/apl/scalar-functions/string-functions/base64-decode-toarray) | Decodes a Base64-encoded string into an array of bytes. |
| String function | [base64\_decode\_tostring](/apl/scalar-functions/string-functions/base64-decode-tostring) | Decodes a base64 string to a UTF-8 string. |
| String function | [base64\_encode\_fromarray](/apl/scalar-functions/string-functions/base64-encode-fromarray) | Converts a sequence of bytes into a Base64-encoded string. |
| String function | [base64\_encode\_tostring](/apl/scalar-functions/string-functions/base64-encode-tostring) | Encodes a string as base64 string. |
| String function | [coalesce](/apl/scalar-functions/string-functions/coalesce) | Returns the first non-null/non-empty value from a list. |
| String function | [countof\_regex](/apl/scalar-functions/string-functions/countof-regex) | Counts occurrences of a regex in a string. |
| String function | [countof](/apl/scalar-functions/string-functions/countof) | Counts occurrences of a substring in a string. |
| String function | [extract\_all](/apl/scalar-functions/string-functions/extract-all) | Gets all matches for a regular expression from a text string. |
| String function | [extract](/apl/scalar-functions/string-functions/extract) | Gets a match for a regular expression from a text string. |
| String function | [format\_bytes](/apl/scalar-functions/string-functions/format-bytes) | Formats a number of bytes as a string including units. |
| String function | [format\_url](/apl/scalar-functions/string-functions/format-url) | Formats a string into a valid URL. |
| String function | [gettype](/apl/scalar-functions/string-functions/gettype) | Returns the runtime type of an argument. |
| String function | [indexof](/apl/scalar-functions/string-functions/indexof) | Returns index of the first occurrence of a substring. |
| String function | [isascii](/apl/scalar-functions/string-functions/isascii) | Returns `true` if all characters in an input string are ASCII characters. |
| String function | [isempty](/apl/scalar-functions/string-functions/isempty) | Returns `true` if the argument is empty or null. |
| String function | [isnotempty](/apl/scalar-functions/string-functions/isnotempty) | Returns `true` if the argument isn’t empty or null. |
| String function | [isnotnull](/apl/scalar-functions/string-functions/isnotnull) | Returns `true` if the argument isn’t null. |
| String function | [isnull](/apl/scalar-functions/string-functions/isnull) | Returns `true` if the argument is null. |
| String function | [parse\_bytes](/apl/scalar-functions/string-functions/parse-bytes) | Parses byte-size string to number of bytes. |
| String function | [parse\_csv](/apl/scalar-functions/string-functions/parse-csv) | Splits a CSV-formatted string into an array. |
| String function | [parse\_json](/apl/scalar-functions/string-functions/parse-json) | Parses a string as a JSON value. |
| String function | [parse\_url](/apl/scalar-functions/string-functions/parse-url) | Parses a URL string and returns parts in a dynamic object. |
| String function | [parse\_urlquery](/apl/scalar-functions/string-functions/parse-urlquery) | Parses a URL query string into key-value pairs. |
| String function | [quote](/apl/scalar-functions/string-functions/quote) | Returns a string representing the input enclosed in double quotes, with internal quotes and escape sequences handled appropriately. |
| String function | [replace\_regex](/apl/scalar-functions/string-functions/replace-regex) | Replaces regex matches with another string. |
| String function | [replace\_string](/apl/scalar-functions/string-functions/replace-string) | Replaces string matches with another string. |
| String function | [replace](/apl/scalar-functions/string-functions/replace) | Replaces all regex matches with another string. |
| String function | [reverse](/apl/scalar-functions/string-functions/reverse) | Reverses a string. |
| String function | [split](/apl/scalar-functions/string-functions/split) | Splits a string into an array using a delimiter. |
| String function | [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim) | Concatenates 2–64 arguments with a delimiter. |
| String function | [strcat](/apl/scalar-functions/string-functions/strcat) | Concatenates 1–64 arguments. |
| String function | [strcmp](/apl/scalar-functions/string-functions/strcmp) | Compares two strings. |
| String function | [string-size](/apl/scalar-functions/string-functions/string-size) | Returns the length, in characters, of the input string. |
| String function | [strip\_ansi\_escapes](/apl/scalar-functions/string-functions/strip-ansi-escapes) | Removes ANSI escape sequences from strings. |
| String function | [strlen](/apl/scalar-functions/string-functions/strlen) | Returns the length of a string. |
| String function | [strrep](/apl/scalar-functions/string-functions/strrep) | Repeats a string a given number of times. |
| String function | [substring](/apl/scalar-functions/string-functions/substring) | Extracts a substring. |
| String function | [tolower](/apl/scalar-functions/string-functions/tolower) | Converts string to lowercase. |
| String function | [totitle](/apl/scalar-functions/string-functions/totitle) | Converts string to title case. |
| String function | [toupper](/apl/scalar-functions/string-functions/toupper) | Converts string to uppercase. |
| String function | [translate](/apl/scalar-functions/string-functions/translate) | Substitutes characters in a string, one by one, based on their position in two input lists. |
| String function | [trim\_end\_regex](/apl/scalar-functions/string-functions/trim-end-regex) | Trims trailing characters using regex. |
| String function | [trim\_end](/apl/scalar-functions/string-functions/trim-end) | Trims trailing characters. |
| String function | [trim\_regex](/apl/scalar-functions/string-functions/trim-regex) | Trims characters matching a regex. |
| String function | [trim\_space](/apl/scalar-functions/string-functions/trim-space) | Removes all leading and trailing whitespace from a string. |
| String function | [trim\_start\_regex](/apl/scalar-functions/string-functions/trim-start-regex) | Trims leading characters using regex. |
| String function | [trim\_start](/apl/scalar-functions/string-functions/trim-start) | Trims leading characters. |
| String function | [trim](/apl/scalar-functions/string-functions/trim) | Trims leading/trailing characters. |
| String function | [unicode\_codepoints\_from\_string](/apl/scalar-functions/string-functions/unicode-codepoints-from-string) | Converts a UTF-8 string into an array of Unicode code points. |
| String function | [unicode\_codepoints\_to\_string](/apl/scalar-functions/string-functions/unicode-codepoints-to-string) | Converts an array of Unicode code points into a UTF-8 encoded string. |
| String function | [url\_decode](/apl/scalar-functions/string-functions/url-decode) | Decodes a URL-encoded string. |
| String function | [url\_encode](/apl/scalar-functions/string-functions/url-encode) | Encodes characters into a URL-friendly format. |
| String operator | [!=](/apl/scalar-operators/string-operators) | Not equals (case-sensitive). Example: `"abc" != "ABC"` |
| String operator | [!\~](/apl/scalar-operators/string-operators) | Not equals (case-insensitive). Example: `"aBc" !~ "xyz"` |
| String operator | [!contains\_cs](/apl/scalar-operators/string-operators) | RHS doesn’t occur in LHS (case-sensitive). Example: `"parentSpanId" !contains_cs "Id"` |
| String operator | [!contains](/apl/scalar-operators/string-operators) | RHS doesn’t occur in LHS (case-insensitive). Example: `"parentSpanId" !contains "abc"` |
| String operator | [!endswith\_cs](/apl/scalar-operators/string-operators) | RHS isn’t a closing subsequence of LHS (case-sensitive). Example: `"parentSpanId" !endswith_cs "Span"` |
| String operator | [!endswith](/apl/scalar-operators/string-operators) | RHS isn’t a closing subsequence of LHS (case-insensitive). Example: `"parentSpanId" !endswith "Span"` |
| String operator | [!has\_cs](/apl/scalar-operators/string-operators) | RHS isn’t a whole term in LHS (case-sensitive). Example: `"North America" !has_cs "America"` |
| String operator | [!has](/apl/scalar-operators/string-operators) | RHS isn’t a whole term in LHS (case-insensitive). Example: `"North America" !has "america"` |
| String operator | [!hasprefix\_cs](/apl/scalar-operators/string-operators) | LHS string doesn’t start with the RHS string (case-sensitive). Example: `"DOCS_file" !hasprefix_cs "DOCS"` |
| String operator | [!hasprefix](/apl/scalar-operators/string-operators) | LHS string doesn’t start with the RHS string (case-insensitive). Example: `"Admin_User" !hasprefix "Admin"` |
| String operator | [!hassuffix\_cs](/apl/scalar-operators/string-operators) | LHS string doesn’t end with the RHS string (case-sensitive). Example: `"Document.HTML" !hassuffix_cs ".HTML"` |
| String operator | [!hassuffix](/apl/scalar-operators/string-operators) | LHS string doesn’t end with the RHS string (case-insensitive). Example: `"documentation.docx" !hassuffix ".docx"` |
| String operator | [!in](/apl/scalar-operators/string-operators) | Not equals to any of the elements (case-sensitive). Example: `"bca" !in ("123", "345", "abc")` |
| String operator | [!in\~](/apl/scalar-operators/string-operators) | Not equals to any of the elements (case-insensitive). Example: `"bca" !in~ ("123", "345", "ABC")` |
| String operator | [!matches regex](/apl/scalar-operators/string-operators) | LHS doesn’t contain a match for RHS. Example: `"parentSpanId" !matches regex "g.*r"` |
| String operator | [!startswith\_cs](/apl/scalar-operators/string-operators) | RHS isn’t an initial subsequence of LHS (case-sensitive). Example: `"parentSpanId" !startswith_cs "parent"` |
| String operator | [!startswith](/apl/scalar-operators/string-operators) | RHS isn’t an initial subsequence of LHS (case-insensitive). Example: `"parentSpanId" !startswith "Id"` |
| String operator | [==](/apl/scalar-operators/string-operators) | Equals (case-sensitive). Example: `"aBc" == "aBc"` |
| String operator | [=\~](/apl/scalar-operators/string-operators) | Equals (case-insensitive). Example: `"abc" =~ "ABC"` |
| String operator | [contains\_cs](/apl/scalar-operators/string-operators) | RHS occurs as a subsequence of LHS (case-sensitive). Example: `"parentSpanId" contains_cs "Id"` |
| String operator | [contains](/apl/scalar-operators/string-operators) | RHS occurs as a subsequence of LHS (case-insensitive). Example: `"parentSpanId" contains "Span"` |
| String operator | [endswith\_cs](/apl/scalar-operators/string-operators) | RHS is a closing subsequence of LHS (case-sensitive). Example: `"parentSpanId" endswith_cs "Id"` |
| String operator | [endswith](/apl/scalar-operators/string-operators) | RHS is a closing subsequence of LHS (case-insensitive). Example: `"parentSpanId" endswith "Id"` |
| String operator | [has\_cs](/apl/scalar-operators/string-operators) | RHS is a whole term in LHS (case-sensitive). Example: `"North America" has_cs "America"` |
| String operator | [has](/apl/scalar-operators/string-operators) | RHS is a whole term in LHS (case-insensitive). Example: `"North America" has "america"` |
| String operator | [hasprefix\_cs](/apl/scalar-operators/string-operators) | LHS string starts with the RHS string (case-sensitive). Example: `"DOCS_file" hasprefix_cs "DOCS"` |
| String operator | [hasprefix](/apl/scalar-operators/string-operators) | LHS string starts with the RHS string (case-insensitive). Example: `"Admin_User" hasprefix "Admin"` |
| String operator | [hassuffix\_cs](/apl/scalar-operators/string-operators) | LHS string ends with the RHS string (case-sensitive). Example: `"Document.HTML" hassuffix_cs ".HTML"` |
| String operator | [hassuffix](/apl/scalar-operators/string-operators) | LHS string ends with the RHS string (case-insensitive). Example: `"documentation.docx" hassuffix ".docx"` |
| String operator | [in](/apl/scalar-operators/string-operators) | Equals to one of the elements (case-sensitive). Example: `"abc" in ("123", "345", "abc")` |
| String operator | [in\~](/apl/scalar-operators/string-operators) | Equals to one of the elements (case-insensitive). Example: `"abc" in~ ("123", "345", "ABC")` |
| String operator | [matches regex](/apl/scalar-operators/string-operators) | LHS contains a match for RHS. Example: `"parentSpanId" matches regex "g.*r"` |
| String operator | [startswith\_cs](/apl/scalar-operators/string-operators) | RHS is an initial subsequence of LHS (case-sensitive). Example: `"parentSpanId" startswith_cs "parent"` |
| String operator | [startswith](/apl/scalar-operators/string-operators) | RHS is an initial subsequence of LHS (case-insensitive). Example: `"parentSpanId" startswith "parent"` |
| Tabular operator | [count](/apl/tabular-operators/count-operator) | Returns an integer representing the total number of records in the dataset. |
| Tabular operator | [distinct](/apl/tabular-operators/distinct-operator) | Returns a dataset with unique values from the specified fields, removing any duplicate entries. |
| Tabular operator | [extend-valid](/apl/tabular-operators/extend-valid-operator) | Returns a table where the specified fields are extended with new values based on the given expression for valid rows. |
| Tabular operator | [extend](/apl/tabular-operators/extend-operator) | Returns the original dataset with one or more new fields appended, based on the defined expressions. |
| Tabular operator | [externaldata](/apl/tabular-operators/externaldata-operator) | Returns a table with the specified schema, containing data retrieved from an external source. |
| Tabular operator | [getschema](/apl/tabular-operators/getschema-operator) | Returns the schema of the input, including field names and their data types. |
| Tabular operator | [join](/apl/tabular-operators/join-operator) | Returns a dataset containing rows from two different tables based on conditions. |
| Tabular operator | [limit](/apl/tabular-operators/limit-operator) | Returns the top N rows from the input dataset. |
| Tabular operator | [lookup](/apl/tabular-operators/lookup-operator) | Returns a dataset where rows from one dataset are enriched with matching columns from a lookup table based on conditions. |
| Tabular operator | [make-series](/apl/tabular-operators/make-series) | Returns a dataset where the specified field is aggregated into a time series. |
| Tabular operator | [mv-expand](/apl/tabular-operators/mv-expand) | Returns a dataset where the specified field is expanded into multiple rows. |
| Tabular operator | [order](/apl/tabular-operators/order-operator) | Returns the input dataset, sorted according to the specified fields and order. |
| Tabular operator | [parse](/apl/tabular-operators/parse-operator) | Returns the input dataset with new fields added based on the specified parsing pattern. |
| Tabular operator | [parse-kv](/apl/tabular-operators/parse-kv) | Returns a dataset where key-value pairs are extracted from a string field into individual columns. |
| Tabular operator | [parse-where](/apl/tabular-operators/parse-where) | Returns a dataset where values from a string are extracted based on a pattern. |
| Tabular operator | [project-away](/apl/tabular-operators/project-away-operator) | Returns the input dataset excluding the specified fields. |
| Tabular operator | [project-keep](/apl/tabular-operators/project-keep-operator) | Returns a dataset with only the specified fields. |
| Tabular operator | [project-rename](/apl/tabular-operators/project-rename) | Returns a dataset where the specified field is renamed according to the specified pattern. |
| Tabular operator | [project-reorder](/apl/tabular-operators/project-reorder-operator) | Returns a table with the specified fields reordered as requested followed by any unspecified fields in their original order. |
| Tabular operator | [project](/apl/tabular-operators/project-operator) | Returns a dataset containing only the specified fields. |
| Tabular operator | [redact](/apl/tabular-operators/redact-operator) | Returns the input dataset with sensitive data replaced or hashed. |
| Tabular operator | [sample](/apl/tabular-operators/sample-operator) | Returns a table containing the specified number of rows, selected randomly from the input dataset. |
| Tabular operator | [search](/apl/tabular-operators/search-operator) | Returns all rows where the specified keyword appears in any field. |
| Tabular operator | [sort](/apl/tabular-operators/sort-operator) | Returns a table with rows ordered based on the specified fields. |
| Tabular operator | [summarize](/apl/tabular-operators/summarize-operator) | Returns a table where each row represents a unique combination of values from the by fields, with the aggregated results calculated for the other fields. |
| Tabular operator | [take](/apl/tabular-operators/take-operator) | Returns the specified number of rows from the dataset. |
| Tabular operator | [top](/apl/tabular-operators/top-operator) | Returns the top N rows from the dataset based on the specified sorting criteria. |
| Tabular operator | [union](/apl/tabular-operators/union-operator) | Returns all rows from the specified tables or queries. |
| Tabular operator | [where](/apl/tabular-operators/where-operator) | Returns a filtered dataset containing only the rows where the condition evaluates to true. |
| Time series function | [series\_abs](/apl/scalar-functions/time-series/series-abs) | Returns the absolute value of a series. |
| Time series function | [series\_acos](/apl/scalar-functions/time-series/series-acos) | Returns the inverse cosine (arccos) of a series. |
| Time series function | [series\_add](/apl/scalar-functions/time-series/series-add) | Performs element-wise addition between two series. |
| Time series function | [series\_asin](/apl/scalar-functions/time-series/series-asin) | Returns the inverse sine (arcsin) of a series. |
| Time series function | [series\_atan](/apl/scalar-functions/time-series/series-atan) | Returns the inverse tangent (arctan) of a series. |
| Time series function | [series\_ceiling](/apl/scalar-functions/time-series/series-ceiling) | Rounds each element up to the nearest integer. |
| Time series function | [series\_cos](/apl/scalar-functions/time-series/series-cos) | Returns the cosine of a series. |
| Time series function | [series\_cosine\_similarity](/apl/scalar-functions/time-series/series-cosine-similarity) | Calculates the cosine similarity between two series. |
| Time series function | [series\_divide](/apl/scalar-functions/time-series/series-divide) | Performs element-wise division between two series. |
| Time series function | [series\_dot\_product](/apl/scalar-functions/time-series/series-dot-product) | Calculates the dot product between two series. |
| Time series function | [series\_equals](/apl/scalar-functions/time-series/series-equals) | Compares each element in a series to a specified value and returns a boolean array. |
| Time series function | [series\_exp](/apl/scalar-functions/time-series/series-exp) | Calculates the exponential (e^x) of each element in a series. |
| Time series function | [series\_fft](/apl/scalar-functions/time-series/series-fft) | Performs a Fast Fourier Transform on a series, converting time-domain data into frequency-domain representation. |
| Time series function | [series\_fill\_backward](/apl/scalar-functions/time-series/series-fill-backward) | Fills missing values by propagating the last known value backward through the array. |
| Time series function | [series\_fill\_const](/apl/scalar-functions/time-series/series-fill-const) | Fills missing values with a specified constant value. |
| Time series function | [series\_fill\_forward](/apl/scalar-functions/time-series/series-fill-forward) | Fills missing values by propagating the first known value forward through the array. |
| Time series function | [series\_fill\_linear](/apl/scalar-functions/time-series/series-fill-linear) | Fills missing values using linear interpolation between known values. |
| Time series function | [series\_fir](/apl/scalar-functions/time-series/series-fir) | Applies a Finite Impulse Response filter to a series using a specified filter kernel. |
| Time series function | [series\_floor](/apl/scalar-functions/time-series/series-floor) | Rounds down each element in a series to the nearest integer. |
| Time series function | [series\_greater](/apl/scalar-functions/time-series/series-greater) | Returns the elements of a series that are greater than a specified value. |
| Time series function | [series\_greater\_equals](/apl/scalar-functions/time-series/series-greater-equals) | Returns the elements of a series that are greater than or equal to a specified value. |
| Time series function | [series\_ifft](/apl/scalar-functions/time-series/series-ifft) | Performs an Inverse Fast Fourier Transform on a series, converting frequency-domain data back into time-domain representation. |
| Time series function | [series\_iir](/apl/scalar-functions/time-series/series-iir) | Applies an Infinite Impulse Response filter to a series. |
| Time series function | [series\_less](/apl/scalar-functions/time-series/series-less) | Returns the elements of a series that are less than a specified value. |
| Time series function | [series\_less\_equals](/apl/scalar-functions/time-series/series-less-equals) | Returns the elements of a series that are less than or equal to a specified value. |
| Time series function | [series\_log](/apl/scalar-functions/time-series/series-log) | Returns the natural logarithm of each element in a series. |
| Time series function | [series\_magnitude](/apl/scalar-functions/time-series/series-magnitude) | Calculates the Euclidean norm (magnitude) of a series. |
| Time series function | [series\_max](/apl/scalar-functions/time-series/series-max) | Returns the maximum value from a series. |
| Time series function | [series\_min](/apl/scalar-functions/time-series/series-min) | Returns the minimum value from a series. |
| Time series function | [series\_multiply](/apl/scalar-functions/time-series/series-multiply) | Performs element-wise multiplication of two series. |
| Time series function | [series\_not\_equals](/apl/scalar-functions/time-series/series-not-equals) | Returns the elements of a series that aren’t equal to a specified value. |
| Time series function | [series\_pearson\_correlation](/apl/scalar-functions/time-series/series-pearson-correlation) | Calculates the Pearson correlation coefficient between two series. |
| Time series function | [series\_pow](/apl/scalar-functions/time-series/series-pow) | Raises each element in a series to a specified power. |
| Time series function | [series\_sign](/apl/scalar-functions/time-series/series-sign) | Returns the sign of each element in a series. |
| Time series function | [series\_sin](/apl/scalar-functions/time-series/series-sin) | Returns the sine of a series. |
| Time series function | [series\_stats](/apl/scalar-functions/time-series/series-stats) | Computes comprehensive statistical measures for a series. |
| Time series function | [series\_stats\_dynamic](/apl/scalar-functions/time-series/series-stats-dynamic) | Computes statistical measures and returns them in a dynamic object format. |
| Time series function | [series\_subtract](/apl/scalar-functions/time-series/series-subtract) | Performs element-wise subtraction between two series. |
| Time series function | [series\_sum](/apl/scalar-functions/time-series/series-sum) | Returns the sum of a series. |
| Time series function | [series\_tan](/apl/scalar-functions/time-series/series-tan) | Returns the tangent of a series. |
| Type function | [iscc](/apl/scalar-functions/type-functions/iscc) | Checks whether a value is a valid credit card (CC) number. |
| Type function | [isimei](/apl/scalar-functions/type-functions/isimei) | Checks whether a value is a valid International Mobile Equipment Identity (IMEI) number. |
| Type function | [ismap](/apl/scalar-functions/type-functions/ismap) | Checks whether a value is of the `dynamic` type and represents a mapping. |
| Type function | [isreal](/apl/scalar-functions/type-functions/isreal) | Checks whether a value is a real number. |
| Type function | [isstring](/apl/scalar-functions/type-functions/isstring) | Checks whether a value is a string. |
| Type function | [isutf8](/apl/scalar-functions/type-functions/isutf8) | Checks whether a value is a valid UTF-8 encoded sequence. |
# Map fields
Source: https://axiom.co/docs/apl/data-types/map-fields
This page explains what map fields are and how to query them.
Map fields are a special type of field that can hold a collection of nested key-value pairs within a single field. You can think of the content of a map field as a JSON object.
Axiom automatically creates map fields in datasets that use [OpenTelemetry](/send-data/opentelemetry) and you can create map fields yourself in any dataset.
## Benefits and drawbacks of map fields
Map fields help you manage high-dimensionality data by storing multiple key-value pairs within a single field. One of the benefits of map fields is that you can store additional attributes without adding more fields. This is particularly useful when the shape of your data is unpredictable (for example, additional attributes added by OpenTelemetry instrumentation). Using map fields means that you can avoid reaching the field limit of a dataset.
Use map fields in the following cases:
* You approach the dataset field limit.
* The shape of your data is unpredictable. For example, an OpenTelemetry instrumentation or another SDK creates objects with many keys.
* You work with feature flags or custom attributes that generate many fields.
Map fields reduce impact on field limits, but involve trade-offs in query efficiency and compression. The drawbacks of map fields are the following:
* Querying map fields uses more query-hours than querying conventional fields.
* In some cases, map fields don’t compress as well as conventional fields. For example, if there is little shared structure between map values. This means datasets with map fields can use more storage.
* You don’t have visibility into map fields from the schema. For example, autocomplete doesn’t know the properties inside the map field.
## Custom attributes in tracing datasets
If you use [OpenTelemetry](/send-data/opentelemetry) to send data to Axiom, you find some attributes in the `attributes.custom` map field. The reason is that instrumentation libraries can add hundreds or even thousands of arbitrary attributes to spans. Storing each custom attribute in a separate field would significantly increase the number of fields in your dataset. To keep the number of fields in your dataset under control, Axiom places all custom attributes in the single `attributes.custom` map field.
## Use map fields in queries
Map fields are particularly useful for handling nested data structures, such as data from `logfmt` format or other key-value formats with unpredictable structure.
The example query below uses the `http.protocol` property inside the `attributes.custom` map field to filter results:
```kusto theme={null}
['otel-demo-traces']
| where ['attributes.custom']['http.protocol'] == 'HTTP/1.1'
```
[Run in playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7b%22apl%22%3a%22%5b%27otel-demo-traces%27%5d%5cn%7c%20where%20%5b%27attributes.custom%27%5d%5b%27http.protocol%27%5d%20%3d%3d%20%27HTTP%2f1.1%27%22%2c%22queryOptions%22%3a%7b%22quickRange%22%3a%2230d%22%7d%7d)
## Create map fields using UI
To create a map field using the UI:
1. Go to the Datasets tab.
2. Select the dataset where you want to create the map field.
3. In the top right of the fields list, click **More > Create map field**.
4. In **Field name**, enter the full name of the field, including parent fields, if any. For example, `map_field_name`. For more information on syntax, see [Access properties of nested maps](#access-properties-of-nested-maps)
5. Click **Create map field**.
## Create map fields using API
To create a map field using the Axiom API, send a request to the [Create map field](/restapi/endpoints/createMapField) endpoint. For example:
```bash theme={null}
curl --request POST \
--url https://api.axiom.co/v2/datasets/DATASET_NAME/mapfields \
--header 'Authorization: Bearer API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"name": "MAP_FIELD"
}'
```
Replace `API_TOKEN` with the Axiom API token you have generated. For added security, store the API token in an environment variable.
Replace `DATASET_NAME` with the name of the Axiom dataset where you send your data.
Replace `MAP_FIELD` with the name of the field that you want to change to a map field.
## View map fields
To view map fields:
1. Go to the Datasets tab.
2. Select a dataset where you want to view map fields.
3. Map fields are labelled in the following way:
* **MAPPED** means that the field was previously an ordinary field but at some point its parent was changed to a map field. Axiom adds new events to the field as an attribute of the parent map field. Events you ingested before the change retain the ordinary structure.
* **UNUSED** means that the field is configured as a map field but you haven’t yet ingested data into it. Once ingested, data within this field won’t count toward your field limit.
* **REMOVED** means that the field was configured as a map field but at some point it was changed to an ordinary field. Axiom adds new events to the field as usual. Events you ingested before the change retain the map structure. To fully remove this field, first [trim your dataset](/reference/datasets#trim-dataset) to remove the time period when map data was ingested, and then [vacuum the fields](/reference/datasets#vacuum-fields).
## Access properties of nested maps
To access the properties of nested maps, use index notation, dot notation, or a mix of the two. If you use index notation for an entity, enclose the entity name in quotation marks (`'` or `"`) and square brackets (`[]`). For example:
* `where ['map_field']['property1']['property2'] == 14`
* `where map_field.property1.property2 == 14`
* `where ['map_field'].property1.property2 == 14`
If an entity name has spaces (` `), dots (`.`), or dashes (`-`), you can only use index notation for that entity. You can use dot notation for the other entities. For example:
* `where ['map.field']['property.name1']['property.name2'] == 14`
* `where ['map.field'].property1.property2 == 14`
In OTel traces, custom attributes are located in the `attributes.custom` map field. You can access them as `['attributes.custom']['header.Accept']`, for example. In this case, you don’t access the `Accept` field nested within the `header` field. What actually happens is that you access the field named `header.Accept` within the `attributes.custom` map field.
For more information on quoting field names, see [Entity names](/apl/entities/entity-names#quote-identifiers).
## Map fields and flattened fields
Within a dataset, the same fields can exist as flattened fields and as subfields of a map field.
For example, consider the following:
1. `geo` is initially not a map field.
2. You ingest the following:
```json theme={null}
{
"geo": {
"city": "Paris",
"country": "France"
}
}
```
This adds two flattened fields to the dataset that you can access as `['geo.city']` or `['geo.country']`.
3. You change `geo` to a map field through the UI or the API.
4. You ingest the following:
```json theme={null}
{
"geo": {
"city": "Paris",
"country": "France"
}
}
```
You use the same ingest JSON as before, but this adds the new subfields to the `geo` parent map field. You can access the subfields as `['geo']['city']` and `['geo']['country']`.
Axiom treats the flattened fields (`['geo.city']` and `['geo.country']`) and the subfields of the map field (`['geo']['city']` and `['geo']['country']`) as separate fields and doesn’t maintain a relationship between them.
Queries using `['geo.city']` access a field literally named `geo.city`, while `['geo']['city']` accesses the `city` key inside a `geo` map. These references aren’t equivalent.
To avoid confusion:
* Choose either a flattened or map-based structure when designing your schema.
* Be explicit in queries about which fields to include or exclude.
# Null values
Source: https://axiom.co/docs/apl/data-types/null-values
This page explains how APL represents missing values.
All scalar data types in APL have a special value that represents a missing value. This value is called the null value, or null.
## Null literals
The null value of a scalar type D is represented in the query language by the null literal D(null). The following query returns a single row full of null values:
```kusto theme={null}
print bool(null), datetime(null), dynamic(null), int(null), long(null), real(null), double(null), time(null)
```
## Predicates on null values
The scalar function [isnull()](/apl/scalar-functions/string-functions#isnull\(\)) can be used to determine if a scalar value is the null value. The corresponding function [isnotnull()](/apl/scalar-functions/string-functions#isnotnull\(\)) can be used to determine if a scalar value isn’t the null value.
## Equality and inequality of null values
* Equality (`==`): Applying the equality operator to two null values yields `bool(null)`. Applying the equality operator to a null value and a non-null value yields `bool(false)`.
* inequality(`!=`): Applying the inequality operator to two null values yields `bool(null)`. Applying the inequality operator to a null value and a non-null value yields `bool(true)`.
# Scalar data types
Source: https://axiom.co/docs/apl/data-types/scalar-data-types
This page explains the data types in APL.
Axiom Processing Language supplies a set of system data types that define all the types of data that can be used with APL.
The following table lists the data types supported by APL, alongside additional aliases you can use to refer to them.
| **Type** | **Additional names** | **gettype()** |
| ------------------------------------- | ----------------------------- | ------------------------------------------------------------ |
| [bool()](#the-bool-data-type) | **boolean** | **int8** |
| [datetime()](#the-datetime-data-type) | **date** | **datetime** |
| [dynamic()](#the-dynamic-data-type) | | **array** or **dictionary** or any other of the other values |
| [int()](#the-int-data-type) | **int** has an alias **long** | **int** |
| [long()](#the-long-data-type) | | **long** |
| [real()](#the-real-data-type) | **double** | **real** |
| [string()](#the-string-data-type) | | **string** |
| [timespan()](#the-timespan-data-type) | **time** | **timespan** |
## The bool data type
The bool (boolean) data type can have one of two states: `true` or `false` (internally encoded as 1 and 0, respectively), as well as the null value.
### bool literals
The bool data type has the following literals:
* true and bool(true): Representing trueness
* false and bool(false): Representing falsehood
* null and bool(null): Representing the null value
### bool operators
The `bool` data type supports the following operators: equality (`==`), inequality (`!=`), logical-and (`and`), and logical-or (`or`).
## The datetime data type
The datetime (date) data type represents an instant in time, typically expressed as a date and time of day. Values range from 00:00:00 (midnight), January 1, 0001 Anno Domini (Common Era) through 11:59:59 PM, December 31, 9999 AD (CE) in the Gregorian calendar.
### datetime literals
Literals of type **datetime** have the syntax **datetime** (`value`), where a number of formats are supported for value, as indicated by the following table:
| **Example** | **Value** |
| ------------------------------------------------------------ | -------------------------------------------------------------- |
| **datetime(2019-11-30 23:59:59.9)** **datetime(2015-12-31)** | Times are always in UTC. Omitting the date gives a time today. |
| **datetime(null)** | Check out [null values](/apl/data-types/null-values) |
| **now()** | The current time. |
| **now(-timespan)** | now()-timespan |
| **ago(timespan)** | now()-timespan |
**now()** and **ago()** indicate a `datetime` value compared with the moment in time when APL started to execute the query.
### Supported formats
Axiom supports the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format which is the standard format for representing dates and times in the Gregorian calendar.
| **Format** | **Example** |
| ------------------- | --------------------------- |
| %Y-%m-%dT%H:%M:%s%z | 2016-06-26T08:20:03.123456Z |
| %Y-%m-%dT%H:%M:%s | 2016-06-26T08:20:03.123456 |
| %Y-%m-%dT%H:%M | 2016-06-26T08:20 |
| %Y-%m-%d %H:%M:%s%z | 2016-10-06 15:55:55.123456Z |
| %Y-%m-%d %H:%M:%s | 2016-10-06 15:55:55 |
| %Y-%m-%d %H:%M | 2016-10-06 15:55 |
| %Y-%m-%d | 2014-11-08 |
## The dynamic data type
The **dynamic** scalar data type is special in that it can take on any value of other scalar data types from the list below, as well as arrays and property bags. Specifically, a **dynamic** value can be:
* null
* A value of any of the primitive scalar data types: **bool**, **datetime**, **int**, **long**, **real**, **string**, and **timespan**.
* An array of **dynamic** values, holding zero or more values with zero-based indexing.
* A property bag, holding zero or more key-value pairs.
### Dynamic literals
A literal of type dynamic looks like this:
dynamic (`Value`)
Value can be:
* null, in which case the literal represents the null dynamic value: **dynamic(null)**.
* Another scalar data type literal, in which case the literal represents the **dynamic** literal of the "inner" type. For example, **dynamic(6)** is a dynamic value holding the value 6 of the long scalar data type.
* An array of dynamic or other literals: \[`ListOfValues`]. For example, dynamic(\[3, 4, "bye"]) is a dynamic array of three elements, two **long** values and one **string** value.
* A property bag: \{`Name`=`Value ...`}. For example, `dynamic(\{"a":1, "b":\{"a":2\}\})` is a property bag with two slots, a, and b, with the second slot being another property bag.
## The int data type
The **int** data type represents a signed, 64-bit wide, integer.
The special form **int(null)** represents the [null value.](/apl/data-types/null-values)
**int** has an alias **[long](/apl/data-types/scalar-data-types#the-long-data-type)**
## The long data type
The **long** data type represents a signed, 64-bit wide, integer.
### long literals
Literals of the long data type can be specified in the following syntax:
long(`Value`)
Where Value can take the following forms:
* One more or digits, in which case the literal value is the decimal representation of these digits. For example, **long(11)** is the number eleven of type long.
* A minus (`-`) sign followed by one or more digits. For example, **long(-3)** is the number minus three of type **long**.
* null, in which case this is the [null value](/apl/data-types/null-values) of the **long** data type. Thus, the null value of type **long** is **long(null)**.
## The real data type
The **real** data type represents a 64-bit wide, double-precision, floating-point number.
## The string data type
The **string** data type represents a sequence of zero or more [Unicode](https://home.unicode.org/) characters.
### String literals
There are several ways to encode literals of the **string** data type in a query text:
* Enclose the string in double-quotes(`"`): "This is a string literal. Single quote characters (') don’t require escaping. Double quote characters (") are escaped by a backslash (\\)"
* Enclose the string in single-quotes (`'`): Another string literal. Single quote characters (') require escaping by a backslash (\\). Double quote characters (") don’t require escaping.
In the two representations above, the backslash (`\`) character indicates escaping. The backslash is used to escape the enclosing quote characters, tab characters (`\t`), newline characters (`\n`), and itself (`\\`).
### Raw string literals
Raw string literals are also supported. In this form, the backslash character (`\`) stands for itself, and doesn’t denote an escape sequence.
* Enclosed in double-quotes (`""`): `@"This is a raw string literal"`
* Enclose in single-quotes (`'`): `@'This is a raw string literal'`
Raw strings are particularly useful for regexes where you can use `@"^[\d]+$"` instead of `"^[\\d]+$"`.
## The timespan data type
The **timespan** `(time)` data type represents a time interval.
## timespan literals
Literals of type **timespan** have the syntax **timespan(value)**, where a number of formats are supported for value, as indicated by the following table:
| **Value** | **length of time** |
| --------------- | ------------------ |
| '`2d` | 2 days |
| `1.5h` | 1.5 hour |
| `30m` | 30 minutes |
| `10s` | 10 seconds |
| `timespan(15s)` | 15 seconds |
| `0.1s` | 0.1 second |
| `timespan(2d)` | 2 days |
## Type conversions
APL provides a set of functions to convert values between different scalar data types. These conversion functions allow you to convert a value from one type to another.
Some of the commonly used conversion functions include:
* `tobool()`: Converts input to boolean representation.
* `todatetime()`: Converts input to datetime scalar.
* `todouble()` or `toreal()`: Converts input to a value of type real.
* `tostring()`: Converts input to a string representation.
* `totimespan()`: Converts input to timespan scalar.
* `tolong()`: Converts input to long (signed 64-bit) number representation.
* `toint()`: Converts input to an integer value (signed 64-bit) number representation.
For a complete list of conversion functions and their detailed descriptions and examples, refer to the [Conversion functions](/apl/scalar-functions/conversion-functions) documentation.
# Entity names
Source: https://axiom.co/docs/apl/entities/entity-names
This page explains how to use entity names in your APL query.
APL entities (datasets, tables, columns, and operators) are named. For example, two fields or columns in the same dataset can have the same name if the casing is different, and a table and a dataset may have the same name because they aren’t in the same scope.
## Columns
* Column names are case-sensitive for resolving purposes and they have a specific position in the dataset’s collection of columns.
* Column names are unique within a dataset and table.
* In queries, columns are generally referenced by name only. They can only appear in expressions, and the query operator under which the expression appears determines the table or tabular data stream.
## Identifier naming rules
Axiom uses identifiers to name various entities. Valid identifier names follow these rules:
* Between 1 and 1024 characters long.
* Allowed characters:
* Alphanumeric characters (letters and digits)
* Underscore (`_`)
* Space (` `)
* Dot (`.`)
* Dash (`-`)
Identifier names are case-sensitive.
## Quote identifiers
Quote an identifier in your APL query if any of the following is true:
* The identifier name contains at least one of the following special characters:
* Space (` `)
* Dot (`.`)
* Dash (`-`)
* The identifier name is identical to one of the reserved keywords of the APL query language. For example, `project` or `where`.
If any of the above is true, you must quote the identifier by enclosing it in quotation marks (`'` or `"`) and square brackets (`[]`). For example, `['my-field']`.
If none of the above is true, you don’t need to quote the identifier in your APL query. For example, `myfield`. In this case, quoting the identifier name is optional.
# Migrate from SQL to APL
Source: https://axiom.co/docs/apl/guides/migrating-from-sql-to-apl
This guide helps you migrate SQL to APL, helping you understand key differences and providing you with query examples.
## Introduction
As data grows exponentially, organizations are continuously seeking more efficient and powerful tools to manage and analyze their data. The Query tab, which utilizes the Axiom Processing Language (APL), is one such service that offers fast, scalable, and interactive data exploration capabilities.
This tutorial helps you migrate SQL to APL, helping you understand key differences and providing you with query examples.
## Introduction to Axiom Processing Language (APL)
Axiom Processing Language (APL) is the language used by the Query tab, a fast and highly scalable data exploration service. APL is optimized for real-time and historical data analytics, making it a suitable choice for various data analysis tasks.
**Tabular operators**: In APL, there are several tabular operators that help you manipulate and filter data, similar to SQL’s SELECT, FROM, WHERE, GROUP BY, and ORDER BY clauses. Some of the commonly used tabular operators are:
* `extend`: Adds new columns to the result set.
* `project`: Selects specific columns from the result set.
* `where`: Filters rows based on a condition.
* `summarize`: Groups and aggregates data similar to the GROUP BY clause in SQL.
* `sort`: Sorts the result set based on one or more columns, similar to ORDER BY in SQL.
## Key differences between SQL and APL
While SQL and APL are query languages, there are some key differences to consider:
* APL is designed for querying large volumes of structured, semi-structured, and unstructured data.
* APL is a pipe-based language, meaning you can chain multiple operations using the pipe operator (`|`) to create a data transformation flow.
* APL doesn’t use SELECT, and FROM clauses like SQL. Instead, it uses keywords such as summarize, extend, where, and project.
* APL is case-sensitive, whereas SQL isn’t.
## Benefits of migrating from SQL to APL:
* **Time Series Analysis:** APL is particularly strong when it comes to analyzing time-series data (logs, telemetry data, etc.). It has a rich set of operators designed specifically for such scenarios, making it much easier to handle time-based analysis.
* **Pipelining:** APL uses a pipelining model, much like the UNIX command line. You can chain commands together using the pipe (`|`) symbol, with each command operating on the results of the previous command. This makes it very easy to write complex queries.
* **Easy to Learn:** APL is designed to be simple and easy to learn, especially for those already familiar with SQL. It doesn’t require any knowledge of database schemas or the need to specify joins.
* **Scalability:** APL is a more scalable platform than SQL. This means that it can handle larger amounts of data.
* **Flexibility:** APL is a more flexible platform than SQL. This means that it can be used to analyze different types of data.
* **Features:** APL offers more features and capabilities than SQL. This includes features such as real-time analytics, and time-based analysis.
## Basic APL Syntax
A basic APL query follows this structure:
```kusto theme={null}
|
|
|
|
```
## Query Examples
Let’s see some examples of how to convert SQL queries to APL.
## SELECT with a simple filter
**SQL:**
```sql theme={null}
SELECT *
FROM [Sample-http-logs]
WHERE method = 'GET';
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where method == 'GET'
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20method%20==%20%27GET%27%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## COUNT with GROUP BY
**SQL:**
```sql theme={null}
SELECT Country, COUNT(*)
FROM [Sample-http-logs]
GROUP BY method;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize count() by method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20count\(\)%20by%20method%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Top N results
**SQL:**
```sql theme={null}
SELECT TOP 10 Status, Method
FROM [Sample-http-logs]
ORDER BY Method DESC;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| top 10 by method desc
| project status, method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|top%2010%20by%20method%20desc%20\n|%20project%20status,%20method%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Simple filtering and projection
**SQL:**
```sql theme={null}
SELECT method, status, geo.country
FROM [Sample-http-logs]
WHERE resp_header_size_bytes >= 18;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where resp_header_size_bytes >= 18
| project method, status, ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|where%20resp_header_size_bytes%20%3E=18%20\n|%20project%20method,%20status,%20\[%27geo.country%27]%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## COUNT with a HAVING clause
**SQL:**
```sql theme={null}
SELECT geo.country
FROM [Sample-http-logs]
GROUP BY geo.country
HAVING COUNT(*) > 100;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize count() by ['geo.country']
| where count_ > 100
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20count\(\)%20by%20\[%27geo.country%27]\n|%20where%20count_%20%3E%20100%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Multiple Aggregations
**SQL:**
```sql theme={null}
SELECT geo.country,
COUNT(*) AS TotalRequests,
AVG(req_duration_ms) AS AverageRequest,
MIN(req_duration_ms) AS MinRequest,
MAX(req_duration_ms) AS MaxRequest
FROM [Sample-http-logs]
GROUP BY geo.country;
```
**APL:**
```kusto theme={null}
Users
| summarize TotalRequests = count(),
AverageRequest = avg(req_duration_ms),
MinRequest = min(req_duration_ms),
MaxRequest = max(req_duration_ms) by ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20totalRequests%20=%20count\(\),%20Averagerequest%20=%20avg\(req_duration_ms\),%20MinRequest%20=%20min\(req_duration_ms\),%20MaxRequest%20=%20max\(req_duration_ms\)%20by%20\[%27geo.country%27]%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
### Sum of a column
**SQL:**
```sql theme={null}
SELECT SUM(resp_body_size_bytes) AS TotalBytes
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
[‘sample-http-logs’]
| summarize TotalBytes = sum(resp_body_size_bytes)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20TotalBytes%20=%20sum\(resp_body_size_bytes\)%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
### Average of a column
**SQL:**
```sql theme={null}
SELECT AVG(req_duration_ms) AS AverageRequest
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize AverageRequest = avg(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20AverageRequest%20=%20avg\(req_duration_ms\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Minimum and Maximum Values of a column
**SQL:**
```sql theme={null}
SELECT MIN(req_duration_ms) AS MinRequest, MAX(req_duration_ms) AS MaxRequest
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize MinRequest = min(req_duration_ms), MaxRequest = max(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20MinRequest%20=%20min\(req_duration_ms\),%20MaxRequest%20=%20max\(req_duration_ms\)%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Count distinct values
**SQL:**
```sql theme={null}
SELECT COUNT(DISTINCT method) AS UniqueMethods
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize UniqueMethods = dcount(method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|summarize%20UniqueMethods%20=%20dcount\(method\)%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Standard deviation of a data
**SQL:**
```sql theme={null}
SELECT STDDEV(req_duration_ms) AS StdDevRequest
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize StdDevRequest = stdev(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20stdDEVRequest%20=%20stdev\(req_duration_ms\)%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Variance of a data
**SQL:**
```sql theme={null}
SELECT VAR(req_duration_ms) AS VarRequest
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize VarRequest = variance(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20VarRequest%20=%20variance\(req_duration_ms\)%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Multiple aggregation functions
**SQL:**
```sql theme={null}
SELECT COUNT(*) AS TotalDuration, SUM(req_duration_ms) AS TotalDuration, AVG(Price) AS AverageDuration
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize TotalOrders = count(), TotalDuration = sum( req_duration_ms), AverageDuration = avg(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20TotalOrders%20=%20count\(\),%20TotalDuration%20=%20sum\(req_duration_ms\),%20AverageDuration%20=%20avg\(req_duration_ms\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Aggregation with GROUP BY and ORDER BY
**SQL:**
```sql theme={null}
SELECT status, COUNT(*) AS TotalStatus, SUM(resp_header_size_bytes) AS TotalRequest
FROM [Sample-http-logs];
GROUP BY status
ORDER BY TotalSpent DESC;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize TotalStatus = count(), TotalRequest = sum(resp_header_size_bytes) by status
| order by TotalRequest desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20TotalStatus%20=%20count\(\),%20TotalRequest%20=%20sum\(resp_header_size_bytes\)%20by%20status\n|%20order%20by%20TotalRequest%20desc%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Count with a condition
**SQL:**
```sql theme={null}
SELECT COUNT(*) AS HighContentStatus
FROM [Sample-http-logs];
WHERE resp_header_size_bytes > 1;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where resp_header_size_bytes > 1
| summarize HighContentStatus = count()
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20resp_header_size_bytes%20%3E%201\n|%20summarize%20HighContentStatus%20=%20count\(\)%20%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Aggregation with HAVING
**SQL:**
```sql theme={null}
SELECT Status
FROM [Sample-http-logs];
GROUP BY Status
HAVING COUNT(*) > 10;
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| summarize OrderCount = count() by status
| where OrderCount > 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20OrderCount%20=%20count\(\)%20by%20status\n|%20where%20OrderCount%20%3E%2010%20%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Count occurrences of a value in a field
**SQL:**
```sql theme={null}
SELECT content_type, COUNT(*) AS RequestCount
FROM [Sample-http-logs];
WHERE content_type = ‘text/csv’;
```
**APL:**
```kusto theme={null}
['sample-http-logs'];
| where content_type == 'text/csv'
| summarize RequestCount = count()
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20content_type%20==%20%27text/csv%27%20\n|%20summarize%20RequestCount%20=%20count\(\)%20%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## String Functions:
## Length of a string
**SQL:**
```sql theme={null}
SELECT LEN(Status) AS NameLength
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend NameLength = strlen(status)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20NameLength%20=%20strlen\(status\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Concatentation
**SQL:**
```sql theme={null}
SELECT CONCAT(content_type, ' ', method) AS FullLength
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend FullLength = strcat(content_type, ' ', method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20FullLength%20=%20strcat\(content_type,%20%27%20%27,%20method\)%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Substring
**SQL:**
```sql theme={null}
SELECT SUBSTRING(content_type, 1, 10) AS ShortDescription
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend ShortDescription = substring(content_type, 0, 10)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20ShortDescription%20=%20substring\(content_type,%200,%2010\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Left and Right
**SQL:**
```sql theme={null}
SELECT LEFT(content_type, 3) AS LeftTitle, RIGHT(content_type, 3) AS RightTitle
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend LeftTitle = substring(content_type, 0, 3), RightTitle = substring(content_type, strlen(content_type) - 3, 3)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20LeftTitle%20=%20substring\(content_type,%200,%203\),%20RightTitle%20=%20substring\(content_type,%20strlen\(content_type\)%20-%203,%203\)%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Replace
**SQL:**
```sql theme={null}
SELECT REPLACE(StaTUS, 'old', 'new') AS UpdatedStatus
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend UpdatedStatus = replace('old', 'new', status)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20UpdatedStatus%20=%20replace\(%27old%27,%20%27new%27,%20status\)%20%20%22,%22queryOptions%22:\{%22quickRange%22:%2215d%22}})
## Upper and Lower
**SQL:**
```sql theme={null}
SELECT UPPER(FirstName) AS UpperFirstName, LOWER(LastName) AS LowerLastName
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| project upperFirstName = toupper(content_type), LowerLastNmae = tolower(status)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20project%20upperFirstName%20=%20toupper\(content_type\),%20LowerLastNmae%20=%20tolower\(status\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## LTrim and RTrim
**SQL:**
```sql theme={null}
SELECT LTRIM(content_type) AS LeftTrimmedFirstName, RTRIM(content_type) AS RightTrimmedLastName
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend LeftTrimmedFirstName = trim_start(' ', content_type), RightTrimmedLastName = trim_end(' ', content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20project%20LeftTrimmedFirstName%20=%20trim_start\(%27%27,%20content_type\),%20RightTrimmedLastName%20=%20trim_end\(%27%27,%20content_type\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Trim
**SQL:**
```sql theme={null}
SELECT TRIM(content_type) AS TrimmedFirstName
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend TrimmedFirstName = trim(' ', content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20TrimmedFirstName%20=%20trim\(%27%20%27,%20content_type\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Reverse
**SQL:**
```sql theme={null}
SELECT REVERSE(Method) AS ReversedFirstName
FROM [Sample-http-logs];
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend ReversedFirstName = reverse(method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20project%20ReservedFirstnName%20=%20reverse\(method\)%20%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Case-insensitive search
**SQL:**
```sql theme={null}
SELECT Status, Method
FROM “Sample-http-logs”
WHERE LOWER(Method) LIKE 'get’';
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where tolower(method) contains 'GET'
| project status, method
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20tolower\(method\)%20contains%20%27GET%27\n|%20project%20status,%20method%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Take the First Step Today: Dive into APL
The journey from SQL to APL might seem daunting at first, but with the right approach, it can become an empowering transition. It’s about expanding your data query capabilities to leverage the advanced, versatile, and fast querying infrastructure that APL provides. In the end, the goal is to enable you to draw more value from your data, make faster decisions, and ultimately propel your business forward.
Try converting some of your existing SQL queries to APL and observe the performance difference. Explore the Axiom Processing Language and start experimenting with its unique features.
**Happy querying!**
# Migrate from Sumo Logic Query Language to APL
Source: https://axiom.co/docs/apl/guides/migrating-from-sumologic-to-apl
This guide dives into why APL could be a superior choice for your data needs, and the differences between Sumo Logic and APL.
## Introduction
In the sphere of data analytics and log management, being able to query data efficiently and effectively is of paramount importance.
This guide dives into why APL could be a superior choice for your data needs, the differences between Sumo Logic and APL, and the potential benefits you could reap from migrating from Sumo Logic to APL. Let’s explore the compelling case for APL as a robust, powerful tool for handling your complex data querying requirements.
APL is powerful and flexible and uses a pipe (`|`) operator for chaining commands, and it provides a richer set of functions and operators for more complex queries.
## Benefits of Migrating from SumoLogic to APL
* **Scalability and Performance:** APL was built with scalability in mind. It handles very large volumes of data more efficiently and provides quicker query execution compared to Sumo Logic, making it a suitable choice for organizations with extensive data requirements. APL is designed for high-speed data ingestion, real-time analytics, and providing insights across structured, semi-structured data. It’s also optimized for time-series data analysis, making it highly efficient for log and telemetry data.
* **Advanced Analytics Capabilities:** With APL’s support for aggregation and conversion functions and more advanced statistical visualization, organizations can derive more sophisticated insights from their data.
## Query Examples
Let’s see some examples of how to convert SumoLogic queries to APL.
## Parse, and Extract Operators
Extract `from` and `to` fields. For example, if a raw event contains `From: Jane To: John,` then `from=Jane and to=John.`
**Sumo Logic:**
```bash theme={null}
* | parse "From: * To: *" as (from, to)
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend (method) == extract("From: (.*?) To: (.*)", 1, method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20\(method\)%20==%20extract\(%22From:%20\(.*?\)%20To:%20\(.*\)%22,%201,%20method\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Extract Source IP with Regex
In this section, we will utilize a regular expression to identify the four octets of an IP address. This will help us efficiently extract the source IP addresses from the data.
**Sumo Logic:**
```bash theme={null}
*| parse regex "(\\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend ip = extract("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})", 1, "23.45.67.90")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20ip%20=%20extract\(%22\(\\\d\{1,3}\\\\.\\\d\{1,3}\\\\.\\\d\{1,3}\\\\.\\\d\{1,3}\)%22,%201,%20%2223.45.67.90%22\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Extract Visited URLs
This section focuses on identifying all URL addresses visited and extracting them to populate the "url" field. This method provides an organized way to track user activity using APL.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "GET * " as url
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where method == "GET"
| project url = extract(@"(\w+)", 1, method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20where%20method%20%3D%3D%20%5C%22GET%5C%22%5Cn%7C%20project%20url%20%3D%20extract\(%40%5C%22\(%5C%5Cw%2B\)%5C%22%2C%201%2C%20method\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
## Extract Data from Source Category Traffic
This section aims to identify and analyze traffic originating from the Source Category. We will extract critical information including the source addresses, the sizes of messages transmitted, and the URLs visited, providing valuable insights into the nature of the traffic using APL.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "* " as src_IP
| parse " 200 * " as size
| parse "GET * " as url
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend src_IP = extract("^(\\S+)", 0, uri)
| extend size = extract("^(\\S+)", 1, status)
| extend url = extract("^(\\S+)", 1, method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20src_IP%20%3D%20extract\(%5C%22%5E\(%40S%2B\)%5C%22%2C%200%2C%20uri\)%5Cn%7C%20extend%20size%20%3D%20extract\(%5C%22%5E\(%40S%2B\)%5C%22%2C%201%2C%20status\)%5Cn%7C%20extend%20url%20%3D%20extract\(%5C%22%5E\(%40S%2B\)%5C%22%2C%201%2C%20method\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
## Calculate Bytes Transferred per Source IP
In this part, we will compute the total number of bytes transferred to each source IP address. This will allow us to gauge the data volume associated with each source using APL.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "* " as src_IP
| parse " 200 * " as size
| count, sum(size) by src_IP
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend src_IP = extract("^(\\S+)", 1, uri)
| extend size = toint(extract("200", 0, status))
| summarize count(), sum(size) by src_IP
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20size%20=%20toint\(extract\(%22200%22,%200,%20status\)\)\n|%20summarize%20count\(\),%20sum\(size\)%20by%20status%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Compute Average HTTP Response Size
In this section, we will calculate the average size of all successful HTTP responses. This metric helps us to understand the typical data load associated with successful server responses.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse " 200 * " as size
| avg(size)
```
**APL:**
Get the average value from a string:
```kusto theme={null}
['sample-http-logs']
| extend number = todouble(extract("\\d+(\\.\\d+)?", 0, status))
| summarize Average = avg(number)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20number%20=%20todouble\(status\)\n|%20summarize%20Average%20=%20avg\(number\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Extract Data with Missing Size Field (NoDrop)
This section focuses on extracting key parameters like `src`, `size`, and `URL`, even when the `size` field may be absent from the log message.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "* " as src_IP
| parse " 200 * " as size nodrop
| parse "GET * " as url
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where content_type == "text/css"
| extend src_IP = extract("^(\\S+)", 1, ['id'])
| extend size = toint(extract("(\\w+)", 1, status))
| extend url = extract("GET", 0, method)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20content_type%20%3D%3D%20%5C%22text%2Fcss%5C%22%20%7C%20extend%20src_IP%20%3D%20extract\(%5C%22%5E\(%5C%5CS%2B\)%5C%22%2C%201%2C%20%5B%27id%27%5D\)%20%7C%20extend%20size%20%3D%20toint\(extract\(%5C%22\(%5C%5Cw%2B\)%5C%22%2C%201%2C%20status\)\)%20%7C%20extend%20url%20%3D%20extract\(%5C%22GET%5C%22%2C%200%2C%20method\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
## Count URL Visits
This section is dedicated to identifying the frequency of visits to a specific URL. By counting these occurrences, we can gain insights into website popularity and user behavior.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "GET * " as url
| count by url
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend url = extract("^(\\S+)", 1, method)
| summarize Count = count() by url
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?qid=RsnK4jahgNC-rviz3s)
## Page Count by Source IP
In this section, we will identify the total number of pages associated with each source IP address. This analysis will allow us to understand the volume of content generated or hosted by each source.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "* -" as src_ip
| count by src_ip
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend src_ip = extract(".*", 0, ['id'])
| summarize Count = count() by src_ip
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20src_ip%20=%20extract\(%22.*%22,%200,%20%20\[%27id%27]\)\n|%20summarize%20Count%20=%20count\(\)%20by%20src_ip%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Reorder Pages by Load Frequency
We aim to identify the total number of pages per source IP address in this section. Following this, the pages will be reordered based on the frequency of loads, which will provide insights into the most accessed content.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse "* " as src_ip
| parse "GET * " as url
| count by url
| sort by _count
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend src_ip = extract(".*", 0, ['id'])
| extend url = extract("(GET)", 1, method)
| where isnotnull(url)
| summarize _count = count() by url, src_ip
| order by _count desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20src_ip%20=%20extract\(%22.*%22,%200,%20\[%27id%27]\)\n|%20extend%20url%20=%20extract\(%22\(GET\)%22,%201,%20method\)\n|%20where%20isnotnull\(url\)\n|%20summarize%20_count%20=%20count\(\)%20by%20url,%20src_ip\n|%20order%20by%20_count%20desc%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Identify the top 10 requested pages.
**Sumo Logic:**
```bash theme={null}
* | parse "GET * " as url
| count by url
| top 10 url by _count
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where method == "GET"
| top 10 by method desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20method%20==%20%22GET%22\n|%20top%2010%20by%20method%20desc%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Top 10 IPs by Bandwidth Usage
In this section, we aim to identify the top 10 source IP addresses based on their bandwidth consumption.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache
| parse " 200 * " as size
| parse "* -" as src_ip
| sum(size) as total_bytes by src_ip
| top 10 src_ip by total_bytes
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend size = req_duration_ms
| summarize total_bytes = sum(size) by ['id']
| top 10 by total_bytes desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20size%20=%20req_duration_ms\n|%20summarize%20total_bytes%20=%20sum\(size\)%20by%20\[%27id%27]\n|%20top%2010%20by%20total_bytes%20desc%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Top 6 IPs by Number of Hits
This section focuses on identifying the top six source IP addresses according to the number of hits they generate. This will provide insight into the most frequently accessed or active sources in the network.
**Sumo Logic**
```bash theme={null}
_sourceCategory=apache
| parse "* -" as src_ip
| count by src_ip
| top 100 src_ip by _count
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend src_ip = extract("^(\\S+)", 1, user_agent)
| summarize _count = count() by src_ip
| top 6 by _count desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20_count%20=%20count\(\)%20by%20user_agent\n|%20order%20by%20_count%20desc\n|%20limit%206%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Timeslice and Transpose
For the Source Category "apache", count by status\_code and timeslice of 1 hour.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache*
| parse "HTTP/1.1\" * * \"" as (status_code, size)
| timeslice 1h
| count by _timeslice, status_code
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend status_code = extract("^(\\S+)", 1, method)
| where status_code == "POST"
| summarize count() by status_code, bin(_time, 1h)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20method%20==%20%22POST%22\n|%20summarize%20count\(\)%20by%20method,%20bin\(_time,%201h\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Hourly Status Code Count for "Text" Source
In this section, We aim to count instances by `status_code`, grouped into one-hour timeslices, and then transpose `status_code` to column format. This will help us understand the frequency and timing of different status codes.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=text*
| parse "HTTP/1.1\" * * \"" as (status_code, size)
| timeslice 1h
| count by _timeslice, status_code
| transpose row _timeslice column status_code
```
**APL:**
```
['sample-http-logs']
| where content_type startswith 'text/css'
| extend status_code= status
| summarize count() by bin(_time, 1h), content_type, status_code
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20content_type%20startswith%20%27text/css%27\n|%20extend%20status_code%20=%20status\n|%20summarize%20count\(\)%20by%20bin\(_time,%201h\),%20content_type,%20status_code%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Status Code Count in 5 Time Buckets
In this example, we will perform a count by 'status\_code', sliced into five time buckets across the search results. This will help analyze the distribution and frequency of status codes over specific time intervals.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache*
| parse "HTTP/1.1\" * * \"" as (status_code, size)
| timeslice 5 buckets
| count by _timeslice, status_code
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where content_type startswith 'text/css'
| extend p=("HTTP/1.1\" * * \""), tostring( is_tls)
| extend status_code= status
| summarize count() by bin(_time, 12m), status_code
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20content_type%20startswith%20%27text/css%27\n|%20extend%20p=\(%22HTTP/1.1\\%22%20*%20*%20\\%22%22\),%20tostring\(is_tls\)\n|%20extend%20status_code%20=%20status\n|%20summarize%20count\(\)%20by%20bin\(_time,%2012m\),%20status_code%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Grouped Status Code Count
In this example, we will count messages by status code categories. We will group all messages with status codes in the `200s`, `300s`, `400s`, and `500s` together, we are also groupint the method requests with the `GET`, `POST`, `PUT`, `DELETE` attributes. This will provide an overview of the response status distribution.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=Apache/Access
| timeslice 15m
| if (status_code matches "20*",1,0) as resp_200
| if (status_code matches "30*",1,0) as resp_300
| if (status_code matches "40*",1,0) as resp_400
| if (status_code matches "50*",1,0) as resp_500
| if (!(status_code matches "20*" or status_code matches "30*" or status_code matches "40*" or status_code matches "50*"),1,0) as resp_others
| count(*), sum(resp_200) as tot_200, sum(resp_300) as tot_300, sum(resp_400) as tot_400, sum(resp_500) as tot_500, sum(resp_others) as tot_others by _timeslice
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend MethodCategory = case(
method == "GET", "GET Requests",
method == "POST", "POST Requests",
method == "PUT", "PUT Requests",
method == "DELETE", "DELETE Requests",
"Other Methods")
| extend StatusCodeCategory = case(
status startswith "2", "Success",
status startswith "3", "Redirection",
status startswith "4", "Client Error",
status startswith "5", "Server Error",
"Unknown Status")
| extend ContentTypeCategory = case(
content_type == "text/csv", "CSV",
content_type == "application/json", "JSON",
content_type == "text/html", "HTML",
"Other Types")
| summarize Count=count() by bin_auto(_time), StatusCodeCategory, MethodCategory, ContentTypeCategory
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20MethodCategory%20=%20case\(\n%20%20%20method%20==%20%22GET%22,%20%22GET%20Requests%22,\n%20%20%20method%20==%20%22POST%22,%20%22POST%20Requests%22,\n%20%20%20method%20==%20%22PUT%22,%20%22PUT%20Requests%22,\n%20%20%20method%20==%20%22DELETE%22,%20%22DELETE%20Requests%22,\n%20%20%20%22Other%20Methods%22\)\n|%20extend%20StatusCodeCategory%20=%20case\(\n%20%20%20status%20startswith%20%222%22,%20%22Success%22,\n%20%20%20status%20startswith%20%223%22,%20%22Redirection%22,\n%20%20%20status%20startswith%20%224%22,%20%22Client%20Error%22,\n%20%20%20status%20startswith%20%225%22,%20%22Server%20Error%22,\n%20%20%20%22Unknown%20Status%22\)\n|%20extend%20ContentTypeCategory%20=%20case\(\n%20%20%20content_type%20==%20%22text/csv%22,%20%22CSV%22,\n%20%20%20content_type%20==%20%22application/json%22,%20%22JSON%22,\n%20%20%20content_type%20==%20%22text/html%22,%20%22HTML%22,\n%20%20%20%22Other%20Types%22\)\n|%20summarize%20Count=count\(\)%20by%20bin_auto\(_time\),%20StatusCodeCategory,%20MethodCategory,%20ContentTypeCategory%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Conditional Operators
For the Source Category "apache", find all messages with a client error status code (40\*):
**Sumo Logic:**
```bash theme={null}
_sourceCategory=apache*
| parse "HTTP/1.1\" * * \"" as (status_code, size)
| where status_code matches "40*"
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| where content_type startswith 'text/css'
| extend p = ("HTTP/1.1\" * * \"")
| where status == "200"
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20content_type%20startswith%20%27text/css%27\n|%20extend%20p%20=%20\(%22HTTP/1.1\\%22%20*%20*%20\\%22%22\)\n|%20where%20status%20==%20%22200%22%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Browser-based Hit Count
In this query example, we aim to count the number of hits by browser. This analysis will provide insights into the different browsers used to access the source and their respective frequencies.
**Sumo Logic:**
```bash theme={null}
_sourceCategory=Apache/Access
| extract "\"[A-Z]+ \S+ HTTP/[\d\.]+\" \S+ \S+ \S+ \"(?[^\"]+?)\""
| if (agent matches "*MSIE*",1,0) as ie
| if (agent matches "*Firefox*",1,0) as firefox
| if (agent matches "*Safari*",1,0) as safari
| if (agent matches "*Chrome*",1,0) as chrome
| sum(ie) as ie, sum(firefox) as firefox, sum(safari) as safari, sum(chrome) as chrome
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend ie = case(tolower(user_agent) contains "msie", 1, 0)
| extend firefox = case(tolower(user_agent) contains "firefox", 1, 0)
| extend safari = case(tolower(user_agent) contains "safari", 1, 0)
| extend chrome = case(tolower(user_agent) contains "chrome", 1, 0)
| summarize data = sum(ie), lima = sum(firefox), lo = sum(safari), ce = sum(chrome)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20ie%20=%20case\(tolower\(user_agent\)%20contains%20%22msie%22,%201,%200\)\n|%20extend%20firefox%20=%20case\(tolower\(user_agent\)%20contains%20%22firefox%22,%201,%200\)\n|%20extend%20safari%20=%20case\(tolower\(user_agent\)%20contains%20%22safari%22,%201,%200\)\n|%20extend%20chrome%20=%20case\(tolower\(user_agent\)%20contains%20%22chrome%22,%201,%200\)\n|%20summarize%20data%20=%20sum\(ie\),%20lima%20=%20sum\(firefox\),%20lo%20=%20sum\(safari\),%20ce%20=%20sum\(chrome\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Use the where operator to match only weekend days.
**Sumo Logic:**
```bash theme={null}
* | parse "day=*:" as day_of_week
| where day_of_week in ("Saturday","Sunday")
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend day_of_week = dayofweek(_time)
| where day_of_week == 1 or day_of_week == 0
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20day_of_week%20=%20dayofweek\(_time\)\n|%20where%20day_of_week%20==%201%20or%20day_of_week%20==%200%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Extract Numeric Version Numbers
In this section, we will identify version numbers that match numeric values 2, 3, or 1. We will utilize the `num` operator to convert these strings into numerical format, facilitating easier analysis and comparison.
**Sumo Logic:**
```bash theme={null}
* | parse "Version=*." as number | num(number)
| where number in (2,3,6)
```
**APL:**
```kusto theme={null}
['sample-http-logs']
| extend p= (req_duration_ms)
| extend number=toint(p)
| where number in (2,3,6)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20p=%20\(req_duration_ms\)\n|%20extend%20number=toint\(p\)\n|%20where%20number%20in%20\(2,3,6\)%22,%22queryOptions%22:\{%22quickRange%22:%2290d%22}})
## Making the Leap: Transform Your Data Analytics with APL
As we’ve navigated through the process of migrating from Sumo Logic to APL, we hope you’ve found the insights valuable. The powerful capabilities of Axiom Processing Lnaguage are now within your reach, ready to empower your data analytics journey.
Ready to take the next step in your data analytics journey? Dive deeper into APL and discover how it can unlock even more potential in your data. Check out our APL [learning resources](/apl/guides/migrating-from-sql-to-apl) and [tutorials](/apl/tutorial) to become proficient in APL, and join our [community forums](http://axiom.co/discord) to engage with other APL users. Together, we can redefine what’s possible in data analytics. Remember, the migration to APL isn’t just a change, it’s an upgrade. Embrace the change, because better data analytics await you.
Begin your APL journey today!
# Migrate from Splunk SPL to APL
Source: https://axiom.co/docs/apl/guides/splunk-cheat-sheet
This step-by-step guide provides a high-level mapping from Splunk SPL to APL.
Splunk and Axiom are powerful tools for log analysis and data exploration. The data explorer interface uses Axiom Processing Language (APL). There are some differences between the query languages for Splunk and Axiom. When transitioning from Splunk to APL, you will need to understand how to convert your Splunk SPL queries into APL.
**This guide provides a high-level mapping from Splunk to APL.**
## Basic Searching
Splunk uses a `search` command for basic searching, while in APL, simply specify the dataset name followed by a filter.
**Splunk:**
```bash theme={null}
search index="myIndex" error
```
**APL:**
```kusto theme={null}
['myDatasaet']
| where FieldName contains “error”
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20method%20contains%20%27GET%27%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Filtering
In Splunk, perform filtering using the `search` command, usually specifying field names and their desired values. In APL, perform filtering by using the `where` operator.
**Splunk:**
```bash theme={null}
Search index=”myIndex” error
| stats count
```
**APL:**
```kusto theme={null}
['myDataset']
| where fieldName contains “error”
| count
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20content_type%20contains%20%27text%27\n|%20count\n|%20limit%2010%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Aggregation
In Splunk, the `stats` command is used for aggregation. In APL, perform aggregation using the `summarize` operator.
**Splunk:**
```bash theme={null}
search index="myIndex"
| stats count by status
```
**APL:**
```kusto theme={null}
['myDataset']
| summarize count() by status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20summarize%20count\(\)%20by%20status%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Time Frames
In Splunk, select a time range for a search in the time picker on the search page. In APL, filter by a time range using the where operator and the `timespan` field of the dataset.
**Splunk:**
```bash theme={null}
search index="myIndex" earliest=-1d@d latest=now
```
**APL:**
```kusto theme={null}
['myDataset']
| where _time >= ago(1d) and _time <= now()
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20_time%20%3E=%20ago\(1d\)%20and%20_time%20%3C=%20now\(\)%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Sorting
In Splunk, the `sort` command is used to order the results of a search. In APL, perform sorting by using the `sort by` operator.
**Splunk:**
```bash theme={null}
search index="myIndex"
| sort - content_type
```
**APL:**
```kusto theme={null}
['myDataset']
| sort by countent_type desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20sort%20by%20content_type%20desc%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Selecting Fields
In Splunk, use the fields command to specify which fields to include or exclude in the search results. In APL, use the `project` operator, `project-away` operator, or the `project-keep` operator to specify which fields to include in the query results.
**Splunk:**
```bash theme={null}
index=main sourcetype=mySourceType
| fields status, responseTime
```
**APL:**
```kusto theme={null}
['myDataset']
| extend newName = oldName
| project-away oldName
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20newStatus%20=%20status%20\n|%20project-away%20status%20%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Renaming Fields
In Splunk, rename fields using the `rename` command, while in APL rename fields using the `extend,` and `project` operator. Here is the general syntax:
**Splunk:**
```bash theme={null}
index="myIndex" sourcetype="mySourceType"
| rename oldFieldName AS newFieldName
```
**APL:**
```kusto theme={null}
['myDataset']
| where method == "GET"
| extend new_field_name = content_type
| project-away content_type
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20where%20method%20==%20%27GET%27\n|%20extend%20new_field_name%20=%20content_type\n|%20project-away%20content_type%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Calculated Fields
In Splunk, use the `eval` command to create calculated fields based on the values of other fields, while in APL use the `extend` operator to create calculated fields based on the values of other fields.
**Splunk**
```bash theme={null}
search index="myIndex"
| eval newField=field1+field2
```
**APL:**
```kusto theme={null}
['myDataset']
| extend newField = field1 + field2
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=\{%22apl%22:%22\[%27sample-http-logs%27]\n|%20extend%20calculatedFields%20=%20req_duration_ms%20%2b%20resp_body_size_bytes%22,%22queryOptions%22:\{%22quickRange%22:%2230d%22}})
## Structure and Concepts
The following table compares concepts and data structures between Splunk and APL logs.
| Concept | Splunk | APL | Comment |
| ------------------------- | -------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| data caches | buckets | caching and retention policies | Controls the period and caching level for the data.This setting directly affects the performance of queries. |
| logical partition of data | index | dataset | Allows logical separation of the data. |
| structured event metadata | N/A | dataset | Splunk doesn’t expose the concept of metadata to the search language. APL logs have the concept of a dataset, which has fields and columns. Each event instance is mapped to a row. |
| data record | event | row | Terminology change only. |
| types | datatype | datatype | APL data types are more explicit because they are set on the fields. Both have the ability to work dynamically with data types and roughly equivalent sets of data types. |
| query and search | search | query | Concepts essentially are the same between APL and Splunk |
## Functions
The following table specifies functions in APL that are equivalent to Splunk Functions.
| Splunk | APL |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| strcat | strcat() |
| split | split() |
| if | iff() |
| tonumber | todouble(), tolong(), toint() |
| upper, lower | toupper(), tolower() |
| replace | replace\_string() or replace\_regex() |
| substr | substring() |
| tolower | tolower() |
| toupper | toupper() |
| match | matches regex |
| regex | matches regex **(in splunk, regex is an operator. In APL, it’s a relational operator.)** |
| searchmatch | == **(In splunk, `searchmatch` allows searching the exact string.)** |
| random | rand(), rand(n) **(Splunk’s function returns a number between zero to 231 -1. APL returns a number between 0.0 and 1.0, or if a parameter is provided, between 0 and n-1.)** |
| now | now() |
In Splunk, the function is invoked by using the `eval` operator. In APL, it’s used as part of the `extend` or `project`.
In Splunk, the function is invoked by using the `eval` operator. In APL, it can be used with the `where` operator.
## Filter
APL log queries start from a tabular result set in which a filter is applied. In Splunk, filtering is the default operation on the current index. You may also use the where operator in Splunk, but we don’t recommend it.
| Product | Operator | Example |
| :------ | :--------- | :------------------------------------------------------------------------- |
| Splunk | **search** | Sample.Logs="330009.2" method="GET" \_indextime>-24h |
| APL | **where** | \['sample-http-logs']
\| where method == "GET" and \_time > ago(24h) |
## Get n events or rows for inspection
APL log queries also support `take` as an alias to `limit`. In Splunk, if the results are ordered, `head` returns the first n results. In APL, `limit` isn’t ordered, but it returns the first n rows that are found.
| Product | Operator | Example |
| ------- | -------- | ---------------------------------------- |
| Splunk | head | Sample.Logs=330009.2
\| head 100 |
| APL | limit | \['sample-htto-logs']
\| limit 100 |
## Get the first *n* events or rows ordered by a field or column
For the bottom results, in Splunk, use `tail`. In APL, specify ordering direction by using `asc`.
| Product | Operator | Example |
| :------ | :------- | :------------------------------------------------------------------ |
| Splunk | head | Sample.Logs="33009.2"
\| sort Event.Sequence
\| head 20 |
| APL | top | \['sample-http-logs']
\| top 20 by method |
## Extend the result set with new fields or columns
Splunk has an `eval` function, but it’s not comparable to the `eval` operator in APL. Both the `eval` operator in Splunk and the `extend` operator in APL support only scalar functions and arithmetic operators.
| Product | Operator | Example |
| :------ | :------- | :------------------------------------------------------------------------------------ |
| Splunk | eval | Sample.Logs=330009.2
\| eval state= if(Data.Exception = "0", "success", "error") |
| APL | extend | \['sample-http-logs']
\| extend Grade = iff(req\_duration\_ms >= 80, "A", "B") |
## Rename
APL uses the `project` operator to rename a field. In the `project` operator, a query can take advantage of any indexes that are prebuilt for a field. Splunk has a `rename` operator that does the same.
| Product | Operator | Example |
| :------ | :------- | :-------------------------------------------------------------- |
| Splunk | rename | Sample.Logs=330009.2
\| rename Date.Exception as execption |
| APL | project | \['sample-http-logs']
\| project updated\_status = status |
## Format results and projection
Splunk uses the `table` command to select which columns to include in the results. APL has a `project` operator that does the same and [more](/apl/tabular-operators/project-operator).
| Product | Operator | Example |
| :------ | :------- | :--------------------------------------------------- |
| Splunk | table | Event.Rule=330009.2
\| table rule, state |
| APL | project | \['sample-http-logs']
\| project status, method |
Splunk uses the `field -` command to select which columns to exclude from the results. APL has a `project-away` operator that does the same.
| Product | Operator | Example |
| :------ | :--------------- | :-------------------------------------------------------------- |
| Splunk | **fields -** | Sample.Logs=330009.2\`
\| fields - quota, hightest\_seller |
| APL | **project-away** | \['sample-http-logs']
\| project-away method, status |
## Aggregation
See the [list of summarize aggregations functions](/apl/aggregation-function/statistical-functions) that are available.
| Splunk operator | Splunk example | APL operator | APL example |
| :-------------- | :------------------------------------------------------------- | :----------- | :----------------------------------------------------------------------- |
| **stats** | search (Rule=120502.\*)
\| stats count by OSEnv, Audience | summarize | \['sample-http-logs']
\| summarize count() by content\_type, status |
## Sort
In Splunk, to sort in ascending order, you must use the `reverse` operator. APL also supports defining where to put nulls, either at the beginning or at the end.
| Product | Operator | Example |
| :------ | :------- | :------------------------------------------------------------- |
| Splunk | sort | Sample.logs=120103
\| sort Data.Hresult
\| reverse |
| APL | order by | \['sample-http-logs']
\| order by status desc |
Whether you’re just starting your transition or you’re in the thick of it, this guide can serve as a helpful roadmap to assist you in your journey from Splunk to Axiom Processing Language.
Dive into the Axiom Processing Language, start converting your Splunk queries to APL, and explore the rich capabilities of the Query tab. Embrace the learning curve, and remember, every complex query you master is another step forward in your data analytics journey.
# Axiom Processing Language (APL)
Source: https://axiom.co/docs/apl/introduction
This section explains how to use the Axiom Processing Language to get deeper insights from your data.
The Axiom Processing Language (APL) is a query language that’s perfect for getting deeper insights from your data. Whether logs, events, analytics, or similar, APL provides the flexibility to filter, manipulate, and summarize your data exactly the way you need it.
## Prerequisites
* [Create an Axiom account](https://app.axiom.co/register).
* [Create a dataset in Axiom](/reference/datasets#create-dataset) 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:
```kusto theme={null}
DatasetName
| Operator ...
| Operator ...
```
* `DatasetName` is the name of the dataset you want to query.
* `Operator` is an operation you apply to the data.
Apart from Axiom datasets, you can use other data sources:
* External data sources using the [externaldata](/apl/tabular-operators/externaldata-operator) operator.
* Specify a data table in the APL query itself using the `let` statement.
## Example query
```kusto theme={null}
['github-issue-comment-event']
| extend isBot = actor contains '-bot' or actor contains '[bot]'
| where isBot == true
| summarize count() by bin_auto(_time), actor
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'github-issue-comment-event'%5D%20%7C%20extend%20isBot%20%3D%20actor%20contains%20'-bot'%20or%20actor%20contains%20'%5Bbot%5D'%20%7C%20where%20isBot%20%3D%3D%20true%20%7C%20summarize%20count\(\)%20by%20bin_auto\(_time\)%2C%20actor%22%7D)
The query above uses a dataset called `github-issue-comment-event` as its data source. It uses the following operators:
* [extend](/apl/tabular-operators/extend-operator) adds a new field `isBot` to the query results. It sets the values of the new field to true if the values of the `actor` field in the original dataset contain `-bot` or `[bot]`.
* [where](/apl/tabular-operators/where-operator) filters for the values of the `isBot` field. It only returns rows where the value is true.
* [summarize](/apl/tabular-operators/summarize-operator) 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 |
The query results are a representation of your data based on your request. The query doesn’t change the original dataset.
## 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](/apl/entities/entity-names).
## What’s next
Check out the [list of example queries](/apl/tutorial) or explore the supported operators and functions:
* [Scalar functions](/apl/scalar-functions/)
* [Aggregation functions](/apl/aggregation-function/)
* [Tabular operators](/apl/tabular-operators/)
* [Scalar operators](/apl/scalar-operators/)
# Set statement
Source: https://axiom.co/docs/apl/query-statement/set-statement
The set statement is used to set a query option in your APL query.
The `set` statement is used to set a query option. Options enabled with the `set` statement only have effect for the duration of the query.
The `set` statement affects how your query is processed and the returned results.
## Syntax
```kusto theme={null}
set OptionName=OptionValue
```
## Strict types
The `stricttypes` query option lets you specify only the exact type of the data type declaration needed in your query. Otherwise, it throws a **QueryFailed** error.
## Example
```kusto theme={null}
set stricttypes;
['Dataset']
| where number == 5
```
# Special field attributes
Source: https://axiom.co/docs/apl/reference/special-field-attributes
This page explains how to implement special fields within APL queries to enhance the functionality and interactivity of datasets. Use these fields in APL queries to add unique behaviors to the Axiom user interface.
## Add link to table
* Name: `_row_url`
* Type: string
* Description: Define the URL to which the entire table links.
* APL query example: `extend _row_url = 'https://axiom.co/'`
* Expected behavior: Make rows clickable. When clicked, go to the specified URL.
If you specify a static string as the URL, all rows link to that page. To specify a different URL for each row, use an dynamic expression like `extend _row_url = strcat('https://axiom.co/', uri)` where `uri` is a field in your data.
## Add link to values in a field
* Name: `_FIELDNAME_url`
* Type: string
* Description: Define a URL to which values in a field link.
* APL query example: `extend _website_url = 'https://axiom.co/'`
* Expected behavior: Make values in the `website` field clickable. When clicked, go to the specified URL.
Replace `FIELDNAME` with the actual name of the field.
## Add tooltip to values in a field
* Name: `_FIELDNAME_tooltip`
* Type: string
* Description: Define text to be displayed when hovering over values in a field.
* Example Usage: `extend _errors_tooltip = 'Number of errors'`
* Expected behavior: Display a tooltip with the specified text when the user hovers over values in a field.
Replace `FIELDNAME` with the actual name of the field.
## Add description to values in a field
* Name: `_FIELDNAME_description`
* Type: string
* Description: Define additional information to be displayed under the values in a field.
* Example Usage: `extend _diskusage_description = 'Current disk usage'`
* Expected behavior: Display additional text under the values in a field for more context.
Replace `FIELDNAME` with the actual name of the field.
## Add unit of measurement
* Name: `_FIELDNAME_unit`
* Type: string
* Description: Specify the unit of measurement for another field’s value allowing for proper formatting and display.
* APL query example: `extend _size_unit = "gbytes"`
* Expected behavior: Format the value in the `size` field according to the unit specified in the `_size_unit` field.
Replace `FIELDNAME` with the actual name of the field you want to format. For example, for a field named `size`, use `_size_unit = "gbytes"` to display its values in gigabytes in the query results.
The supported units are the following:
**Percentage**
| Unit name | APL sytax |
| ----------------- | ---------- |
| percent (0-100) | percent100 |
| percent (0.0-1.0) | percent |
**Currency**
| Unit name | APL sytax |
| ------------ | --------- |
| Dollars (\$) | curusd |
| Pounds (£) | curgbp |
| Euro (€) | cureur |
| Bitcoin (฿) | curbtc |
**Data (IEC)**
| Unit name | APL sytax |
| ---------- | --------- |
| bits(IEC) | bits |
| bytes(IEC) | bytes |
| kibibytes | kbytes |
| mebibytes | mbytes |
| gibibytes | gbytes |
| tebibytes | tbytes |
| pebibytes | pbytes |
**Data (metric)**
| Unit name | APL sytax |
| ------------- | --------- |
| bits(Metric) | decbits |
| bytes(Metric) | decbytes |
| kilobytes | deckbytes |
| megabytes | decmbytes |
| gigabytes | decgbytes |
| terabytes | dectbytes |
| petabytes | decpbytes |
**Data rate**
| Unit name | APL sytax |
| ------------- | --------- |
| packets/sec | pps |
| bits/sec | bps |
| bytes/sec | Bps |
| kilobytes/sec | KBs |
| kilobits/sec | Kbits |
| megabytes/sec | MBs |
| megabits/sec | Mbits |
| gigabytes/sec | GBs |
| gigabits/sec | Gbits |
| terabytes/sec | TBs |
| terabits/sec | Tbits |
| petabytes/sec | PBs |
| petabits/sec | Pbits |
**Datetime**
| Unit name | APL sytax |
| ----------------- | --------- |
| Hertz (1/s) | hertz |
| nanoseconds (ns) | ns |
| microseconds (µs) | µs |
| milliseconds (ms) | ms |
| seconds (s) | secs |
| minutes (m) | mins |
| hours (h) | hours |
| days (d) | days |
| ago | ago |
**Throughput**
| Unit name | APL sytax |
| ------------------ | --------- |
| counts/sec (cps) | cps |
| ops/sec (ops) | ops |
| requests/sec (rps) | reqps |
| reads/sec (rps) | rps |
| writes/sec (wps) | wps |
| I/O ops/sec (iops) | iops |
| counts/min (cpm) | cpm |
| ops/min (opm) | opm |
| requests/min (rps) | reqpm |
| reads/min (rpm) | rpm |
| writes/min (wpm) | wpm |
## Example
The example APL query below adds a tooltip and a description to the values of the `status` field. Clicking one of the values in this field leads to a page about status codes. The query adds the new field `resp_body_size_bits` that displays the size of the response body in the unit of bits.
```apl theme={null}
['sample-http-logs']
| extend _status_tooltip = 'The status of the HTTP request is the response code from the server. It shows if an HTTP request has been successfully completed.'
| extend _status_description = 'This is the status of the HTTP request.'
| extend _status_url = 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Status'
| extend resp_body_size_bits = resp_body_size_bytes * 8
| extend _resp_body_size_bits_unit = 'bits'
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20_status_tooltip%20%3D%20'The%20status%20of%20the%20HTTP%20request%20is%20the%20response%20code%20from%20the%20server.%20It%20shows%20if%20an%20HTTP%20request%20has%20been%20successfully%20completed.'%20%7C%20extend%20_status_description%20%3D%20'This%20is%20the%20status%20of%20the%20HTTP%20request.'%20%7C%20extend%20_status_url%20%3D%20'https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FHTTP%2FStatus'%20%7C%20extend%20resp_body_size_bits%20%3D%20resp_body_size_bytes%20*%208%20%7C%20extend%20_resp_body_size_bits_unit%20%3D%20'bits'%22%7D)
# Array functions
Source: https://axiom.co/docs/apl/scalar-functions/array-functions
This section explains how to use array functions in APL.
The table summarizes the array functions available in APL.
| Function | Description |
| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [array\_concat](/apl/scalar-functions/array-functions/array-concat) | Concatenates a number of dynamic arrays to a single array. |
| [array\_extract](/apl/scalar-functions/array-functions/array-extract) | Returns a dynamic array containing the extracted elements. |
| [array\_iff](/apl/scalar-functions/array-functions/array-iff) | Returns a new array containing elements from the input array that satisfy the condition. |
| [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of) | Searches the array for the specified item, and returns its position. |
| [array\_length](/apl/scalar-functions/array-functions/array-length) | Calculates the number of elements in a dynamic array. |
| [array\_reverse](/apl/scalar-functions/array-functions/array-reverse) | Reverses the order of the elements in a dynamic array. |
| [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left) | Rotates values inside a dynamic array to the left. |
| [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right) | Rotates values inside a dynamic array to the right. |
| [array\_select\_dict](/apl/scalar-functions/array-functions/array-select-dict) | Selects a dictionary from an array of dictionaries. |
| [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left) | Shifts the values inside a dynamic array to the left. |
| [array\_shift\_right](/apl/scalar-functions/array-functions/array-shift-right) | Shifts values inside an array to the right. |
| [array\_slice](/apl/scalar-functions/array-functions/array-slice) | Extracts a slice of a dynamic array. |
| [array\_sort\_asc](/apl/scalar-functions/array-functions/array-sort-asc) | Sorts an array in ascending order. |
| [array\_sort\_desc](/apl/scalar-functions/array-functions/array-sort-desc) | Sorts an array in descending order. |
| [array\_split](/apl/scalar-functions/array-functions/array-split) | Splits an array to multiple arrays according to the split indices and packs the generated array in a dynamic array. |
| [array\_sum](/apl/scalar-functions/array-functions/array-sum) | Calculates the sum of elements in a dynamic array. |
| [bag\_has\_key](/apl/scalar-functions/array-functions/bag-has-key) | Checks whether a dynamic property bag contains a specific key. |
| [bag\_keys](/apl/scalar-functions/array-functions/bag-keys) | Returns all keys in a dynamic property bag. |
| [bag\_pack](/apl/scalar-functions/array-functions/bag-pack) | Converts a list of key-value pairs to a dynamic property bag. |
| [isarray](/apl/scalar-functions/array-functions/isarray) | Checks whether a value is an array. |
| [len](/apl/scalar-functions/array-functions/len) | Returns the length of a string or the number of elements in an array. |
| [pack\_array](/apl/scalar-functions/array-functions/pack-array) | Packs all input values into a dynamic array. |
| [pack\_dictionary](/apl/scalar-functions/array-functions/pack-dictionary) | Returns a dynamic object that represents a dictionary where each key maps to its associated value. |
| [strcat\_array](/apl/scalar-functions/array-functions/strcat-array) | Takes an array and returns a single concatenated string with the array’s elements separated by the specified delimiter. |
## Dynamic arrays
Most array functions accept a dynamic array as their parameter. Dynamic arrays allow you to add or remove elements. You can change a dynamic array with an array function.
A dynamic array expands as you add more elements. This means that you don’t need to determine the size in advance.
# array_concat
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-concat
This page explains how to use the array_concat function in APL.
The `array_concat` function in APL (Axiom Processing Language) concatenates two or more arrays into a single array. Use this function when you need to merge multiple arrays into a single array structure. It’s particularly useful for situations where you need to handle and combine collections of elements across different fields or sources, such as log entries, OpenTelemetry trace data, or security logs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In SPL, you typically use the `mvappend` function to concatenate multiple fields or arrays into a single array. In APL, the equivalent is `array_concat`, which also combines arrays but requires you to specify each array as a parameter.
```sql Splunk example theme={null}
| eval combined_array = mvappend(array1, array2, array3)
```
```kusto APL equivalent theme={null}
| extend combined_array = array_concat(array1, array2, array3)
```
ANSI SQL doesn’t natively support an array concatenation function across different arrays. Instead, you typically use `UNION` to combine results from multiple arrays or collections. In APL, `array_concat` allows you to directly concatenate multiple arrays, providing a more straightforward approach.
```sql SQL example theme={null}
SELECT array1 UNION ALL array2 UNION ALL array3
```
```kusto APL equivalent theme={null}
| extend combined_array = array_concat(array1, array2, array3)
```
## Usage
### Syntax
```kusto theme={null}
array_concat(array1, array2, ...)
```
### Parameters
* `array1`: The first array to concatenate.
* `array2`: The second array to concatenate.
* `...`: Additional arrays to concatenate.
### Returns
An array containing all elements from the input arrays in the order they are provided.
## Use case examples
In log analysis, you can use `array_concat` to merge collections of user requests into a single array to analyze request patterns across different endpoints.
**Query**
```kusto theme={null}
['sample-http-logs']
| take 50
| summarize combined_requests = array_concat(pack_array(uri), pack_array(method))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20summarize%20combined_requests%20%3D%20array_concat\(pack_array\(uri\)%2C%20pack_array\(method\)\)%22%7D)
**Output**
| \_time | uri | method | combined\_requests |
| ------------------- | ----------------------- | ------ | ------------------------------------ |
| 2024-10-28T12:30:00 | /api/v1/textdata/cnfigs | POST | \["/api/v1/textdata/cnfigs", "POST"] |
This example concatenates the `uri` and `method` values into a single array for each log entry, allowing for combined analysis of access patterns and request methods in log data.
In OpenTelemetry traces, use `array_concat` to join span IDs and trace IDs for a comprehensive view of trace behavior across services.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| summarize combined_ids = array_concat(pack_array(span_id), pack_array(trace_id))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20take%2050%20%7C%20summarize%20combined_ids%20%3D%20array_concat\(pack_array\(span_id\)%2C%20pack_array\(trace_id\)\)%22%7D)
**Output**
| combined\_ids |
| ---------------------------------- |
| \["span1", "trace1", "span2", ...] |
| \_time | trace\_id | span\_id | combined\_ids |
| ------------------- | ------------- | --------- | ------------------------------- |
| 2024-10-28T12:30:00 | trace\_abc123 | span\_001 | \["trace\_abc123", "span\_001"] |
This example creates an array containing both `span_id` and `trace_id` values, offering a unified view of the trace journey across services.
In security logs, `array_concat` can consolidate multiple IP addresses or user IDs to detect potential attack patterns involving different locations or users.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '500'
| take 50
| summarize failed_attempts = array_concat(pack_array(id), pack_array(['geo.city']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'500'%20%7C%20take%2050%20%7C%20summarize%20failed_attempts%20%3D%20array_concat\(pack_array\(id\)%2C%20pack_array\(%5B'geo.city'%5D\)\)%22%7D)
**Output**
| \_time | id | geo.city | combined\_ids |
| ------------------- | ------------------------------------ | -------- | --------------------------------------------------- |
| 2024-10-28T12:30:00 | fc1407f5-04ca-4f4e-ad01-f72063736e08 | Avenal | \["fc1407f5-04ca-4f4e-ad01-f72063736e08", "Avenal"] |
This query combines failed user IDs and cities where the request originated, allowing security analysts to detect suspicious patterns or brute force attempts from different regions.
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
# array_extract
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-extract
This page explains how to use the array_extract function in APL.
Use the `array_extract` function to extract specific values from a dynamic array using a JSON path expression. You can use this function to transform structured array data, such as arrays of objects, into simpler arrays of scalars. This is useful when working with nested JSON-like structures where you need to extract only selected fields for analysis, visualization, or filtering.
Use `array_extract` when:
* You need to pull scalar values from arrays of objects.
* You want to simplify a nested data structure before further analysis.
* You are working with structured logs or metrics where key values are nested inside arrays.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use `spath` with a wildcard or field extraction logic to navigate nested structures. APL’s `array_extract` uses JSON path syntax to extract array elements that match a given pattern.
```sql Splunk example theme={null}
| eval arr=mvappend("{\"id\":1,\"value\":true}", "{\"id\":2,\"value\":false}")
| spath input=arr path="{}.value" output=extracted_value
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend extracted_value = array_extract(dynamic([{'id': 1, 'value': true}, {'id': 2, 'value': false}]), @'$[*].value')
| project _time, extracted_value
```
ANSI SQL doesn’t offer native support for JSON path queries on arrays in standard syntax. While some engines support functions like `JSON_VALUE` or `JSON_TABLE`, they operate on single objects. APL’s `array_extract` provides a concise and expressive way to query arrays using JSON path.
```sql SQL example theme={null}
SELECT JSON_EXTRACT(data, '$[*].value') AS extracted_value
FROM my_table;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend extracted_value = array_extract(dynamic([{'id': 1, 'value': true}, {'id': 2, 'value': false}]), @'$[*].value')
| project _time, extracted_value
```
## Usage
### Syntax
```kusto theme={null}
array_extract(sourceArray, jsonPath)
```
### Parameters
| Name | Type | Description |
| ------------- | --------- | ------------------------------------------------------- |
| `sourceArray` | `dynamic` | A JSON-like dynamic array to extract values from. |
| `jsonPath` | `string` | A JSON path expression to select values from the array. |
### Returns
A dynamic array of values that match the JSON path expression. The function always returns an array, even when the path matches only one element or no elements.
## Use case examples
Use `array_extract` to retrieve specific fields from structured arrays, such as arrays of request metadata.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend extracted_value = array_extract(dynamic([{'id': 1, 'value': true}, {'id': 2, 'value': false}]), @'$[*].value')
| project _time, extracted_value
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20extracted_value%20%3D%20array_extract%28dynamic%28\[%7B'id'%3A%201%2C%20'value'%3A%20true%7D%2C%20%7B'id'%3A%202%2C%20'value'%3A%20false%7D]%29%2C%20%40'%24%5B*%5D.value'%29%20%7C%20project%20_time%2C%20extracted_value%22%7D)
**Output**
| \_time | extracted\_value |
| ---------------- | ------------------ |
| Jun 24, 09:28:10 | \["true", "false"] |
| Jun 24, 09:28:10 | \["true", "false"] |
| Jun 24, 09:28:10 | \["true", "false"] |
This query extracts the `value` field from an array of objects, returning a flat array of booleans in string form.
Use `array_extract` to extract service names from a nested structure—for example, collecting `service.name` from span records in a trace bundle.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize traces=make_list(pack('trace_id', trace_id, 'service', ['service.name'])) by span_id
| extend services=array_extract(traces, @'$[*].service')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20traces%3Dmake_list%28pack%28'trace_id'%2C%20trace_id%2C%20'service'%2C%20%5B'service.name'%5D%29%29%20by%20span_id%20%7C%20extend%20services%3Darray_extract%28traces%2C%20%40'%24%5B*%5D.service'%29%22%7D)
**Output**
| span\_id | services |
| ---------------- | ----------------- |
| 24157518330f7967 | \[frontend-proxy] |
| 209a0815d291d88a | \[currency] |
| aca763479149f1d0 | \[frontend-web] |
This query collects and extracts the `service.name` fields from a constructed nested structure of spans.
Use `array_extract` to extract HTTP status codes from structured log entries grouped into sessions.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize events=make_list(pack('uri', uri, 'status', status)) by id
| extend status_codes=array_extract(events, @'$[*].status')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20events%3Dmake_list%28pack%28'uri'%2C%20uri%2C%20'status'%2C%20status%29%29%20by%20id%20%7C%20extend%20status_codes%3Darray_extract%28events%2C%20%40'%24%5B*%5D.status'%29%22%7D)
**Output**
| id | status\_codes |
| ----- | ------------- |
| user1 | \[200] |
| user2 | \[201] |
| user3 | \[200] |
This query extracts all HTTP status codes per user session, helping to identify patterns like repeated failures or suspicious behavior.
## List of related functions
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Returns a subarray like `array_extract`, but supports negative indexing.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Useful before applying `array_extract`.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Joins arrays end-to-end. Use before or after slicing arrays with `array_extract`.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the position of an element in an array, which can help set the `startIndex` for `array_extract`.
# array_iff
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-iff
This page explains how to use the array_iff function in APL.
The `array_iff` function in Axiom Processing Language (APL) allows you to create arrays based on a condition. It returns an array with elements from two specified arrays, choosing each element from the first array when a condition is met and from the second array otherwise. This function is useful for scenarios where you need to evaluate a series of conditions across multiple datasets, especially in log analysis, trace data, and other applications requiring conditional element selection within arrays.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, array manipulation based on conditions typically requires using conditional functions or eval expressions. APL’s `array_iff` function lets you directly select elements from one array or another based on a condition, offering more streamlined array manipulation.
```sql Splunk example theme={null}
eval selected_array=if(condition, array1, array2)
```
```kusto APL equivalent theme={null}
array_iff(condition_array, array1, array2)
```
In ANSI SQL, conditionally selecting elements from arrays often requires complex `CASE` statements or functions. With APL’s `array_iff` function, you can directly compare arrays and conditionally populate them, simplifying array-based operations.
```sql SQL example theme={null}
CASE WHEN condition THEN array1 ELSE array2 END
```
```kusto APL equivalent theme={null}
array_iff(condition_array, array1, array2)
```
## Usage
### Syntax
```kusto theme={null}
array_iff(condition_array, array1, array2)
```
### Parameters
* `condition_array`: An array of boolean values, where each element determines whether to choose the corresponding element from `array1` or `array2`.
* `array1`: The array to select elements from when the corresponding `condition_array` element is `true`.
* `array2`: The array to select elements from when the corresponding `condition_array` element is `false`.
### Returns
An array where each element is selected from `array1` if the corresponding `condition_array` element is `true`, and from `array2` otherwise.
## Use case examples
The `array_iff` function can help filter log data conditionally, such as choosing specific durations based on HTTP status codes.
**Query**
```kusto theme={null}
['sample-http-logs']
| order by _time desc
| limit 1000
| summarize is_ok = make_list(status == '200'), request_duration = make_list(req_duration_ms)
| project ok_request_duration = array_iff(is_ok, request_duration, 0)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20order%20by%20_time%20desc%20%7C%20limit%201000%20%7C%20summarize%20is_ok%20%3D%20make_list\(status%20%3D%3D%20'200'\)%2C%20request_duration%20%3D%20make_list\(req_duration_ms\)%20%7C%20project%20ok_request_duration%20%3D%20array_iff\(is_ok%2C%20request_duration%2C%200\)%22%7D)
**Output**
| ok\_request\_duration |
| -------------------------------------------------------------------- |
| \[0.3150485097707766, 0, 0.21691408087847264, 0, 0.2757618582190533] |
This example filters the `req_duration_ms` field to include only durations for the most recent 1,000 requests with status `200`, replacing others with `0`.
With OpenTelemetry trace data, you can use `array_iff` to filter spans based on the service type, such as selecting durations for `server` spans and setting others to zero.
**Query**
```kusto theme={null}
['otel-demo-traces']
| order by _time desc
| limit 1000
| summarize is_server = make_list(kind == 'server'), duration_list = make_list(duration)
| project server_durations = array_iff(is_server, duration_list, 0)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20order%20by%20_time%20desc%20%7C%20limit%201000%20%7C%20summarize%20is_server%20%3D%20make_list\(kind%20%3D%3D%20'server'\)%2C%20duration_list%20%3D%20make_list\(duration\)%20%7C%20project%20%20server_durations%20%3D%20array_iff\(is_server%2C%20duration_list%2C%200\)%22%7D)
**Output**
| server\_durations |
| ---------------------------------------- |
| \["45.632µs", "54.622µs", 0, "34.051µs"] |
In this example, `array_iff` selects durations only for `server` spans, setting non-server spans to `0`.
In security logs, `array_iff` can be used to focus on specific cities in which HTTP requests originated, such as showing response durations for certain cities and excluding others.
**Query**
```kusto theme={null}
['sample-http-logs']
| limit 1000
| summarize is_london = make_list(['geo.city'] == "London"), request_duration = make_list(req_duration_ms)
| project london_duration = array_iff(is_london, request_duration, 0)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%20%7C%20limit%201000%20%7C%20summarize%20is_london%20%3D%20make_list\(%5B'geo.city'%5D%20%3D%3D%20'London'\)%2C%20request_duration%20%3D%20make_list\(req_duration_ms\)%20%7C%20project%20london_duration%20%3D%20array_iff\(is_london%2C%20request_duration%2C%200\)%22%7D)
**Output**
| london\_duration |
| ---------------- |
| \[100, 0, 250] |
This example filters the `req_duration_ms` array to show durations for requests from London, with non-matching cities having `0` as duration.
## List of related functions
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
# array_index_of
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-index-of
This page explains how to use the array_index_of function in APL.
The `array_index_of` function in APL returns the zero-based index of the first occurrence of a specified value within an array. If the value isn’t found, the function returns `-1`. Use this function when you need to identify the position of a specific item within an array, such as finding the location of an error code in a sequence of logs or pinpointing a particular value within telemetry data arrays.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `mvfind` function retrieves the position of an element within an array, similar to how `array_index_of` operates in APL. However, note that APL uses a zero-based index for results, while SPL is one-based.
```splunk Splunk example theme={null}
| eval index=mvfind(array, "value")
```
```kusto APL equivalent theme={null}
let index = array_index_of(array, 'value')
```
ANSI SQL doesn’t have a direct equivalent for finding the index of an element within an array. Typically, you would use a combination of array and search functions if supported by your SQL variant.
```sql SQL example theme={null}
SELECT POSITION('value' IN ARRAY[...])
```
```kusto APL equivalent theme={null}
let index = array_index_of(array, 'value')
```
## Usage
### Syntax
```kusto theme={null}
array_index_of(array, lookup_value, [start], [length], [occurrence])
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| array | array | Yes | Input array to search. |
| lookup\_value | scalar | Yes | Scalar value to search for in the array. Accepted data types: long, integer, double, datetime, timespan, or string. |
| start\_index | number | No | The index where to start the search. A negative value offsets the starting search value from the end of the array by `abs(start_index)` steps. |
| length | number | No | Number of values to examine. A value of `-1` means unlimited length. |
| occurrence | number | No | The number of the occurrence. By default `1`. |
### Returns
`array_index_of` returns the zero-based index of the first occurrence of the specified `lookup_value` in `array`. If `lookup_value` doesn’t exist in the array, it returns `-1`.
## Use case examples
You can use `array_index_of` to find the position of a specific HTTP status code within an array of codes in your log analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| take 50
| summarize status_array = make_list(status)
| extend index_500 = array_index_of(status_array, '500')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20summarize%20status_array%20%3D%20make_list\(status\)%20%7C%20extend%20index_500%20%3D%20array_index_of\(status_array%2C%20'500'\)%22%7D)
**Output**
| status\_array | index\_500 |
| ---------------------- | ---------- |
| \["200", "404", "500"] | 2 |
This query creates an array of `status` codes and identifies the position of the first occurrence of the `500` status.
In OpenTelemetry traces, you can find the position of a specific `service.name` within an array of service names to detect when a particular service appears.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| summarize service_array = make_list(['service.name'])
| extend frontend_index = array_index_of(service_array, 'frontend')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20%20service_array%20%3D%20make_list\(%5B'service.name'%5D\)%20%7C%20extend%20frontend_index%20%3D%20array_index_of\(service_array%2C%20'frontend'\)%22%7D)
**Output**
| service\_array | frontend\_index |
| ---------------------------- | --------------- |
| \["frontend", "cartservice"] | 0 |
This query collects the array of services and determines where the `frontend` service first appears.
When working with security logs, `array_index_of` can help identify the index of a particular error or status code, such as `500`, within an array of `status` codes.
**Query**
```kusto theme={null}
['sample-http-logs']
| take 50
| summarize status_array = make_list(status)
| extend index_500 = array_index_of(status_array, '500')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20summarize%20status_array%20%3D%20make_list\(status\)%20%7C%20extend%20index_500%20%3D%20array_index_of\(status_array%2C%20'500'\)%22%7D)
**Output**
| status\_array | index\_500 |
| ---------------------- | ---------- |
| \["200", "404", "500"] | 2 |
This query helps identify at what index the `500` status code appears.
## List of related functions
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left): Rotates elements of an array to the left.
# array_length
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-length
This page explains how to use the array_length function in APL.
The `array_length` function in APL (Axiom Processing Language) returns the length of an array. You can use this function to analyze and filter data by array size, such as identifying log entries with specific numbers of entries or events with multiple tags. This function is useful for analyzing structured data fields that contain arrays, such as lists of error codes, tags, or IP addresses.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use the `mvcount` function to determine the length of a multivalue field. In APL, `array_length` serves the same purpose by returning the size of an array within a column.
```sql Splunk example theme={null}
| eval array_size = mvcount(array_field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend array_size = array_length(array_field)
```
In ANSI SQL, you would use functions such as `CARDINALITY` or `ARRAY_LENGTH` (in databases that support arrays) to get the length of an array. In APL, the `array_length` function is straightforward and works directly with array fields in any dataset.
```sql SQL example theme={null}
SELECT CARDINALITY(array_field) AS array_size
FROM sample_table
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend array_size = array_length(array_field)
```
## Usage
### Syntax
```kusto theme={null}
array_length(array_expression)
```
### Parameters
* array\_expression: An expression representing the array to measure.
### Returns
The function returns an integer representing the number of elements in the specified array.
## Use case example
In OpenTelemetry traces, `array_length` can reveal the number of events associated with a span.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| extend event_count = array_length(events)
| where event_count > 2
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20take%2050%20%7C%20extend%20event_count%20%3D%20array_length\(events\)%20%7C%20where%20event_count%20%3E%202%22%7D)
**Output**
| \_time | trace\_id | span\_id | service.name | event\_count |
| ------------------- | ------------- | --------- | ------------ | ------------ |
| 2024-10-28T12:30:00 | trace\_abc123 | span\_001 | frontend | 3 |
This query finds spans associated with at least three events.
## List of related functions
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left): Shifts array elements one position to the left, moving the first element to the last position.
# array_reverse
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-reverse
This page explains how to use the array_reverse function in APL.
Use the `array_reverse` function in APL to reverse the order of elements in an array. This function is useful when you need to transform data where the sequence matters, such as reversing a list of events for chronological analysis or processing lists in descending order.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, reversing an array isn’t a built-in function, so you typically manipulate the data manually or use workarounds. In APL, `array_reverse` simplifies this process by reversing the array directly.
```sql Splunk example theme={null}
# SPL doesn’t have a direct array_reverse equivalent.
```
```kusto APL equivalent theme={null}
let arr = dynamic([1, 2, 3, 4, 5]);
print reversed_arr = array_reverse(arr)
```
Standard ANSI SQL lacks an explicit function to reverse an array; you generally need to create a custom solution. APL’s `array_reverse` makes reversing an array straightforward.
```sql SQL example theme={null}
-- ANSI SQL lacks a built-in array reverse function.
```
```kusto APL equivalent theme={null}
let arr = dynamic([1, 2, 3, 4, 5]);
print reversed_arr = array_reverse(arr)
```
## Usage
### Syntax
```kusto theme={null}
array_reverse(array_expression)
```
### Parameters
* `array_expression`: The array you want to reverse. This array must be of a dynamic type.
### Returns
Returns the input array with its elements in reverse order.
## Use case examples
Use `array_reverse` to inspect the sequence of actions in log entries, reversing the order to understand the initial steps of a user's session.
**Query**
```kusto theme={null}
['sample-http-logs']
| summarize paths = make_list(uri) by id
| project id, reversed_paths = array_reverse(paths)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20paths%20%3D%20make_list\(uri\)%20by%20id%20%7C%20project%20id%2C%20reversed_paths%20%3D%20array_reverse\(paths\)%22%7D)
**Output**
| id | reversed\_paths |
| ----- | ------------------------------------ |
| U1234 | \['/home', '/cart', '/product', '/'] |
| U5678 | \['/login', '/search', '/'] |
This example identifies a user’s navigation sequence in reverse, showing their entry point into the system.
Use `array_reverse` to analyze trace data by reversing the sequence of span events for each trace, allowing you to trace back the sequence of service calls.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize spans = make_list(span_id) by trace_id
| project trace_id, reversed_spans = array_reverse(spans)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20spans%20%3D%20make_list\(span_id\)%20by%20trace_id%20%7C%20project%20trace_id%2C%20reversed_spans%20%3D%20array_reverse\(spans\)%22%7D)
**Output**
| trace\_id | reversed\_spans |
| --------- | ------------------------- |
| T12345 | \['S4', 'S3', 'S2', 'S1'] |
| T67890 | \['S7', 'S6', 'S5'] |
This example reveals the order in which service calls were made in a trace, but in reverse, aiding in backtracking issues.
Apply `array_reverse` to examine security events, like login attempts or permission checks, in reverse order to identify unusual access patterns or last actions.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '403'
| summarize blocked_uris = make_list(uri) by id
| project id, reversed_blocked_uris = array_reverse(blocked_uris)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'403'%20%7C%20summarize%20blocked_uris%20%3D%20make_list\(uri\)%20by%20id%20%7C%20project%20id%2C%20reversed_blocked_uris%20%3D%20array_reverse\(blocked_uris\)%22%7D)
**Output**
| id | reversed\_blocked\_uris |
| ----- | ------------------------------------- |
| U1234 | \['/admin', '/settings', '/login'] |
| U5678 | \['/account', '/dashboard', '/login'] |
This example helps identify the sequence of unauthorized access attempts by each user.
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
* [array\_shift\_right](/apl/scalar-functions/array-functions/array-shift-right): Shifts array elements to the right.
* [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left): Shifts array elements one position to the left, moving the first element to the last position.
# array_rotate_left
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-rotate-left
This page explains how to use the array_rotate_left function in APL.
The `array_rotate_left` function in Axiom Processing Language (APL) rotates the elements of an array to the left by a specified number of positions. It’s useful when you want to reorder elements in a fixed-length array, shifting elements to the left while moving the leftmost elements to the end. For instance, this function can help analyze sequences where relative order matters but the starting position doesn’t, such as rotating network logs, error codes, or numeric arrays in data for pattern identification.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In APL, `array_rotate_left` allows for direct rotation within the array. Splunk SPL doesn’t have a direct equivalent, so you may need to combine multiple SPL functions to achieve a similar rotation effect.
```sql Splunk example theme={null}
| eval rotated_array = mvindex(array, 1) . "," . mvindex(array, 0)
```
```kusto APL equivalent theme={null}
print rotated_array = array_rotate_left(dynamic([1,2,3,4]), 1)
```
ANSI SQL lacks a direct equivalent for array rotation within arrays. A similar transformation can be achieved using array functions if available or by restructuring the array through custom logic.
```sql SQL example theme={null}
SELECT array_column[2], array_column[3], array_column[0], array_column[1] FROM table
```
```kusto APL equivalent theme={null}
print rotated_array = array_rotate_left(dynamic([1,2,3,4]), 2)
```
## Usage
### Syntax
```kusto theme={null}
array_rotate_left(array, positions)
```
### Parameters
* `array`: The array to be rotated. Use a dynamic data type.
* `positions`: An integer specifying the number of positions to rotate the array to the left.
### Returns
A new array where the elements have been rotated to the left by the specified number of positions.
## Use case example
Analyze traces by rotating the field order for visualization or pattern matching.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend rotated_sequence = array_rotate_left(events, 1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20rotated_sequence%20%3D%20array_rotate_left\(events%2C%201\)%22%7D)
**Output**
```json events theme={null}
[
{
"name": "Enqueued",
"timestamp": 1733997117722909000
},
{
"timestamp": 1733997117722911700,
"name": "Sent"
},
{
"name": "ResponseReceived",
"timestamp": 1733997117723591400
}
]
```
```json rotated_sequence theme={null}
[
{
"timestamp": 1733997117722911700,
"name": "Sent"
},
{
"name": "ResponseReceived",
"timestamp": 1733997117723591400
},
{
"timestamp": 1733997117722909000,
"name": "Enqueued"
}
]
```
This example rotates trace-related fields, which can help to identify variations in trace data when visualized differently.
## List of related functions
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_reverse](/apl/scalar-functions/array-functions/array-reverse): Reverses the order of array elements.
# array_rotate_right
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-rotate-right
This page explains how to use the array_rotate_right function in APL.
The `array_rotate_right` function in APL allows you to rotate the elements of an array to the right by a specified number of positions. This function is useful when you need to reorder data within arrays, either to shift recent events to the beginning, reorder log entries, or realign elements based on specific processing logic.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In APL, the `array_rotate_right` function provides functionality similar to the use of `mvindex` or specific SPL commands for reordering arrays. The rotation here shifts all elements by a set count to the right, maintaining their original order within the new positions.
```sql Splunk example theme={null}
| eval rotated_array=mvindex(array, -3)
```
```kusto APL equivalent theme={null}
| extend rotated_array = array_rotate_right(array, 3)
```
ANSI SQL lacks a direct function for rotating elements within arrays. In APL, the `array_rotate_right` function offers a straightforward way to accomplish this by specifying a rotation count, while SQL users typically require a more complex use of `CASE` statements or custom functions to achieve the same.
```sql SQL example theme={null}
-- No direct ANSI SQL equivalent for array rotation
```
```kusto APL equivalent theme={null}
| extend rotated_array = array_rotate_right(array_column, 3)
```
## Usage
### Syntax
```kusto theme={null}
array_rotate_right(array, count)
```
### Parameters
* `array`: An array to rotate.
* `count`: An integer specifying the number of positions to rotate the array to the right.
### Returns
An array where the elements are rotated to the right by the specified `count`.
## Use case example
In OpenTelemetry traces, rotating an array of span details can help you reorder trace information for performance tracking or troubleshooting.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend rotated_sequence = array_rotate_right(events, 1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20rotated_sequence%20%3D%20array_rotate_right\(events%2C%201\)%22%7D)
**Output**
```json events theme={null}
[
{
"attributes": null,
"name": "Enqueued",
"timestamp": 1733997421220380700
},
{
"name": "Sent",
"timestamp": 1733997421220390400,
"attributes": null
},
{
"attributes": null,
"name": "ResponseReceived",
"timestamp": 1733997421221118500
}
]
```
```json rotated_sequence theme={null}
[
{
"attributes": null,
"name": "ResponseReceived",
"timestamp": 1733997421221118500
},
{
"attributes": null,
"name": "Enqueued",
"timestamp": 1733997421220380700
},
{
"name": "Sent",
"timestamp": 1733997421220390400,
"attributes": null
}
]
```
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left): Rotates elements of an array to the left.
# array_select_dict
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-select-dict
This page explains how to use the array_select_dict function in APL.
The `array_select_dict` function in APL allows you to retrieve a dictionary from an array of dictionaries based on a specified key-value pair. This function is useful when you need to filter arrays and extract specific dictionaries for further processing. If no match exists, it returns `null`. Non-dictionary values in the input array are ignored.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
The `array_select_dict` function in APL is similar to filtering objects in an array based on conditions in Splunk SPL. However, unlike Splunk, where filtering often applies directly to JSON structures, `array_select_dict` specifically targets arrays of dictionaries.
```sql Splunk example theme={null}
| eval selected = mvfilter(array, 'key' == 5)
```
```kusto APL equivalent theme={null}
| project selected = array_select_dict(array, "key", 5)
```
In ANSI SQL, filtering typically involves table rows rather than nested arrays. The APL `array_select_dict` function applies a similar concept to array elements, allowing you to extract dictionaries from arrays using a condition.
```sql SQL example theme={null}
SELECT *
FROM my_table
WHERE JSON_CONTAINS(array_column, '{"key": 5}')
```
```kusto APL equivalent theme={null}
| project selected = array_select_dict(array_column, "key", 5)
```
## Usage
### Syntax
```kusto theme={null}
array_select_dict(array, key, value)
```
### Parameters
| Name | Type | Description |
| ----- | ------- | ------------------------------------- |
| array | dynamic | Input array of dictionaries. |
| key | string | Key to match in each dictionary. |
| value | scalar | Value to match for the specified key. |
### Returns
The function returns the first dictionary in the array that matches the specified key-value pair. If no match exists, it returns `null`. Non-dictionary elements in the array are ignored.
## Use case example
This example demonstrates how to use `array_select_dict` to extract a dictionary where the key `service.name` has the value `frontend`.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend array = dynamic([{"service.name": "frontend", "status_code": "200"}, {"service.name": "backend", "status_code": "500"}])
| project selected = array_select_dict(array, "service.name", "frontend")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20array%20%3D%20dynamic\(%5B%7B'service.name'%3A%20'frontend'%2C%20'status_code'%3A%20'200'%7D%2C%20%7B'service.name'%3A%20'backend'%2C%20'status_code'%3A%20'500'%7D%5D\)%20%7C%20project%20selected%20%3D%20array_select_dict\(array%2C%20'service.name'%2C%20'frontend'\)%22%7D)
**Output**
`{"service.name": "frontend", "status_code": "200"}`
This query selects the first dictionary in the array where `service.name` equals `frontend` and returns it.
## List of related functions
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
# array_shift_left
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-shift-left
This page explains how to use the array_shift_left function in APL.
The `array_shift_left` function in APL rotates the elements of an array to the left by a specified number of positions. If the shift exceeds the array length, it wraps around and continues from the beginning. This function is useful when you need to realign or reorder elements for pattern analysis, comparisons, or other array transformations.
For example, you can use `array_shift_left` to:
* Align time-series data for comparative analysis.
* Rotate log entries for cyclic pattern detection.
* Reorganize multi-dimensional datasets in your queries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, there is no direct equivalent to `array_shift_left`, but you can achieve similar results using custom code or by manipulating arrays manually. In APL, `array_shift_left` simplifies this operation by providing a built-in, efficient implementation.
```sql Splunk example theme={null}
| eval rotated_array = mvindex(array, 1) . mvindex(array, 0)
```
```kusto APL equivalent theme={null}
['array_shift_left'](array, 1)
```
ANSI SQL doesn’t have a native function equivalent to `array_shift_left`. Typically, you would use procedural SQL to write custom logic for this transformation. In APL, the `array_shift_left` function provides an elegant, concise solution.
```sql SQL example theme={null}
-- Pseudo code in SQL
SELECT ARRAY_SHIFT_LEFT(array_column, shift_amount)
```
```kusto APL equivalent theme={null}
['array_shift_left'](array_column, shift_amount)
```
## Usage
### Syntax
```kusto theme={null}
['array_shift_left'](array, shift_amount)
```
### Parameters
| Parameter | Type | Description |
| -------------- | ------- | ------------------------------------------------------ |
| `array` | Array | The array to shift. |
| `shift_amount` | Integer | The number of positions to shift elements to the left. |
### Returns
An array with elements shifted to the left by the specified `shift_amount`. The function wraps the excess elements to the start of the array.
## Use case example
Reorganize span events to analyze dependencies in a different sequence.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| extend shifted_events = array_shift_left(events, 1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20take%2050%20%7C%20extend%20shifted_events%20%3D%20array_shift_left\(events%2C%201\)%22%7D)
**Output**
```json events theme={null}
[
{
"name": "Enqueued",
"timestamp": 1734001111273917000,
"attributes": null
},
{
"attributes": null,
"name": "Sent",
"timestamp": 1734001111273925400
},
{
"name": "ResponseReceived",
"timestamp": 1734001111274167300,
"attributes": null
}
]
```
```json shifted_events theme={null}
[
{
"attributes": null,
"name": "Sent",
"timestamp": 1734001111273925400
},
{
"name": "ResponseReceived",
"timestamp": 1734001111274167300,
"attributes": null
},
null
]
```
This query shifts span events for `frontend` services to analyze the adjusted sequence.
## List of related functions
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left): Rotates elements of an array to the left.
* [array\_shift\_right](/apl/scalar-functions/array-functions/array-shift-right): Shifts array elements to the right.
# array_shift_right
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-shift-right
This page explains how to use the array_shift_right function in APL.
The `array_shift_right` function in Axiom Processing Language (APL) shifts the elements of an array one position to the right. The last element of the array wraps around and becomes the first element. You can use this function to reorder elements, manage time-series data in circular arrays, or preprocess arrays for specific analytical needs.
### When to use the function
* To manage and rotate data within arrays.
* To implement cyclic operations or transformations.
* To manipulate array data structures in log analysis or telemetry contexts.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, similar functionality might be achieved using custom code to rotate array elements, as there is no direct equivalent to `array_shift_right`. APL provides this functionality natively, making it easier to work with arrays directly.
```sql Splunk example theme={null}
| eval shifted_array=mvappend(mvindex(array,-1),mvindex(array,0,len(array)-1))
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend shifted_array = array_shift_right(array)
```
ANSI SQL doesn’t have a built-in function for shifting arrays. In SQL, achieving this would involve user-defined functions or complex subqueries. In APL, `array_shift_right` simplifies this operation significantly.
```sql SQL example theme={null}
WITH shifted AS (
SELECT
array_column[ARRAY_LENGTH(array_column)] AS first_element,
array_column[1:ARRAY_LENGTH(array_column)-1] AS rest_of_elements
FROM table
)
SELECT ARRAY_APPEND(first_element, rest_of_elements) AS shifted_array
FROM shifted
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend shifted_array = array_shift_right(array)
```
## Usage
### Syntax
```kusto theme={null}
array_shift_right(array: array) : array
```
### Parameters
| Parameter | Type | Description |
| --------- | ----- | ------------------------------------------ |
| `array` | array | The input array whose elements are shifted |
### Returns
An array with its elements shifted one position to the right. The last element of the input array wraps around to the first position.
## Use case example
Reorganize span events in telemetry data for visualization or debugging.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| extend shifted_events = array_shift_right(events, 1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20take%2050%20%7C%20extend%20shifted_events%20%3D%20array_shift_right\(events%2C%201\)%22%7D)
**Output**
```json events theme={null}
[
{
"name": "Enqueued",
"timestamp": 1734001215487927300,
"attributes": null
},
{
"attributes": null,
"name": "Sent",
"timestamp": 1734001215487937000
},
{
"timestamp": 1734001215488191000,
"attributes": null,
"name": "ResponseReceived"
}
]
```
```json shifted_events theme={null}
[
null,
{
"timestamp": 1734001215487927300,
"attributes": null,
"name": "Enqueued"
},
{
"attributes": null,
"name": "Sent",
"timestamp": 1734001215487937000
}
]
```
The query rotates span events for better trace debugging.
## List of related functions
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_rotate\_left](/apl/scalar-functions/array-functions/array-rotate-left): Rotates elements of an array to the left.
* [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left): Shifts array elements one position to the left, moving the first element to the last position.
# array_slice
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-slice
This page explains how to use the array_slice function in APL.
The `array_slice` function in APL extracts a subset of elements from an array, based on specified start and end indices. This function is useful when you want to analyze or transform a portion of data within arrays, such as trimming logs, filtering specific events, or working with trace data in OpenTelemetry logs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you can use `mvindex` to extract elements from an array. APL's `array_slice` is similar but more expressive, allowing you to specify slices with optional bounds.
```sql Splunk example theme={null}
| eval sliced_array=mvindex(my_array, 1, 3)
```
```kusto APL equivalent theme={null}
T | extend sliced_array = array_slice(my_array, 1, 3)
```
In ANSI SQL, arrays are often handled using JSON functions or window functions, requiring workarounds to slice arrays. In APL, `array_slice` directly handles arrays, making operations more concise.
```sql SQL example theme={null}
SELECT JSON_EXTRACT(my_array, '$[1:3]') AS sliced_array FROM my_table
```
```kusto APL equivalent theme={null}
T | extend sliced_array = array_slice(my_array, 1, 3)
```
## Usage
### Syntax
```kusto theme={null}
array_slice(array, start, end)
```
### Parameters
| Parameter | Description |
| --------- | ------------------------------------------------------------------------------------------------- |
| `array` | The input array to slice. |
| `start` | The starting index of the slice (inclusive). If negative, it’s counted from the end of the array. |
| `end` | The ending index of the slice (exclusive). If negative, it’s counted from the end of the array. |
### Returns
An array containing the elements from the specified slice. If the indices are out of bounds, it adjusts to return valid elements without error.
## Use case example
Filter spans from trace data to analyze a specific range of events.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where array_length(events) > 4
| extend sliced_events = array_slice(events, -3, -1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20array_length\(events\)%20%3E%204%20%7C%20extend%20sliced_events%20%3D%20array_slice\(events%2C%20-3%2C%20-1\)%22%7D)
**Output**
```json events theme={null}
[
{
"timestamp": 1734001336443987200,
"attributes": null,
"name": "prepared"
},
{
"attributes": {
"feature_flag.provider_name": "flagd",
"feature_flag.variant": "off",
"feature_flag.key": "paymentServiceUnreachable"
},
"name": "feature_flag",
"timestamp": 1734001336444001800
},
{
"name": "charged",
"timestamp": 1734001336445970200,
"attributes": {
"custom": {
"app.payment.transaction.id": "49567406-21f4-41aa-bab2-69911c055753"
}
}
},
{
"name": "shipped",
"timestamp": 1734001336446488600,
"attributes": {
"custom": {
"app.shipping.tracking.id": "9a3b7a5c-aa41-4033-917f-50cb7360a2a4"
}
}
},
{
"attributes": {
"feature_flag.variant": "off",
"feature_flag.key": "kafkaQueueProblems",
"feature_flag.provider_name": "flagd"
},
"name": "feature_flag",
"timestamp": 1734001336461096700
}
]
```
```json sliced_events theme={null}
[
{
"name": "charged",
"timestamp": 1734001336445970200,
"attributes": {
"custom": {
"app.payment.transaction.id": "49567406-21f4-41aa-bab2-69911c055753"
}
}
},
{
"name": "shipped",
"timestamp": 1734001336446488600,
"attributes": {
"custom": {
"app.shipping.tracking.id": "9a3b7a5c-aa41-4033-917f-50cb7360a2a4"
}
}
}
]
```
Slices the last three events from the `events` array, excluding the final one.
## List of related functions
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_reverse](/apl/scalar-functions/array-functions/array-reverse): Reverses the order of array elements.
* [array\_shift\_right](/apl/scalar-functions/array-functions/array-shift-right): Shifts array elements to the right.
# array_sort_asc
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-sort-asc
This page explains how to use the array_sort_asc function in APL.
Use the `array_sort_asc` function to return a new array that contains all the elements of the input array, sorted in ascending order. This function is useful when you want to normalize the order of array elements for comparison, presentation, or further processing—such as identifying patterns, comparing sequences, or selecting boundary values.
You can apply `array_sort_asc` to arrays of numbers, strings, or dynamic objects, making it useful across many telemetry, logging, and security data scenarios.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, arrays are typically handled through `mvsort`, which sorts multivalue fields in ascending order. In APL, `array_sort_asc` provides similar functionality but works on dynamic arrays and returns a new sorted array.
```sql Splunk example theme={null}
| eval sorted_values = mvsort(multivalue_field)
```
```kusto APL equivalent theme={null}
datatable(arr: dynamic)
[
dynamic([4, 2, 5])
]
| extend sorted_arr = array_sort_asc(arr)
```
ANSI SQL does not directly support array data types or array sorting. You typically normalize arrays with `UNNEST` and sort the results using `ORDER BY`. In APL, you can sort arrays inline using `array_sort_asc`, which is more concise and expressive.
```sql SQL example theme={null}
SELECT val
FROM UNNEST([4, 2, 5]) AS val
ORDER BY val ASC
```
```kusto APL equivalent theme={null}
print sorted_arr = array_sort_asc(dynamic([4, 2, 5]))
```
## Usage
### Syntax
```kusto theme={null}
array_sort_asc(array)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------- | -------- | ------------------------------------------------------------------------- |
| array | dynamic | ✓ | An array of values to sort. Can be numbers, strings, or other primitives. |
### Returns
A new array that contains the same elements as the input array, sorted in ascending order. If the input is not an array, the function returns an empty array.
## Example
**Query**
```kusto theme={null}
['sample-http-logs']
| project sort = array_sort_asc(dynamic(['x', 'a', 'm', 'o', 'i']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20project%20sort%20%3D%20array_sort_asc\(dynamic\(%5B'x'%2C%20'a'%2C%20'm'%2C%20'o'%2C%20'i'%5D\)\)%22%7D)
**Output**
```json theme={null}
[
[
"a",
"i",
"m",
"o",
"x"
]
]
```
## List of related functions
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Returns the position of an element in an array. Use after sorting to find where values fall.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Use to understand array size before or after sorting.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Returns a subrange of the array. Useful after sorting to get top-N or bottom-N elements.
* [array\_sort\_desc](/apl/scalar-functions/array-functions/array-sort-desc): Sorts array elements in descending order. Use when you need reverse ordering.
# array_sort_desc
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-sort-desc
This page explains how to use the array_sort_desc function in APL.
Use the `array_sort_desc` function in APL to sort the elements of an array in descending order. This function is especially useful when working with numerical data or categorical data where you want to prioritize higher values first—such as showing the longest durations, highest response times, or most severe error codes at the top of an array.
You can use `array_sort_desc` in scenarios where ordering matters within grouped aggregations, such as collecting response times per user or span durations per trace, and then sorting them to identify the highest or most impactful values.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk doesn’t have a direct equivalent to `array_sort_desc`, but similar outcomes can be achieved using `mvsort` with a custom sort order (and sometimes `reverse`). In APL, `array_sort_desc` explicitly performs a descending sort on array elements, making it more straightforward.
```sql Splunk example theme={null}
... | stats list(duration) as durations by id
... | eval durations=reverse(mvsort(durations))
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| summarize durations=make_list(duration) by trace_id
| extend durations=array_sort_desc(durations)
```
ANSI SQL does not support arrays or array functions natively. You typically use window functions or subqueries to order values. In APL, you can work with arrays directly and apply `array_sort_desc` to sort them.
```sql SQL example theme={null}
SELECT trace_id, ARRAY_AGG(duration ORDER BY duration DESC) AS durations
FROM traces
GROUP BY trace_id;
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| summarize durations=make_list(duration) by trace_id
| extend durations=array_sort_desc(durations)
```
## Usage
### Syntax
```kusto theme={null}
array_sort_desc(array)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ----- | -------- | ----------------------------------------------------- |
| array | array | ✓ | The input array whose elements are sorted descending. |
### Returns
If the input is a valid array, the function returns a new array with its elements sorted in descending order. If the array is empty or contains incompatible types, it returns an empty array.
## Example
**Query**
```kusto theme={null}
['sample-http-logs']
| project sort = array_sort_desc(dynamic(['x', 'a', 'm', 'o', 'i']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20project%20sort%20%3D%20array_sort_desc\(dynamic\(%5B'x'%2C%20'a'%2C%20'm'%2C%20'o'%2C%20'i'%5D\)\)%22%7D)
**Output**
```json theme={null}
[
[
"x",
"o",
"m",
"i",
"a"
]
]
```
## List of related functions
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Returns the index of a value in an array. Useful after sorting to locate specific elements.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Useful for measuring the size of arrays before or after sorting.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a range of elements from an array. Use it after sorting to get the top N or bottom N values.
* [array\_sort\_asc](/apl/scalar-functions/array-functions/array-sort-asc): Sorts an array in ascending order. Use this when you want to prioritize smaller values first.
# array_split
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-split
This page explains how to use the array_split function in APL.
The `array_split` function in APL splits an array into smaller subarrays based on specified split indices and packs the generated subarrays into a dynamic array. This function is useful when you want to partition data for analysis, batch processing, or distributing workloads across smaller units.
You can use `array_split` to:
* Divide large datasets into manageable chunks for processing.
* Create segments for detailed analysis or visualization.
* Handle nested data structures for targeted processing.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, array manipulation is achieved through functions like `mvzip` and `mvfilter`, but there is no direct equivalent to `array_split`. APL provides a more explicit approach for splitting arrays.
```sql Splunk example theme={null}
| eval split_array = mvzip(array_field, "2")
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| extend split_array = array_split(events, 2)
```
ANSI SQL doesn’t have built-in functions for directly splitting arrays. APL provides this capability natively, making it easier to handle array operations within queries.
```sql SQL example theme={null}
-- SQL typically requires custom functions or JSON manipulation.
SELECT * FROM dataset WHERE JSON_ARRAY_LENGTH(array_field) > 0;
```
```kusto APL equivalent theme={null}
['otel-demo-traces']
| extend split_array = array_split(events, 2)
```
## Usage
### Syntax
```kusto theme={null}
array_split(array, index)
```
### Parameters
| Parameter | Description | Type |
| --------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `array` | The array to split. | Dynamic |
| `index` | An integer or dynamic array of integers. These zero-based split indices indicate the location at which to split the array. | Integer or Dynamic |
### Returns
Returns a dynamic array containing N+1 arrays where N is the number of input indices. The original array is split at the input indices.
## Use case examples
### Single split index
Split large event arrays into manageable chunks for analysis.
```kusto theme={null}
['otel-demo-traces']
| where array_length(events) == 3
| extend split_events = array_split(events, 2)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20array_length\(events\)%20%3D%3D%203%20%7C%20extend%20span_chunks%20%3D%20array_split\(events%2C%202\)%22%7D)
**Output**
```json events theme={null}
[
{
"timestamp": 1734033733465219300,
"name": "Enqueued"
},
{
"name": "Sent",
"timestamp": 1734033733465228500
},
{
"timestamp": 1734033733465455900,
"name": "ResponseReceived"
}
]
```
```json split_events theme={null}
[
[
{
"timestamp": 1734033733465219300,
"name": "Enqueued"
},
{
"name": "Sent",
"timestamp": 1734033733465228500
}
],
[
{
"timestamp": 1734033733465455900,
"name": "ResponseReceived"
}
]
]
```
This query splits the `events` array at index `2` into two subarrays for further processing.
### Multiple split indeces
Divide traces into fixed-size segments for better debugging.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where array_length(events) == 3
| extend split_events = array_split(events, dynamic([1,2]))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20array_length\(events\)%20%3D%3D%203%20%7C%20extend%20span_chunks%20%3D%20array_split\(events%2C%20dynamic\(%5B1%2C2%5D\)\)%22%7D)
**Output**
```json events theme={null}
[
{
"attributes": null,
"name": "Enqueued",
"timestamp": 1734034755085206000
},
{
"name": "Sent",
"timestamp": 1734034755085215500,
"attributes": null
},
{
"attributes": null,
"name": "ResponseReceived",
"timestamp": 1734034755085424000
}
]
```
```json split_events theme={null}
[
[
{
"timestamp": 1734034755085206000,
"attributes": null,
"name": "Enqueued"
}
],
[
{
"timestamp": 1734034755085215500,
"attributes": null,
"name": "Sent"
}
],
[
{
"attributes": null,
"name": "ResponseReceived",
"timestamp": 1734034755085424000
}
]
]
```
This query splits the `events` array into three subarrays based on the indices `[1,2]`.
## List of related functions
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left): Shifts array elements one position to the left, moving the first element to the last position.
# array_sum
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/array-sum
This page explains how to use the array_sum function in APL.
The `array_sum` function in APL computes the sum of all numerical elements in an array. This function is particularly useful when you want to aggregate numerical values stored in an array field, such as durations, counts, or measurements, across events or records. Use `array_sum` when your dataset includes array-type fields, and you need to quickly compute their total.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might need to use commands or functions such as `mvsum` for similar operations. In APL, `array_sum` provides a direct method to compute the sum of numerical arrays.
```sql Splunk example theme={null}
| eval total_duration = mvsum(duration_array)
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend total_duration = array_sum(duration_array)
```
ANSI SQL doesn’t natively support array operations like summing array elements. However, you can achieve similar results with `UNNEST` and `SUM`. In APL, `array_sum` simplifies this by handling array summation directly.
```sql SQL example theme={null}
SELECT SUM(value) AS total_duration
FROM UNNEST(duration_array) AS value;
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend total_duration = array_sum(duration_array)
```
## Usage
### Syntax
```kusto theme={null}
array_sum(array_expression)
```
### Parameters
| Parameter | Type | Description |
| ------------------ | ----- | ------------------------------------------ |
| `array_expression` | array | An array of numerical values to be summed. |
### Returns
The function returns the sum of all numerical values in the array. If the array is empty or contains no numerical values, the result is `null`.
## Use case example
Summing the duration of all events in an array field.
**Query**
```kusto theme={null}
['otel-demo-traces']
| summarize event_duration = make_list(duration) by ['service.name']
| extend total_event_duration = array_sum(event_duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20summarize%20event_duration%20%3D%20make_list\(duration\)%20by%20%5B'service.name'%5D%20%7C%20extend%20total_event_duration%20%3D%20array_sum\(event_duration\)%22%7D)
**Output**
| service.name | total\_event\_duration |
| --------------- | ---------------------- |
| frontend | 1667269530000 |
| checkoutservice | 3801404276900 |
The query calculates the total duration of all events for each service.
## List of related functions
* [array\_rotate\_right](/apl/scalar-functions/array-functions/array-rotate-right): Rotates array elements to the right by a specified number of positions.
* [array\_reverse](/apl/scalar-functions/array-functions/array-reverse): Reverses the order of array elements.
* [array\_shift\_left](/apl/scalar-functions/array-functions/array-shift-left): Shifts array elements one position to the left, moving the first element to the last position.
# bag_has_key
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/bag-has-key
This page explains how to use the bag_has_key function in APL.
Use the `bag_has_key` function in APL to check whether a dynamic property bag contains a specific key. This is helpful when your data includes semi-structured or nested fields encoded as dynamic objects, such as JSON-formatted logs or telemetry metadata.
You often encounter property bags in observability data where log entries, spans, or alerts carry key–value metadata. Use `bag_has_key` to filter, conditionally process, or join such records based on the existence of specific keys, without needing to extract the values themselves.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you often check whether a key exists in a JSON object using `spath` and conditional logic. APL simplifies this with `bag_has_key`, which returns a boolean directly and avoids explicit parsing.
```sql Splunk example theme={null}
| eval hasKey=if(isnull(spath(data, "keyName")), false, true)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where bag_has_key(dynamic_field, 'keyName')
```
ANSI SQL doesn’t include native support for property bags or dynamic fields. You typically use JSON functions to access keys in JSON-formatted strings. In APL, dynamic fields are first-class, and `bag_has_key` provides direct support for key existence checks.
```sql SQL example theme={null}
SELECT *
FROM logs
WHERE JSON_EXTRACT(json_column, '$.keyName') IS NOT NULL
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where bag_has_key(dynamic_field, 'keyName')
```
## Usage
### Syntax
```kusto theme={null}
bag_has_key(bag: dynamic, key: string)
```
### Parameters
| Name | Type | Description |
| ----- | --------- | ---------------------------------------------------------------- |
| `bag` | `dynamic` | A dynamic value representing a property bag (e.g., JSON object). |
| `key` | `string` | The key to check for within the property bag. |
### Returns
Returns a `bool` value:
* `true` if the specified key exists in the property bag
* `false` otherwise
## Use case examples
Use `bag_has_key` to filter log entries that include a specific metadata key embedded in a dynamic object.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend metadata = bag_pack('source', 'cdn', 'env', 'prod')
| where bag_has_key(metadata, 'env')
| project _time, id, method, uri, status, metadata
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20metadata%20%3D%20bag_pack%28%27source%27%2C%20%27cdn%27%2C%20%27env%27%2C%20%27prod%27%29%20%7C%20where%20bag_has_key%28metadata%2C%20%27env%27%29%20%7C%20project%20_time%2C%20id%2C%20method%2C%20uri%2C%20status%2C%20metadata%22%7D)
**Output**
| \_time | id | method | uri | status | metadata |
| ----------------- | ---- | ------ | -------------- | ------ | ------------------------------ |
| 2025-05-27T12:30Z | u123 | GET | /login | 200 | \{'source':'cdn','env':'prod'} |
| 2025-05-27T12:31Z | u124 | POST | /cart/checkout | 500 | \{'source':'cdn','env':'prod'} |
The query filters logs where the synthetic `metadata` bag includes the key `'env'`.
Use `bag_has_key` to filter spans that include specific dynamic span attributes.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend attributes = bag_pack('user', 'alice', 'feature_flag', 'beta')
| where bag_has_key(attributes, 'feature_flag')
| project _time, trace_id, span_id, ['service.name'], kind, attributes
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20attributes%20%3D%20bag_pack%28%27user%27%2C%20%27alice%27%2C%20%27feature_flag%27%2C%20%27beta%27%29%20%7C%20where%20bag_has_key%28attributes%2C%20%27feature_flag%27%29%20%7C%20project%20_time%2C%20trace_id%2C%20span_id%2C%20%5B%27service.name%27%5D%2C%20kind%2C%20attributes%22%7D)
**Output**
| \_time | trace\_id | span\_id | \['service.name'] | kind | attributes |
| ----------------- | --------- | -------- | ----------------- | ------ | ---------------------------------------- |
| 2025-05-27T10:02Z | abc123 | span567 | frontend | client | \{'user':'alice','feature\_flag':'beta'} |
The query selects spans with dynamic `attributes` bags containing the `'feature_flag'` key.
Use `bag_has_key` to identify HTTP logs where the request metadata contains sensitive audit-related keys.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend audit_info = bag_pack('action', 'delete', 'reason', 'admin_override')
| where bag_has_key(audit_info, 'reason')
| project _time, id, uri, status, audit_info
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20audit_info%20%3D%20bag_pack%28%27action%27%2C%20%27delete%27%2C%20%27reason%27%2C%20%27admin_override%27%29%20%7C%20where%20bag_has_key%28audit_info%2C%20%27reason%27%29%20%7C%20project%20_time%2C%20id%2C%20uri%2C%20status%2C%20audit_info%22%7D)
**Output**
| \_time | id | uri | status | audit\_info |
| ----------------- | ---- | ------------- | ------ | ----------------------------------------------- |
| 2025-05-27T13:45Z | u999 | /admin/delete | 403 | \{'action':'delete','reason':'admin\_override'} |
The query returns only logs where the `audit_info` bag includes the `'reason'` key, indicating administrative override events.
## List of related functions
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Returns all keys in a dynamic property bag. Use it when you need to enumerate available keys.
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Converts a list of key-value pairs to a dynamic property bag. Use when you need to build a bag.
# bag_keys
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/bag-keys
This page explains how to use the bag_keys function in APL.
Use the `bag_keys` function in APL to extract the keys of a dynamic (bag) object as an array of strings. This is useful when you want to inspect or manipulate the structure of a dynamic field—such as JSON-like nested objects—without needing to know its exact schema in advance.
Use `bag_keys` when you’re working with semi-structured data and want to:
* Discover what properties are present in a dynamic object.
* Iterate over the keys programmatically using other array functions.
* Perform validation or debugging tasks to ensure all expected keys exist.
This function is especially helpful in log analytics, observability pipelines, and security auditing, where dynamic properties are often collected from various services or devices.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically interact with JSON-like fields using the `spath` command or use `keys(_raw)` to retrieve field names. In APL, `bag_keys` serves a similar purpose by returning an array of keys from a dynamic object.
```sql Splunk example theme={null}
| eval key_list=keys(data_field)
```
```kusto APL equivalent theme={null}
datatable(data: dynamic)
[
dynamic({ "ip": "127.0.0.1", "status": "200", "method": "GET" })
]
| extend keys = bag_keys(data)
```
ANSI SQL doesn’t have native support for dynamic objects or JSON key introspection in the same way. However, some SQL dialects (like PostgreSQL or BigQuery) provide JSON-specific functions for extracting keys. `bag_keys` is the APL equivalent for dynamically introspecting JSON objects.
```sql SQL example theme={null}
SELECT JSON_OBJECT_KEYS(data) FROM logs;
```
```kusto APL equivalent theme={null}
datatable(data: dynamic)
[
dynamic({ "ip": "127.0.0.1", "status": "200", "method": "GET" })
]
| extend keys = bag_keys(data)
```
## Usage
### Syntax
```kusto theme={null}
bag_keys(bag)
```
### Parameters
| Name | Type | Description |
| ----- | --------- | -------------------------------------------------- |
| `bag` | `dynamic` | The dynamic object whose keys you want to extract. |
### Returns
An array of type `string[]` containing the names of the keys in the dynamic object. If the input isn’t a dynamic object, the function returns `null`.
## Use case examples
Use `bag_keys` to audit dynamic metadata fields in HTTP logs where each record contains a nested object representing additional request attributes.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend metadata = dynamic({ 'os': 'Windows', 'browser': 'Firefox', 'device': 'Desktop' })
| extend key_list = bag_keys(metadata)
| project _time, uri, metadata, key_list
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20metadata%20%3D%20dynamic\(%7B%20'os'%3A%20'Windows'%2C%20'browser'%3A%20'Firefox'%2C%20'device'%3A%20'Desktop'%20%7D\)%20%7C%20extend%20key_list%20%3D%20bag_keys\(metadata\)%20%7C%20project%20_time%2C%20uri%2C%20metadata%2C%20key_list%22%7D)
**Output**
| \_time | uri | metadata | key\_list |
| ------------------- | ------ | ------------------------------------------------- | ---------------------------- |
| 2025-05-26 12:01:23 | /login | \{os: Windows, browser: Firefox, device: Desktop} | \[‘os’, ‘browser’, ‘device’] |
This query inspects a simulated metadata object and returns the list of its keys, helping you debug inconsistencies or missing fields.
Use `bag_keys` to examine custom span attributes encoded as dynamic fields within OpenTelemetry trace events.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend attributes = dynamic({ 'user_id': 'abc123', 'feature_flag': 'enabled' })
| extend attribute_keys = bag_keys(attributes)
| project _time, ['service.name'], kind, attributes, attribute_keys
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20attributes%20%3D%20dynamic\(%7B%20'user_id'%3A%20'abc123'%2C%20'feature_flag'%3A%20'enabled'%20%7D\)%20%7C%20extend%20attribute_keys%20%3D%20bag_keys\(attributes\)%20%7C%20project%20_time%2C%20%5B'service.name'%5D%2C%20kind%2C%20attributes%2C%20attribute_keys%22%7D)
**Output**
| \_time | \['service.name'] | kind | attributes | attribute\_keys |
| ------------------- | ----------------- | ------ | ------------------------------------------- | ------------------------------ |
| 2025-05-26 13:14:01 | frontend | client | \{user\_id: abc123, feature\_flag: enabled} | \[‘user\_id’, ‘feature\_flag’] |
This query inspects the custom span-level attributes and extracts their keys to verify attribute coverage or completeness.
Use `bag_keys` to list all security-related fields captured dynamically during request monitoring for auditing or compliance.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend security_context = dynamic({ 'auth_status': 'success', 'role': 'admin', 'ip': '192.168.1.5' })
| extend fields = bag_keys(security_context)
| project _time, status, ['geo.country'], security_context, fields
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20security_context%20%3D%20dynamic\(%7B%20'auth_status'%3A%20'success'%2C%20'role'%3A%20'admin'%2C%20'ip'%3A%20'192.168.1.5'%20%7D\)%20%7C%20extend%20fields%20%3D%20bag_keys\(security_context\)%20%7C%20project%20_time%2C%20status%2C%20%5B'geo.country'%5D%2C%20security_context%2C%20fields%22%7D)
**Output**
| \_time | status | \['geo.country'] | security\_context | fields |
| ------------------- | ------ | ---------------- | ------------------------------------------------------ | ------------------------------- |
| 2025-05-26 15:32:10 | 200 | US | \{auth\_status: success, role: admin, ip: 192.168.1.5} | \[‘auth\_status’, ‘role’, ‘ip’] |
This helps you audit security metadata in requests and ensure key fields are present across records.
## List of related functions
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Converts a list of key-value pairs to a dynamic property bag. Use when you need to build a bag.
* [bag\_has\_key](/apl/scalar-functions/array-functions/bag-has-key): Checks whether a dynamic property bag contains a specific key.
# bag_pack
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/bag-pack
This page explains how to use the bag_pack and pack functions in APL.
Use the `bag_pack` function in APL to construct a dynamic property bag from a list of key-value pairs. A property bag is a flexible data structure where keys are strings and values are dynamic types. This function is useful when you want to combine multiple values into a single dynamic object, often to simplify downstream processing or export.
You typically use `bag_pack` in projection scenarios to consolidate structured data—for example, packing related request metadata into one field, or grouping trace data by contextual attributes. This makes it easier to output, filter, or transform nested information.
The `pack` and `bag_pack` functions are equivalent in APL.
A common use is `bag_pack(*)` that gets all fields of your dataset as a bag. The wildcard `*` is useful to match all fields in the current row, but it increases query complexity and decreases stability and performance.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you can use `mvzip` and `eval` to create key-value mappings, or use `spath` to interpret JSON data. However, packing data into a true key-value structure for export or downstream use requires JSON manipulation. APL’s `bag_pack` provides a native and type-safe way to do this.
```sql Splunk example theme={null}
| eval metadata=tojson({"status": status, "duration": req_duration_ms})
```
```kusto APL equivalent theme={null}
project metadata = bag_pack('status', status, 'duration', req_duration_ms)
```
SQL doesn’t have a direct built-in function like `bag_pack`. To achieve similar behavior, you typically construct JSON objects using functions like `JSON_OBJECT` or use user-defined types. In APL, `bag_pack` is the idiomatic way to construct dynamic objects with labeled fields.
```sql SQL example theme={null}
SELECT JSON_OBJECT('status' VALUE status, 'duration' VALUE req_duration_ms) AS metadata FROM logs;
```
```kusto APL equivalent theme={null}
project metadata = bag_pack('status', status, 'duration', req_duration_ms)
```
## Usage
### Syntax
```kusto theme={null}
bag_pack(key1, value1, key2, value2, ...)
```
### Parameters
| Name | Type | Description |
| --------------------- | -------- | ------------------------------------------------------------------------ |
| `key1, key2, ...` | `string` | The names of the fields to include in the property bag. |
| `value1, value2, ...` | `scalar` | The corresponding values for the keys. Values can be of any scalar type. |
The number of keys must equal the number of values. Keys must be string literals or string expressions.
### Returns
A `dynamic` value representing a property bag (dictionary) where keys are strings and values are the corresponding values.
## Use case examples
Use `bag_pack` to create a structured object that captures key request attributes for easier inspection or export.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '500'
| project _time, error_context = bag_pack('uri', uri, 'method', method, 'duration_ms', req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'500'%20%7C%20project%20_time%2C%20error_context%20%3D%20bag_pack%28'uri'%2C%20uri%2C%20'method'%2C%20method%2C%20'duration_ms'%2C%20req_duration_ms%29%22%7D)
**Output**
| \_time | error\_context |
| -------------------- | -------------------------------------------------------------- |
| 2025-05-27T10:00:00Z | `{ "uri": "/api/data", "method": "GET", "duration_ms": 342 }` |
| 2025-05-27T10:05:00Z | `{ "uri": "/api/auth", "method": "POST", "duration_ms": 879 }` |
The query filters HTTP logs to 500 errors and consolidates key request fields into a single dynamic column named `error_context`.
Use `bag_pack` to enrich trace summaries with service metadata for each span.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where ['service.name'] == 'checkout'
| project trace_id, span_id, span_info = bag_pack('kind', kind, 'duration', duration, 'status_code', status_code)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'checkout'%20%7C%20project%20trace_id%2C%20span_id%2C%20span_info%20%3D%20bag_pack%28'kind'%2C%20kind%2C%20'duration'%2C%20duration%2C%20'status_code'%2C%20status_code%29%22%7D)
**Output**
| trace\_id | span\_id | span\_info |
| --------- | -------- | ------------------------------------------------------------------------------ |
| a1b2... | f9c3... | `{ "kind": "server", "duration": "00:00:00.1240000", "status_code": "OK" }` |
| c3d4... | h7e2... | `{ "kind": "client", "duration": "00:00:00.0470000", "status_code": "ERROR" }` |
The query targets spans from the `checkout` and combines attributes into a single object per span.
Use `bag_pack` to create a compact event summary combining user ID and geographic info for anomaly detection.
**Query**
```kusto theme={null}
['sample-http-logs']
| project _time, id, geo_summary = bag_pack('city', ['geo.city'], 'country', ['geo.country'])
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20project%20_time%2C%20id%2C%20geo_summary%20%3D%20bag_pack\('city'%2C%20%5B'geo.city'%5D%2C%20'country'%2C%20%5B'geo.country'%5D\)%22%7D)
**Output**
| \_time | id | geo\_summary |
| -------------------- | -------- | --------------------------------------- |
| 2025-05-27T12:00:00Z | user\_01 | `{ "city": "Berlin", "country": "DE" }` |
| 2025-05-27T12:01:00Z | user\_02 | `{ "city": "Paris", "country": "FR" }` |
The query helps identify patterns in failed access attempts by summarizing location data per event.
## List of related functions
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Returns all keys in a dynamic property bag. Use it when you need to enumerate available keys.
* [bag\_has\_key](/apl/scalar-functions/array-functions/bag-has-key): Checks whether a dynamic property bag contains a specific key.
# bag_zip
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/bag-zip
This page explains how to use the bag_zip function in APL.
Use the `bag_zip` function in APL to combine two arrays—one containing keys and another containing values—into a single dynamic property bag (dictionary). This is useful when you have parallel arrays that you want to merge into a structured key-value object for easier manipulation or output.
You typically use `bag_zip` when parsing data that arrives as separate lists of keys and values, or when transforming array-based structures into more readable dictionary formats. This function is especially helpful in log analysis, data transformation pipelines, and when preparing data for downstream systems that expect key-value pairs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `mvzip` to combine two multi-value fields into paired elements. APL's `bag_zip` goes further by creating a true dictionary object from separate key and value arrays.
```sql Splunk example theme={null}
| eval zipped=mvzip(keys, values, '=')
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend keys = dynamic(['status', 'method', 'city'])
| extend values = dynamic(['200', 'GET', 'Seattle'])
| extend result = bag_zip(keys, values)
```
ANSI SQL doesn't have native support for combining arrays into key-value dictionaries. You typically need to use JSON functions or complex `CASE` statements to achieve similar results. APL's `bag_zip` provides a direct and type-safe way to perform this operation.
```sql SQL example theme={null}
SELECT JSON_OBJECT('key1', value1, 'key2', value2)
FROM table_name
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend keys = dynamic(['key1', 'key2'])
| extend values = dynamic([value1, value2])
| extend result = bag_zip(keys, values)
```
## Usage
### Syntax
```kusto theme={null}
bag_zip(keys, values)
```
### Parameters
| Name | Type | Description |
| -------- | --------- | ------------------------------------------------------------------------- |
| `keys` | `dynamic` | An array of strings representing the keys for the resulting property bag. |
| `values` | `dynamic` | An array of values corresponding to the keys. Can contain any data type. |
### Returns
A `dynamic` property bag (dictionary) where each key from the `keys` array is paired with the corresponding value from the `values` array. If the arrays have different lengths, the function pairs elements up to the length of the shorter array and ignores any extra elements.
## Use case examples
Use `bag_zip` to combine metadata keys and values extracted from HTTP logs into structured objects for easier analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend metadata_keys = dynamic(['status_code', 'request_method', 'city'])
| extend metadata_values = pack_array(status, method, ['geo.city'])
| extend request_metadata = bag_zip(metadata_keys, metadata_values)
| project _time, uri, request_metadata
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20metadata_keys%20%3D%20dynamic\(%5B'status_code'%2C%20'request_method'%2C%20'city'%5D\)%20%7C%20extend%20metadata_values%20%3D%20pack_array\(status%2C%20method%2C%20%5B'geo.city'%5D\)%20%7C%20extend%20request_metadata%20%3D%20bag_zip\(metadata_keys%2C%20metadata_values\)%20%7C%20project%20_time%2C%20uri%2C%20request_metadata%20%7C%20take%205%22%7D)
**Output**
| \_time | uri | request\_metadata |
| ------------------- | --------- | ----------------------------------------------------------- |
| 2025-05-26 10:15:30 | /api/user | \{status\_code: 200, request\_method: GET, city: Seattle} |
| 2025-05-26 10:16:45 | /api/data | \{status\_code: 404, request\_method: POST, city: Portland} |
This query creates structured metadata objects by zipping together field names and their corresponding values, making the data easier to export or process downstream.
Use `bag_zip` to construct custom span attributes from separate attribute name and value arrays in OpenTelemetry traces.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend attr_keys = dynamic(['service', 'span_kind', 'status'])
| extend attr_values = pack_array(['service.name'], kind, status_code)
| extend span_attributes = bag_zip(attr_keys, attr_values)
| project _time, span_id, trace_id, span_attributes
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20attr_keys%20%3D%20dynamic\(%5B'service'%2C%20'span_kind'%2C%20'status'%5D\)%20%7C%20extend%20attr_values%20%3D%20pack_array\(%5B'service.name'%5D%2C%20kind%2C%20status_code\)%20%7C%20extend%20span_attributes%20%3D%20bag_zip\(attr_keys%2C%20attr_values\)%20%7C%20project%20_time%2C%20span_id%2C%20trace_id%2C%20span_attributes%20%7C%20take%205%22%7D)
**Output**
| \_time | span\_id | trace\_id | span\_attributes |
| ------------------- | ------------ | ------------ | ---------------------------------------------------- |
| 2025-05-26 11:20:15 | a1b2c3d4e5f6 | xyz123abc456 | \{service: frontend, span\_kind: server, status: OK} |
| 2025-05-26 11:21:30 | f6e5d4c3b2a1 | def789ghi012 | \{service: cart, span\_kind: client, status: OK} |
This query consolidates span-level metadata into structured attribute dictionaries, simplifying trace analysis and visualization.
Use `bag_zip` to create structured security context objects from arrays of security-related field names and values.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend security_keys = dynamic(['client_ip', 'country', 'http_status'])
| extend security_values = pack_array(id, ['geo.country'], status)
| extend security_context = bag_zip(security_keys, security_values)
| where status != '200'
| project _time, uri, method, security_context
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20security_keys%20%3D%20dynamic\(%5B'client_ip'%2C%20'country'%2C%20'http_status'%5D\)%20%7C%20extend%20security_values%20%3D%20pack_array\(id%2C%20%5B'geo.country'%5D%2C%20status\)%20%7C%20extend%20security_context%20%3D%20bag_zip\(security_keys%2C%20security_values\)%20%7C%20where%20status%20!%3D%20'200'%20%7C%20project%20_time%2C%20uri%2C%20method%2C%20security_context%20%7C%20take%205%22%7D)
**Output**
| \_time | uri | method | security\_context |
| ------------------- | ------------ | ------ | ------------------------------------------------------ |
| 2025-05-26 12:30:00 | /admin/panel | POST | \{client\_ip: user123, country: CN, http\_status: 403} |
| 2025-05-26 12:31:15 | /api/delete | DELETE | \{client\_ip: user456, country: RU, http\_status: 401} |
This query creates structured security context for failed requests, making it easier to analyze security incidents and audit access patterns.
## List of related functions
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Use `bag_pack` when you have key-value pairs as separate arguments rather than arrays. Use `bag_zip` when working with parallel arrays.
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Use `bag_keys` to extract all keys from an existing property bag. Use `bag_zip` to create a new property bag from separate key and value arrays.
* [pack\_dictionary](/apl/scalar-functions/array-functions/pack-dictionary): Similar to `bag_zip`, but `pack_dictionary` takes alternating key-value arguments. Use `bag_zip` for array-based inputs.
* [todynamic](/apl/scalar-functions/conversion-functions/todynamic): Use `todynamic` to parse JSON strings into dynamic objects. Use `bag_zip` to construct dynamic objects programmatically from arrays.
# isarray
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/isarray
This page explains how to use the isarray function in APL.
The `isarray` function in APL checks whether a specified value is an array. Use this function to validate input data, handle dynamic schemas, or filter for records where a field is explicitly an array. It’s particularly useful when working with data that contains fields with mixed data types or optional nested arrays.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, similar functionality is achieved by analyzing the data structure manually, as SPL doesn’t have a direct equivalent to `isarray`. APL simplifies this task by providing the `isarray` function to directly evaluate whether a value is an array.
```sql Splunk example theme={null}
| eval is_array=if(isnotnull(mvcount(field)), "true", "false")
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend is_array=isarray(field)
```
In ANSI SQL, there is no built-in function for directly checking if a value is an array. You might need to rely on JSON functions or structural parsing. APL provides the `isarray` function as a more straightforward solution.
```sql SQL example theme={null}
SELECT CASE
WHEN JSON_TYPE(field) = 'ARRAY' THEN TRUE
ELSE FALSE
END AS is_array
FROM dataset_name;
```
```kusto APL equivalent theme={null}
['dataset.name']
| extend is_array=isarray(field)
```
## Usage
### Syntax
```kusto theme={null}
isarray(value)
```
### Parameters
| Parameter | Description |
| --------- | ------------------------------------ |
| `value` | The value to check if it’s an array. |
### Returns
A boolean value:
* `true` if the specified value is an array.
* `false` otherwise.
## Use case example
Filter for records where the `events` field contains an array.
**Query**
```kusto theme={null}
['otel-demo-traces']
| take 50
| summarize events_array = make_list(events)
| extend is_array = isarray(events_array)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20take%2050%20%7C%20summarize%20events_array%20%3D%20make_list\(events\)%20%7C%20extend%20is_array%20%3D%20isarray\(events_array\)%22%7D)
**Output**
| is\_array |
| --------- |
| true |
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
# len
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/len
This page explains how to use the len function in APL.
Use the `len` function in APL (Axiom Processing Language) to determine the length of a string or the number of elements in an array. This function is useful when you want to filter, sort, or analyze data based on the size of a value—whether that’s the number of characters in a request URL or the number of cities associated with a user.
Use `len` when you need to:
* Measure string lengths (for example, long request URIs).
* Count elements in dynamic arrays (such as tags or multi-value fields).
* Create conditional expressions based on the length of values.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you often use the `len` function within `eval` or `where` expressions to determine string length or array size. In APL, `len` works similarly, but is used as a standalone scalar function.
```sql Splunk example theme={null}
... | eval uri_length=len(uri)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend uri_length = len(uri)
```
In ANSI SQL, you use `LENGTH()` for strings and `CARDINALITY()` for arrays. In APL, `len` handles both cases—string and array—depending on the input type.
```sql SQL example theme={null}
SELECT LENGTH(uri) AS uri_length FROM http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend uri_length = len(uri)
```
## Usage
### Syntax
```kusto theme={null}
len(value)
```
### Parameters
| Name | Type | Description |
| ----- | --------------- | ------------------------------------------------- |
| value | string or array | The input to measure—either a string or an array. |
### Returns
* If `value` is a string, returns the number of characters.
* If `value` is an array, returns the number of elements.
* Returns `null` if the input is `null`.
## Use case examples
Use `len` to find requests with long URIs, which might indicate poorly designed endpoints or potential abuse.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend uri_length = len(uri)
| where uri_length > 100
| project _time, id, uri, uri_length
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20uri_length%20%3D%20len\(uri\)%20%7C%20where%20uri_length%20%3E%20100%20%7C%20project%20_time%2C%20id%2C%20uri%2C%20uri_length%22%7D)
**Output**
| \_time | id | uri | uri\_length |
| -------------------- | ------- | --------------------------------- | ----------- |
| 2025-06-18T12:34:00Z | user123 | /api/products/search?query=... | 132 |
| 2025-06-18T12:35:00Z | user456 | /download/file/very/long/path/... | 141 |
The query filters logs for URIs longer than 100 characters and displays their lengths.
Use `len` to identify traces with IDs of unexpected length, which might indicate instrumentation issues or data inconsistencies.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_id_length = len(trace_id)
| summarize count() by trace_id_length
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20durations%3Dmake_list\(req_duration_ms\)%20by%20id%20%7C%20extend%20sorted%3Dsort_array\(durations%2C%20'desc'\)%20%7C%20extend%20top_3%3Darray_extract\(sorted%2C%200%2C%203\)%22%7D)
**Output**
| trace\_id\_length | count |
| ----------------- | ----- |
| 32 | 4987 |
| 16 | 12 |
The query summarizes trace IDs by their lengths to find unexpected values.
Use `len` to analyze request methods and flag unusually short ones (e.g., malformed logs or attack vectors).
**Query**
```kusto theme={null}
['sample-http-logs']
| extend method_length = len(method)
| where method_length < 3
| project _time, id, method, method_length
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20method_length%20%3D%20len\(method\)%20%7C%20where%20method_length%20%3C%203%20%7C%20project%20_time%2C%20id%2C%20method%2C%20method_length%22%7D)
**Output**
| \_time | id | method | method\_length |
| -------------------- | ------- | ------ | -------------- |
| 2025-06-18T13:10:00Z | user789 | P | 1 |
| 2025-06-18T13:12:00Z | user222 | G | 1 |
The query finds suspicious or malformed request methods that are unusually short.
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Use this when working specifically with arrays.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Returns a subarray like `array_extract`, but supports negative indexing.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Joins arrays end-to-end. Use before or after slicing arrays with `array_extract`.
# pack_array
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/pack-array
This page explains how to use the pack_array function in APL.
The `pack_array` function in APL creates an array from individual values or expressions. You can use this function to group related data into a single field, which can simplify handling and querying of data collections. It’s especially useful when working with nested data structures or aggregating data into arrays for further processing.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use functions like `mvappend` to create multi-value fields. In APL, the `pack_array` function serves a similar purpose by combining values into an array.
```sql Splunk example theme={null}
| eval array_field = mvappend(value1, value2, value3)
```
```kusto APL equivalent theme={null}
| extend array_field = pack_array(value1, value2, value3)
```
In ANSI SQL, arrays are often constructed using functions like `ARRAY`. The `pack_array` function in APL performs a similar operation, creating an array from specified values.
```sql SQL example theme={null}
SELECT ARRAY[value1, value2, value3] AS array_field;
```
```kusto APL equivalent theme={null}
| extend array_field = pack_array(value1, value2, value3)
```
## Usage
### Syntax
```kusto theme={null}
pack_array(value1, value2, ..., valueN)
```
### Parameters
| Parameter | Description |
| --------- | ------------------------------------------ |
| `value1` | The first value to include in the array. |
| `value2` | The second value to include in the array. |
| `...` | Additional values to include in the array. |
| `valueN` | The last value to include in the array. |
The wildcard `*` in `pack_array(*)` is useful to pack all values from the current row into an array, but it increases query complexity and decreases stability and performance.
`pack_array(*)` packs only the values, not the field names. For key-value pairs, use `bag_pack(*)` instead.
### Returns
An array containing the specified values in the order they are provided.
## Use case example
Use `pack_array` to consolidate span data into an array for a trace summary.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend span_summary = pack_array(['service.name'], kind, duration)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20span_summary%20%3D%20pack_array\(%5B'service.name'%5D%2C%20kind%2C%20duration\)%22%7D)
**Output**
| service.name | kind | duration | span\_summary |
| ------------ | ------ | -------- | -------------------------------- |
| frontend | server | 123ms | \["frontend", "server", "123ms"] |
This query creates a concise representation of span details.
## List of related functions
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subset of elements from an array.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
# pack_dictionary
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/pack-dictionary
This page explains how to use the pack_dictionary function in APL.
Use the `pack_dictionary` function in APL to construct a dynamic property bag (dictionary) from a list of keys and values. The resulting dictionary maps each specified key to its corresponding value and allows you to store key-value pairs in a single column for downstream operations like serialization, custom grouping, or structured export.
`pack_dictionary` is especially useful when you want to:
* Create flexible data structures for export or transformation.
* Group dynamic sets of key-value metrics or attributes into a single column.
* Combine multiple scalar fields into a single dictionary for post-processing or output.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
While SPL doesn’t have a direct equivalent of `pack_dictionary`, you can simulate similar behavior using the `eval` command and `mvzip` or `mvmap` to construct composite objects. In APL, `pack_dictionary` is a simpler and more declarative way to produce key-value structures inline.
```sql Splunk example theme={null}
| eval dict=mvmap("key1", value1, "key2", value2)
```
```kusto APL equivalent theme={null}
| extend dict = pack_dictionary('key1', value1, 'key2', value2)
```
ANSI SQL lacks built-in support for dynamic dictionaries. You typically achieve similar functionality by manually assembling JSON strings or using vendor-specific extensions (like PostgreSQL’s `jsonb_build_object`). In contrast, APL provides a native and type-safe way to construct dictionaries using `pack_dictionary`.
```sql SQL example theme={null}
SELECT '{"key1":' || value1 || ',"key2":' || value2 || '}' AS dict FROM my_table;
```
```kusto APL equivalent theme={null}
| extend dict = pack_dictionary('key1', value1, 'key2', value2)
```
## Usage
### Syntax
```kusto theme={null}
pack_dictionary(key1, value1, key2, value2, ...)
```
### Parameters
| Name | Type | Description |
| ------ | -------- | ------------------------------------------------------- |
| keyN | `string` | A constant string that represents a dictionary key. |
| valueN | `scalar` | A scalar value to associate with the corresponding key. |
* The number of arguments must be even.
* Keys must be constant strings.
* Values can be any scalar type.
### Returns
A dynamic object that represents a dictionary where each key maps to its associated value.
## Use case examples
Use `pack_dictionary` to store request metadata in a compact format for structured inspection or export.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend request_info = pack_dictionary(
'method', method,
'uri', uri,
'status', status,
'duration', req_duration_ms
)
| project _time, id, request_info
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20request_info%20%3D%20pack_dictionary\(%20'method'%2C%20method%2C%20'uri'%2C%20uri%2C%20'status'%2C%20status%2C%20'duration'%2C%20req_duration_ms%20\)%20%7C%20project%20_time%2C%20id%2C%20request_info%22%7D)
**Output**
| \_time | id | request\_info |
| -------------------- | ------ | ---------------------------------------------------------------------- |
| 2025-06-18T14:35:00Z | user42 | `{ "method": "GET", "uri": "/home", "status": "200", "duration": 82 }` |
This example creates a single `request_info` column that contains key HTTP request data as a dictionary, simplifying downstream analysis or visualization.
Use `pack_dictionary` to consolidate trace metadata into a structured format for export or debugging.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_metadata = pack_dictionary(
'trace_id', trace_id,
'span_id', span_id,
'service', ['service.name'],
'kind', kind,
'status_code', status_code
)
| project _time, duration, trace_metadata
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20trace_metadata%20%3D%20pack_dictionary\(%20'trace_id'%2C%20trace_id%2C%20'span_id'%2C%20span_id%2C%20'service'%2C%20%5B'service.name'%5D%2C%20'kind'%2C%20kind%2C%20'status_code'%2C%20status_code%20\)%20%7C%20project%20_time%2C%20duration%2C%20trace_metadata%22%7D)
**Output**
| \_time | duration | trace\_metadata |
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| 2025-06-18T14:40:00Z | 00:00:01 | `{ "trace_id": "abc123", "span_id": "def456", "service": "checkoutservice", "kind": "server", "status_code": "OK" }` |
This query generates a `trace_metadata` column that organizes important trace identifiers and status into a single dynamic field.
Use `pack_dictionary` to package request metadata along with geographic information for audit logging or incident forensics.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend geo_info = pack_dictionary(
'city', ['geo.city'],
'country', ['geo.country']
)
| extend request_info = pack_dictionary(
'method', method,
'uri', uri,
'status', status,
'geo', geo_info
)
| project _time, id, request_info
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20geo_info%20%3D%20pack_dictionary\(%20'city'%2C%20%5B'geo.city'%5D%2C%20'country'%2C%20%5B'geo.country'%5D%20\)%20%7C%20extend%20request_info%20%3D%20pack_dictionary\(%20'method'%2C%20method%2C%20'uri'%2C%20uri%2C%20'status'%2C%20status%2C%20'geo'%2C%20geo_info%20\)%20%7C%20project%20_time%2C%20id%2C%20request_info%22%7D)
**Output**
| \_time | id | request\_info |
| -------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| 2025-06-18T14:20:00Z | user88 | `{ "method": "POST", "uri": "/login", "status": "403", "geo": { "city": "Berlin", "country": "DE" } }` |
This example nests geographic context inside the main dictionary to create a structured log suitable for security investigations.
## List of related functions
* [pack\_array](/apl/scalar-functions/array-functions/pack-array): Use this to combine scalar values into an array. Use `pack_array` when you don’t need named keys and want positional data instead.
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Returns the list of keys in a dynamic dictionary. Use this to inspect or filter contents created by `pack_dictionary`.
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Expands a dictionary into multiple columns. Use it to revert the packing performed by `pack_dictionary`.
# strcat_array
Source: https://axiom.co/docs/apl/scalar-functions/array-functions/strcat-array
This page explains how to use the strcat_array function in APL.
The `strcat_array` function in Axiom Processing Language (APL) allows you to concatenate the elements of an array into a single string, with an optional delimiter separating each element. This function is useful when you need to transform a set of values into a readable or exportable format, such as combining multiple log entries, tracing IDs, or security alerts into a single output for further analysis or reporting.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, concatenation typically involves transforming fields into a string using the `eval` command with the `+` operator or `mvjoin()` for arrays. In APL, `strcat_array` simplifies array concatenation by natively supporting array input with a delimiter.
```sql Splunk example theme={null}
| eval concatenated=mvjoin(array_field, ", ")
```
```kusto APL equivalent theme={null}
dataset
| extend concatenated = strcat_array(array_field, ', ')
```
In ANSI SQL, concatenation involves functions like `STRING_AGG()` or manual string building using `CONCAT()`. APL’s `strcat_array` is similar to `STRING_AGG()`, but focuses on array input directly with a customizable delimiter.
```sql SQL example theme={null}
SELECT STRING_AGG(column_name, ', ') AS concatenated FROM table;
```
```kusto APL equivalent theme={null}
dataset
| summarize concatenated = strcat_array(column_name, ', ')
```
## Usage
### Syntax
```kusto theme={null}
strcat_array(array, delimiter)
```
### Parameters
| Parameter | Type | Description |
| ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `array` | dynamic | The array of values to concatenate. |
| `delimiter` | string | The string used to separate each element in the concatenated result. Optional. Defaults to an empty string if not specified. |
### Returns
A single concatenated string with the array’s elements separated by the specified delimiter.
## Use case example
You can use `strcat_array` to combine HTTP methods and URLs for a quick summary of unique request paths.
**Query**
```kusto theme={null}
['sample-http-logs']
| take 50
| extend combined_requests = strcat_delim(' ', method, uri)
| summarize requests_list = make_list(combined_requests)
| extend paths = strcat_array(requests_list, ', ')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20extend%20combined_requests%20%3D%20strcat_delim\('%20'%2C%20method%2C%20uri\)%20%7C%20summarize%20requests_list%20%3D%20make_list\(combined_requests\)%20%7C%20extend%20paths%20%3D%20strcat_array\(requests_list%2C%20'%2C%20'\)%22%7D)
**Output**
| paths |
| ------------------------------------ |
| GET /index, POST /submit, GET /about |
This query summarizes unique HTTP method and URL combinations into a single, readable string.
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the index of an element in an array.
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Combines multiple arrays.
# Conditional functions
Source: https://axiom.co/docs/apl/scalar-functions/conditional-function
Learn how to use and combine different conditional functions in APL
## Conditional functions
| **Function Name** | **Description** |
| ----------------- | ----------------------------------------------------------------------------------------------------------- |
| [case](#case) | Evaluates a list of conditions and returns the first result expression whose condition is satisfied. |
| [iff](#iff) | Evaluates the first argument (the predicate), and returns the value of either the second or third arguments |
## case
Evaluates a list of conditions and returns the first result whose condition is satisfied.
### Arguments
* condition: An expression that evaluates to a Boolean.
* result: An expression that Axiom evaluates and returns the value if its condition is the first that evaluates to true.
* nothingMatchedResult: An expression that Axiom evaluates and returns the value if none of the conditional expressions evaluates to true.
### Returns
Axiom returns the value of the first result whose condition evaluates to true. If none of the conditions is satisfied, Axiom returns the value of `nothingMatchedResult`.
### Example
```kusto theme={null}
case(condition1, result1, condition2, result2, condition3, result3, ..., nothingMatchedResult)
```
```kusto theme={null}
['sample-http-logs'] |
extend status_human_readable = case(
status_int == 200,
'OK',
status_int == 201,
'Created',
status_int == 301,
'Moved Permanently',
status_int == 500,
'Internal Server Error',
'Other'
)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20extend%20status_code%20%3D%20case\(status_int%20%3D%3D%20200%2C%20'OK'%2C%20status_int%20%3D%3D%20201%2C%20'Created'%2C%20status_int%20%3D%3D%20301%2C%20'Moved%20Permanently'%2C%20status_int%20%3D%3D%20500%2C%20'Internal%20Server%20Error'%2C%20'Other'\)%22%7D)
## iff
Evaluates the first argument (the predicate), and returns the value of either the second or third arguments. The second and third arguments must be of the same type.
The `iif` function is equivalent to the `iff` function.
### Arguments
* predicate: An expression that evaluates to a boolean value.
* ifTrue: An expression that gets evaluated and its value returned from the function if predicate evaluates to `true`.
* ifFalse: An expression that gets evaluated and its value returned from the function if predicate evaluates to `false`.
### Returns
This function returns the value of ifTrue if predicate evaluates to true, or the value of ifFalse otherwise.
### Examples
```kusto theme={null}
iff(predicate, ifTrue, ifFalse)
```
```kusto theme={null}
['sample-http-logs']
| project Status = iff(req_duration_ms == 1, "numeric", "Inactive")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20project%20Status%20%3D%20iff%28req_duration_ms%20%3D%3D%201%2C%20%5C%22numeric%5C%22%2C%20%5C%22Inactive%5C%22%29%22%7D)
To return a null value from `iff`, use `dynamic(null)`.
```kusto theme={null}
iff(condition, dynamic(null), value)
```
# Conversion functions
Source: https://axiom.co/docs/apl/scalar-functions/conversion-functions
Learn how to use and combine different conversion functions in APL
## Conversion functions
| **Function Name** | **Description** |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [dynamic\_to\_json](#dynamic-to-json) | Converts a scalar value of type dynamic to a canonical string representation. |
| [ensure\_field](#ensure-field) | Ensures the existence of a field and returns its value or a typed nil if it doesn’t exist. |
| [isbool](#isbool) | Returns a value of true or false if the expression value is passed. |
| [tobool](#tobool) | Converts input to boolean (signed 8-bit) representation. |
| [todatetime](#todatetime) | Converts input to datetime scalar. |
| [todouble, toreal](#todouble%2C-toreal) | Converts the input to a value of type `real`. `todouble` and `toreal` are synonyms. |
| [tohex](#tohex) | Converts input to a hexadecimal string. |
| [toint](#toint) | Converts the input to an integer value (signed 64-bit) number representation. |
| [tolong](#tolong) | Converts input to long (signed 64-bit) number representation. |
| [tostring](#tostring) | Converts input to a string representation. |
| [totimespan](#totimespan) | Converts input to timespan scalar. |
| [toarray](/apl/scalar-functions/conversion-functions/toarray) | Converts input to array. |
| [todynamic](/apl/scalar-functions/conversion-functions/todynamic) | Converts input to dynamic. |
## ensure\_field()
Ensures the existence of a field and returns its value or a typed nil if it doesn’t exist.
### Arguments
| **name** | **type** | **description** |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------ |
| field\_name | string | The name of the field to ensure exists. |
| field\_type | type | The type of the field. See [scalar data types](/apl/data-types/scalar-data-types) for supported types. |
### Returns
This function returns the value of the specified field if it exists, otherwise it returns a typed nil.
### Examples
```kusto theme={null}
ensure_field(field_name, field_type)
```
### Handle missing fields
In this example, the value of `show_field` is nil because the `myfield` field doesn’t exist.
```kusto theme={null}
['sample-http-logs']
| extend show_field = ensure_field("myfield", typeof(string))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20show_field%20%3D%20ensure_field%28%27myfield%27%2C%20typeof%28string%29%29%22%7D)
### Access existing fields
In this example, the value of `newstatus` is the value of `status` because the `status` field exists.
```kusto theme={null}
['sample-http-logs']
| extend newstatus = ensure_field("status", typeof(string))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20newstatus%20%3D%20ensure_field%28%27status%27%2C%20typeof%28string%29%29%22%7D)
### Future-proof queries
In this example, the query is prepared for a field named `upcoming_field` that’s expected to be added to the data soon. By using `ensure_field()`, logic can be written around this future field, and the query will work when the field becomes available.
```kusto theme={null}
['sample-http-logs']
| extend new_field = ensure_field("upcoming_field", typeof(int))
| where new_field > 100
```
## tobool()
Converts input to boolean (signed 8-bit) representation.
### Arguments
* Expr: Expression that will be converted to boolean.
### Returns
* If conversion is successful, result will be a boolean. If conversion isn’t successful, result will be `false`
### Examples
```kusto theme={null}
tobool(Expr)
toboolean(Expr) (alias)
```
```kusto theme={null}
tobool("true") == true
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20tobool%28%5C%22true%5C%22%29%20%3D%3D%20true%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": true
}
```
## todatetime()
Converts input to datetime scalar.
### Arguments
* Expr: Expression that will be converted to datetime.
### Returns
If the conversion is successful, the result will be a datetime value. Else, the result will be `false.`
### Examples
```kusto theme={null}
todatetime(Expr)
```
```kusto theme={null}
todatetime("2022-11-13")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20todatetime%28%5C%222022-11-13%5C%22%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"boo": "2022-11-13T00:00:00Z"
}
```
## todouble, toreal
Converts the input to a value of type real. **(todouble() is an alternative word to toreal())**
### Arguments
* Expr: An expression whose value will be converted to a value of type `real.`
### Returns
If conversion is successful, the result is a value of type real. If conversion isn’t successful, the result returns false.
### Examples
```kusto theme={null}
toreal(Expr)todouble(Expr)
```
```kusto theme={null}
toreal("1567") == 1567
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20toreal%28%5C%221567%5C%22%29%20%3D%3D%201567%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": true
}
```
## tostring()
Converts input to a string representation.
### Arguments
* `Expr:` Expression that will be converted to string.
### Returns
If the Expression value is non-null, the result will be a string representation of the Expression. If the Expression value is null, the result will be an empty string.
### Examples
```kusto theme={null}
tostring(Expr)
```
```kusto theme={null}
tostring(axiom) == "axiom"
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20tostring%28%5C%22axiom%5C%22%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": "axiom"
}
```
## totimespan
Converts input to timespan scalar.
### Arguments
* `Expr:` Expression that will be converted to timespan.
### Returns
If conversion is successful, result will be a timespan value. Else, result will be false.
### Examples
```kusto theme={null}
totimespan(Expr)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20totimespan%282022-11-13%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"conversion_function": "1.998µs"
}
```
## tohex()
Converts input to a hexadecimal string.
### Arguments
* Expr: int or long value that will be converted to a hex string. Other types aren’t supported.
### Returns
If conversion is successful, result will be a string value. If conversion isn’t successful, result will be false.
### Examples
```kusto theme={null}
tohex(value)
```
```kusto theme={null}
tohex(-546) == 'fffffffffffffdde'
```
```kusto theme={null}
tohex(546) == '222'
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20tohex%28-546%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": "fffffffffffffdde"
}
```
## tolong()
Converts input to long (signed 64-bit) number representation.
### Arguments
* Expr: Expression that will be converted to long.
### Returns
If conversion is successful, result will be a long number. If conversion isn’t successful, result will be false.
### Examples
```kusto theme={null}
tolong(Expr)
```
```kusto theme={null}
tolong("241") == 241
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20tolong%28%5C%22241%5C%22%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": 241
}
```
## dynamic\_to\_json()
Converts a scalar value of type `dynamic` to a canonical `string` representation.
### Arguments
* dynamic input (EXpr): The function accepts one argument.
### Returns
Returns a canonical representation of the input as a value of type `string`, according to the following rules:
* If the input is a scalar value of type other than `dynamic`, the output is the app of `tostring()` to that value.
* If the input in an array of values, the output is composed of the characters **\[, ,, and ]** interspersed with the canonical representation described here of each array element.
* If the input is a property bag, the output is composed of the characters **\{, ,, and }** interspersed with the colon (:)-delimited name/value pairs of the properties. The pairs are sorted by the names, and the values are in the canonical representation described here of each array element.
### Examples
```kusto theme={null}
dynamic_to_json(dynamic)
```
```kusto theme={null}
['sample-http-logs']
| project conversion_function = dynamic_to_json(dynamic([1,2,3]))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20dynamic_to_json%28dynamic%28%5B1%2C2%2C3%5D%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": "[1,2,3]"
}
```
## isbool()
Returns a value of true or false if the expression value is passed.
### Arguments
* Expr: The function accepts one argument. The variable of expression to be evaluated.
### Returns
Returns `true` if expression value is a bool, `false` otherwise.
### Examples
```kusto theme={null}
isbool(expression)
```
```kusto theme={null}
isbool("pow") == false
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20conversion_function%20%3D%20isbool%28%5C%22pow%5C%22%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"conversion_function": false
}
```
## toint()
Converts the input to an integer value (signed 64-bit) number representation.
### Arguments
* Value: The value to convert to an [integer](/apl/data-types/scalar-data-types#the-int-data-type).
### Returns
If the conversion is successful, the result will be an integer. Otherwise, the result will be `null`.
### Examples
```kusto theme={null}
toint(value)
```
```kusto theme={null}
| project toint("456") == 456
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20toint%28%5C%22456%5C%22%29%20%3D%3D%20456%22%7D)
# toarray
Source: https://axiom.co/docs/apl/scalar-functions/conversion-functions/toarray
This page explains how to use the toarray function in APL.
Use the `toarray` function in APL to convert a dynamic-typed input—such as a bag, property bag, or JSON array—into a regular array. This is helpful when you want to process the elements individually with array functions like `array_length` or `array_index_of`.
You typically use `toarray` when working with semi-structured data, especially after parsing JSON from log fields or external sources. It lets you access and manipulate nested collections using standard array operations.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, multivalue fields are native, and many SPL commands like `mvexpand`, `mvindex`, and `mvcount` operate directly on them. In APL, dynamic fields can also contain multivalue data, but you need to explicitly convert them to arrays using `toarray` before applying array functions.
```sql Splunk example theme={null}
... | eval methods=split("GET,POST,PUT", ",") | mvcount(methods)
```
```kusto APL equivalent theme={null}
print methods = dynamic(["GET", "POST", "PUT"])
| extend method_count = array_length(toarray(methods))
```
ANSI SQL doesn’t support arrays natively. You typically store lists as JSON and use JSON functions to manipulate them. In APL, you can parse JSON into dynamic values and use `toarray` to convert those into arrays for further processing.
```sql SQL example theme={null}
SELECT JSON_ARRAY_LENGTH('["GET","POST","PUT"]')
```
```kusto APL equivalent theme={null}
print methods = dynamic(["GET", "POST", "PUT"])
| extend method_count = array_length(toarray(methods))
```
## Usage
### Syntax
```kusto theme={null}
toarray(value)
```
### Parameters
| Name | Type | Description |
| ----- | ------- | ---------------------------------------- |
| value | dynamic | A JSON array, property bag, or bag value |
### Returns
An array containing the elements of the dynamic input. If the input is already an array, the result is identical. If the input is a property bag, it returns an array of values. If the input isn’t coercible to an array, the result is an empty array.
## Example
You want to convert a string to an array because you want to pass the result to a function that accepts arrays, such as `array_concat`.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_list = toarray('123')
| extend json_list = parse_json('["frontend", "cartservice", "checkoutservice"]')
| extend combined_list = array_concat(service_list, json_list)
| project _time, combined_list
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20service_list%20%3D%20toarray\('123'\)%20%7C%20extend%20json_list%20%3D%20parse_json\('%5B%5C"frontend%5C"%2C%20%5C"cartservice%5C"%2C%20%5C"checkoutservice%5C"%5D'\)%20%7C%20extend%20combined_list%20%3D%20array_concat\(service_list%2C%20json_list\)%20%7C%20project%20_time%2C%20combined_list%22%7D)
**Output**
| \_time | combined\_list |
| ---------------- | ------------------------------------------------------ |
| Jun 24, 09:28:10 | \["123", "frontend", "cartservice", "checkoutservice"] |
| Jun 24, 09:28:10 | \["123", "frontend", "cartservice", "checkoutservice"] |
| Jun 24, 09:28:10 | \["123", "frontend", "cartservice", "checkoutservice"] |
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Useful before applying `array_extract`.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the position of an element in an array, which can help set the `startIndex` for `array_extract`.
* [pack\_array](/apl/scalar-functions/array-functions/pack-array): Use this to combine scalar values into an array. Use `pack_array` when you don’t need named keys and want positional data instead.
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Returns the list of keys in a dynamic dictionary. Use this to inspect or filter contents created by `pack_dictionary`.
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Expands a dictionary into multiple columns. Use it to revert the packing performed by `pack_dictionary`.
# todynamic
Source: https://axiom.co/docs/apl/scalar-functions/conversion-functions/todynamic
This page explains how to use the todynamic function in APL.
Use the `todynamic` function to parse a string as a dynamic value, such as a JSON object or array. This function is especially useful when your dataset contains structured data in string format and you want to access nested elements, iterate over arrays, or use dynamic-aware functions.
You often find `todynamic` helpful when working with logs, telemetry, or security events that encode rich metadata or nested attributes in stringified JSON. By converting these strings into dynamic values, you can query, filter, and transform the nested fields using APL’s built-in support for dynamic types.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk automatically interprets structured JSON data and allows you to use dot notation directly on fields, without explicit conversion. In APL, you need to explicitly cast a JSON string into a dynamic value using `todynamic`.
```sql Splunk example theme={null}
... | eval json_field = json_extract(raw_field, "$.key")
```
```kusto APL equivalent theme={null}
... | extend json_field = todynamic(raw_field).key
```
In standard SQL, you typically use `JSON_VALUE`, `JSON_QUERY`, or `CAST(... AS JSON)` to access structured content in string format. In APL, use `todynamic` to convert a string to a dynamic value that supports dot notation and further manipulation.
```sql SQL example theme={null}
SELECT JSON_VALUE(raw_column, '$.key') AS value FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend value = todynamic(raw_column).key
```
## Usage
### Syntax
```kusto theme={null}
todynamic(value)
```
### Parameters
| Name | Type | Description |
| ----- | ------ | ----------------------------------------------------- |
| value | string | A string representing a JSON-encoded object or array. |
### Returns
A dynamic value. If the input isn’t a valid JSON string, the function returns `null`.
## Example
You want to find events that match certain criteria such as URI and status code. The criteria are stored in a stringified dictionary.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend criteria = '{"uri": "/api/v1/customer/services", "status": "200"}'
| extend metadata = todynamic(criteria)
| where uri == metadata.uri and status == metadata.status
| project _time, id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20criteria%20%3D%20'%7B%5C"uri%5C"%3A%20%5C"%2Fapi%2Fv1%2Fcustomer%2Fservices%5C"%2C%20%5C"status%5C"%3A%20%5C"200%5C"%7D'%20%7C%20extend%20metadata%20%3D%20todynamic\(criteria\)%20%7C%20where%20uri%20%3D%3D%20metadata.uri%20and%20status%20%3D%3D%20metadata.status%20%7C%20project%20_time%2C%20id%22%7D)
**Output**
| \_time | id |
| ---------------- | ------------------------------------ |
| Jun 24, 09:28:10 | 2f2e5c40-1094-4237-a124-ec50fab7e726 |
| Jun 24, 09:28:10 | 0f9724cb-fa9a-4a2f-bdf6-5c32b2f22efd |
| Jun 24, 09:28:10 | a516c4e9-2ed9-4fb9-a191-94e2844e9b2a |
## List of related functions
* [pack\_array](/apl/scalar-functions/array-functions/pack-array): Use this to combine scalar values into an array. Use `pack_array` when you don’t need named keys and want positional data instead.
* [bag\_keys](/apl/scalar-functions/array-functions/bag-keys): Returns the list of keys in a dynamic dictionary. Use this to inspect or filter contents created by `pack_dictionary`.
* [bag\_pack](/apl/scalar-functions/array-functions/bag-pack): Expands a dictionary into multiple columns. Use it to revert the packing performed by `pack_dictionary`.
# Datetime functions
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions
Learn how to use and combine different timespan functions in APL
The table summarizes the datetime functions available in APL.
| Name | Description |
| --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [ago](#ago) | Subtracts the given timespan from the current UTC clock time. |
| [datetime\_add](#datetime-add) | Calculates a new datetime from a specified datepart multiplied by a specified amount, added to a specified datetime. |
| [datetime\_part](#datetime-part) | Extracts the requested date part as an integer value. |
| [datetime\_diff](#datetime-diff) | Calculates calendarian difference between two datetime values. |
| [dayofmonth](#dayofmonth) | Returns the integer number representing the day number of the given month |
| [dayofweek](#dayofweek) | Returns the integer number of days since the preceding Sunday, as a timespan. |
| [dayofyear](#dayofyear) | Returns the integer number represents the day number of the given year. |
| [endofyear](#endofyear) | Returns the end of the year containing the date |
| [getmonth](#getmonth) | Get the month number (1-12) from a datetime. |
| [getyear](#getyear) | Returns the year part of the `datetime` argument. |
| [hourofday](#hourofday) | Returns the integer number representing the hour number of the given date. |
| [endofday](#endofday) | Returns the end of the day containing the date. |
| [now](#now) | Returns the current UTC clock time, optionally offset by a given timespan. |
| [endofmonth](#endofmonth) | Returns the end of the month containing the date. |
| [endofweek](#endofweek) | Returns the end of the week containing the date. |
| [monthofyear](#monthofyear) | Returns the integer number represents the month number of the given year. |
| [startofday](#startofday) | Returns the start of the day containing the date. |
| [startofmonth](#startofmonth) | Returns the start of the month containing the date. |
| [startofweek](#startofweek) | Returns the start of the week containing the date. |
| [startofyear](#startofyear) | Returns the start of the year containing the date. |
| [unixtime\_microseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime) | Converts a Unix timestamp expressed in whole microseconds to an APL `datetime` value. |
| [unixtime\_milliseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime) | Converts a Unix timestamp expressed in whole milliseconds to an APL `datetime` value. |
| [unixtime\_nanoseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime) | Converts a Unix timestamp expressed in whole nanoseconds to an APL `datetime` value. |
| [unixtime\_seconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime) | Converts a Unix timestamp expressed in whole seconds to an APL `datetime` value. |
| [week\_of\_year](/apl/scalar-functions/datetime-functions/week-of-year) | Returns the ISO 8601 week number from a datetime expression. |
Axiom supports the ISO 8601 format which is the standard format for representing dates and times in the Gregorian calendar. For more information, see [Supported formats](/apl/data-types/scalar-data-types#supported-formats).
## ago
Subtracts the given timespan from the current UTC clock time.
### Arguments
* Interval to subtract from the current UTC clock time
### Returns
now() - a\_timespan
### Example
```kusto theme={null}
ago(6h)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20date_time_functions%20%3D%20ago%286h%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"date_time_functions": "2023-09-11T20:12:39Z"
}
```
```kusto theme={null}
ago(3d)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20date_time_functions%20%3D%20ago%283d%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"date_time_functions": "2023-09-09T02:13:29Z"
}
```
## datetime\_add
Calculates a new datetime from a specified datepart multiplied by a specified amount, added to a specified datetime.
### Arguments
* period: string.
* amount: integer.
* datetime: datetime value.
### Returns
A date after a certain time/date interval has been added.
### Example
```kusto theme={null}
datetime_add(period,amount,datetime)
```
```kusto theme={null}
['sample-http-logs']
| project new_datetime = datetime_add( "month", 1, datetime(2016-10-06))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20new_datetime%20%3D%20datetime_add%28%20%5C%22month%5C%22%2C%201%2C%20datetime%282016-10-06%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"new_datetime": "2016-11-06T00:00:00Z"
}
```
## datetime\_part
Extracts the requested date part as an integer value.
### Arguments
* date: datetime
* part: string
### Returns
An integer representing the extracted part.
### Examples
```kusto theme={null}
datetime_part(part,datetime)
```
```kusto theme={null}
['sample-http-logs']
| project new_datetime = datetime_part("Day", datetime(2016-06-26T08:20:03.123456Z))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20new_datetime%20%3D%20datetime_part%28%5C%22Day%5C%22%2C%20datetime%282016-06-26T08%3A20%3A03.123456Z%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"new_datetime": 26
}
```
## datetime\_diff
Calculates calendarian difference between two datetime values.
### Arguments
* period: string.
* datetime\_1: datetime value.
* datetime\_2: datetime value.
### Returns
An integer, which represents amount of periods in the result of subtraction (datetime\_1 - datetime\_2).
### Example
```kusto theme={null}
datetime_diff(period,datetime_1,datetime_2)
```
```kusto theme={null}
['sample-http-logs']
| project new_datetime = datetime_diff("week", datetime(2019-06-26T08:20:03.123456Z), datetime(2014-06-26T08:19:03.123456Z))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20new_datetime%20%3D%20datetime_diff%28%5C%22week%5C%22%2C%20datetime%28%5C%222019-06-26T08%3A20%3A03.123456Z%5C%22%29%2C%20datetime%28%5C%222014-06-26T08%3A19%3A03.123456Z%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"new_datetime": 260
}
```
```kusto theme={null}
['sample-http-logs']
| project new_datetime = datetime_diff("week", datetime(2015-11-08), datetime(2014-11-08))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20new_datetime%20%3D%20datetime_diff%28%5C%22week%5C%22%2C%20datetime%28%5C%222014-11-08%5C%22%29%2C%20datetime%28%5C%222014-11-08%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"new_datetime": 52
}
```
## dayofmonth
Returns the integer number representing the day number of the given month
### Arguments
* `a_date`: A `datetime`
### Returns
day number of the given month.
### Example
```kusto theme={null}
dayofmonth(a_date)
```
```kusto theme={null}
['sample-http-logs']
| project day_of_the_month = dayofmonth(datetime(2017-11-30))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20day_of_the_month%20%3D%20dayofmonth%28datetime%28%5C%222017-11-30%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"day_of_the_month": 30
}
```
## dayofweek
Returns the integer number of days since the preceding Sunday, as a timespan.
### Arguments
* a\_date: A datetime.
### Returns
The `timespan` since midnight at the beginning of the preceding Sunday, rounded down to an integer number of days.
### Example
```kusto theme={null}
dayofweek(a_date)
```
```kusto theme={null}
['sample-http-logs']
| project day_of_the_week = dayofweek(datetime(2019-05-18))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20day_of_the_week%20%3D%20dayofweek%28datetime%28%5C%222019-05-18%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"day_of_the_week": 6
}
```
## dayofyear
Returns the integer number represents the day number of the given year.
### Arguments
* `a_date`: A `datetime.`
### Returns
`day number` of the given year.
### Example
```kusto theme={null}
dayofyear(a_date)
```
```kusto theme={null}
['sample-http-logs']
| project day_of_the_year = dayofyear(datetime(2020-07-20))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20day_of_the_year%20%3D%20dayofyear%28datetime%28%5C%222020-07-20%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"day_of_the_year": 202
}
```
## endofyear
Returns the end of the year containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the end of the year for the given date value
### Example
```kusto theme={null}
endofyear(date)
```
```kusto theme={null}
['sample-http-logs']
| extend end_of_the_year = endofyear(datetime(2016-06-26T08:20))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20%20end_of_the_year%20%3D%20endofyear%28datetime%28%5C%222016-06-26T08%3A20%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"end_of_the_year": "2016-12-31T23:59:59.999999999Z"
}
```
## getmonth
Get the month number (1-12) from a datetime.
```kusto theme={null}
['sample-http-logs']
| extend get_specific_month = getmonth(datetime(2020-07-26T08:20))
```
## getyear
Returns the year part of the `datetime` argument.
### Example
```kusto theme={null}
getyear(datetime())
```
```kusto theme={null}
['sample-http-logs']
| project get_specific_year = getyear(datetime(2020-07-26))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20get_specific_year%20%3D%20getyear%28datetime%28%5C%222020-07-26%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"get_specific_year": 2020
}
```
## hourofday
Returns the integer number representing the hour number of the given date
### Arguments
* a\_date: A datetime.
### Returns
hour number of the day (0-23).
### Example
```kusto theme={null}
hourofday(a_date)
```
```kusto theme={null}
['sample-http-logs']
| project get_specific_hour = hourofday(datetime(2016-06-26T08:20:03.123456Z))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20get_specific_hour%20%3D%20hourofday%28datetime%28%5C%222016-06-26T08%3A20%3A03.123456Z%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"get_specific_hour": 8
}
```
## endofday
Returns the end of the day containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the end of the day for the given date value.
### Example
```kusto theme={null}
endofday(date)
```
```kusto theme={null}
['sample-http-logs']
| project end_of_day_series = endofday(datetime(2016-06-26T08:20:03.123456Z))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20end_of_day_series%20%3D%20endofday%28datetime%28%5C%222016-06-26T08%3A20%3A03.123456Z%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"end_of_day_series": "2016-06-26T23:59:59.999999999Z"
}
```
## now
Returns the current UTC clock time, optionally offset by a given timespan. This function can be used multiple times in a statement and the clock time being referenced will be the same for all instances.
### Arguments
* offset: A timespan, added to the current UTC clock time. Default: 0.
### Returns
The current UTC clock time as a datetime.
### Example
```kusto theme={null}
now([offset])
```
```kusto theme={null}
['sample-http-logs']
| project returns_clock_time = now(-5d)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20returns_clock_time%20%3D%20now%28-5d%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"returns_clock_time": "2023-09-07T02:54:50Z"
}
```
## endofmonth
Returns the end of the month containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the end of the month for the given date value.
### Example
```kusto theme={null}
endofmonth(date)
```
```kusto theme={null}
['sample-http-logs']
| project end_of_the_month = endofmonth(datetime(2016-06-26T08:20))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20end_of_the_month%20%3D%20endofmonth%28datetime%28%5C%222016-06-26T08%3A20%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"end_of_the_month": "2016-06-30T23:59:59.999999999Z"
}
```
## endofweek
Returns the end of the week containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the end of the week for the given date value
### Example
```kusto theme={null}
endofweek(date)
```
```kusto theme={null}
['sample-http-logs']
| project end_of_the_week = endofweek(datetime(2019-04-18T08:20))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20end_of_the_week%20%3D%20endofweek%28datetime%28%5C%222019-04-18T08%3A20%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"end_of_the_week": "2019-04-20T23:59:59.999999999Z"
}
```
## monthofyear
Returns the integer number represents the month number of the given year.
### Arguments
* `date`: A datetime.
### Returns
month number of the given year.
### Example
```kusto theme={null}
monthofyear(datetime("2018-11-21"))
```
```kusto theme={null}
['sample-http-logs']
| project month_of_the_year = monthofyear(datetime(2018-11-11))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20month_of_the_year%20%3D%20monthofyear%28datetime%28%5C%222018-11-11%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"month_of_the_year": 11
}
```
## startofday
Returns the start of the day containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the start of the day for the given date value
### Examples
```kusto theme={null}
startofday(datetime(2020-08-31))
```
```kusto theme={null}
['sample-http-logs']
| project start_of_the_day = startofday(datetime(2018-11-11))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20start_of_the_day%20%3D%20startofday%28datetime%28%5C%222018-11-11%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"start_of_the_day": "2018-11-11T00:00:00Z"
}
```
## startofmonth
Returns the start of the month containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the start of the month for the given date value
### Example
```kusto theme={null}
['github-issues-event']
| project start_of_the_month = startofmonth(datetime(2020-08-01))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27github-issues-event%27%5D%5Cn%7C%20project%20start_of_the_month%20%3D%20%20startofmonth%28datetime%28%5C%222020-08-01%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"start_of_the_month": "2020-08-01T00:00:00Z"
}
```
```kusto theme={null}
['hackernews']
| extend start_of_the_month = startofmonth(datetime(2020-08-01))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27hn%27%5D%5Cn%7C%20project%20start_of_the_month%20%3D%20startofmonth%28datetime%28%5C%222020-08-01%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"start_of_the_month": "2020-08-01T00:00:00Z"
}
```
## startofweek
Returns the start of the week containing the date
Start of the week is considered to be a Sunday.
### Arguments
* date: The input date.
### Returns
A datetime representing the start of the week for the given date value
### Examples
```kusto theme={null}
['github-issues-event']
| extend start_of_the_week = startofweek(datetime(2020-08-01))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27github-issues-event%27%5D%5Cn%7C%20project%20start_of_the_week%20%3D%20%20startofweek%28datetime%28%5C%222020-08-01%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"start_of_the_week": "2020-07-26T00:00:00Z"
}
```
```kusto theme={null}
['hackernews']
| extend start_of_the_week = startofweek(datetime(2020-08-01))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27hn%27%5D%5Cn%7C%20project%20start_of_the_week%20%3D%20startofweek%28datetime%28%5C%222020-08-01%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"start_of_the_week": "2020-07-26T00:00:00Z"
}
```
```kusto theme={null}
['sample-http-logs']
| extend start_of_the_week = startofweek(datetime(2018-06-11T00:00:00Z))
```
## startofyear
Returns the start of the year containing the date
### Arguments
* date: The input date.
### Returns
A datetime representing the start of the year for the given date value
### Examples
```kusto theme={null}
['sample-http-logs']
| project yearStart = startofyear(datetime(2019-04-03))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20yearStart%20%3D%20startofyear%28datetime%28%5C%222019-04-03%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"yearStart": "2019-01-01T00:00:00Z"
}
```
```kusto theme={null}
['sample-http-logs']
| project yearStart = startofyear(datetime(2019-10-09 01:00:00.0000000))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20yearStart%20%3D%20startofyear%28datetime%28%5C%222019-10-09%2001%3A00%3A00.0000000%5C%22%29%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result:
```json theme={null}
{
"yearStart": "2019-01-01T00:00:00Z"
}
```
# unixtime_microseconds_todatetime
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime
This page explains how to use the unixtime_microseconds_todatetime function in APL.
`unixtime_microseconds_todatetime` converts a Unix timestamp that’s expressed in whole microseconds since 1970-01-01 00:00:00 UTC to an APL `datetime` value.
Use the function whenever you ingest data that stores time as epoch microseconds (for example, JSON logs from NGINX or metrics that follow the StatsD line protocol). Converting to `datetime` lets you bin, filter, and visualize events with the rest of your time-series data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you often convert epoch values with `eval ts=strftime(_time,"%Y-%m-%dT%H:%M:%S.%6N")`. In APL, the conversion happens with a scalar function, so you can use it inline wherever a `datetime` literal is accepted.
```sql Splunk example theme={null}
| eval eventTime=strftime( micro_ts/1000000 , "%Y-%m-%dT%H:%M:%S.%6N")
```
```kusto APL equivalent theme={null}
| extend eventTime = unixtime_microseconds_todatetime(micro_ts)
```
Standard SQL engines rarely expose microsecond-epoch helpers. You usually cast or divide by 1,000,000 and add an interval. APL gives you a dedicated scalar function that returns a native `datetime`, which then supports the full date-time syntax.
```sql SQL example theme={null}
SELECT TIMESTAMP '1970-01-01 00:00:00' + micro_ts / 1000000 * INTERVAL '1 second' FROM events;
```
```kusto APL equivalent theme={null}
['events']
| extend eventTime = unixtime_microseconds_todatetime(micro_ts)
```
## Usage
### Syntax
```kusto theme={null}
unixtime_microseconds_todatetime(microseconds)
```
### Parameters
| Name | Type | Description |
| -------------- | --------------- | ----------------------------------------------------------------------- |
| `microseconds` | `int` or `long` | Whole microseconds since the Unix epoch. Fractional input is truncated. |
### Returns
A `datetime` value that represents the given epoch microseconds at UTC precision (1 microsecond).
## Use case example
The HTTP access logs keep the timestamp as epoch microseconds and you want to convert the values to datetime.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend epoch_microseconds = toint(datetime_diff('Microsecond', _time, datetime(1970-01-01)))
| extend datetime_standard = unixtime_microseconds_todatetime(epoch_microseconds)
| project _time, epoch_microseconds, datetime_standard
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20epoch_microseconds%20%3D%20toint\(datetime_diff\('Microsecond'%2C%20_time%2C%20datetime\(1970-01-01\)\)\)%20%7C%20extend%20datetime_standard%20%3D%20unixtime_microseconds_todatetime\(epoch_microseconds\)%20%7C%20project%20_time%2C%20epoch_microseconds%2C%20datetime_standard%22%7D)
**Output**
| \_time | epoch\_microseconds | datetime\_standard |
| ---------------- | ------------------- | -------------------- |
| May 15, 12:09:22 | 1,747,303,762 | 2025-05-15T10:09:22Z |
This query converts the timestamp to epoch microseconds and then back to datetime for demonstration purposes.
## List of related functions
* [unixtime\_milliseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime): Converts a Unix timestamp expressed in whole milliseconds to an APL `datetime` value.
* [unixtime\_nanoseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime): Converts a Unix timestamp expressed in whole nanoseconds to an APL `datetime` value.
* [unixtime\_seconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime): Converts a Unix timestamp expressed in whole seconds to an APL `datetime` value.
# unixtime_milliseconds_todatetime
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime
This page explains how to use the unixtime_milliseconds_todatetime function in APL.
`unixtime_milliseconds_todatetime` converts a Unix timestamp that’s expressed in whole milliseconds since 1970-01-01 00:00:00 UTC to an APL `datetime` value.
Use the function whenever you ingest data that stores time as epoch milliseconds (for example, JSON logs from NGINX or metrics that follow the StatsD line protocol). Converting to `datetime` lets you bin, filter, and visualize events with the rest of your time-series data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
`unixtime_milliseconds_todatetime()` corresponds to an `eval` expression that divides the epoch value by 1000 and formats the result. You skip both steps in APL because the function takes milliseconds directly.
```sql Splunk example theme={null}
| eval timestamp=strftime(epoch_ms/1000,"%Y-%m-%dT%H:%M:%SZ")
```
```kusto APL equivalent theme={null}
| extend timestamp=unixtime_milliseconds_todatetime(epoch_ms)
```
The function plays the same role as `FROM_UNIXTIME()` or `TO_TIMESTAMP()` in SQL dialects. In APL, you don’t divide by 1,000 because the function expects milliseconds.
```sql SQL example theme={null}
SELECT FROM_UNIXTIME(epoch_ms/1000) AS timestamp FROM requests;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend timestamp=unixtime_milliseconds_todatetime(epoch_ms)
```
## Usage
### Syntax
```kusto theme={null}
unixtime_milliseconds_todatetime(milliseconds)
```
### Parameters
| Name | Type | Description |
| -------------- | --------------- | ----------------------------------------------------------------------- |
| `milliseconds` | `int` or `long` | Whole milliseconds since the Unix epoch. Fractional input is truncated. |
### Returns
A `datetime` value that represents the given epoch milliseconds at UTC precision (1 millisecond).
## Use case example
The HTTP access logs keep the timestamp as epoch milliseconds and you want to convert the values to datetime.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend epoch_milliseconds = toint(datetime_diff('Millisecond', _time, datetime(1970-01-01)))
| extend datetime_standard = unixtime_milliseconds_todatetime(epoch_milliseconds)
| project _time, epoch_milliseconds, datetime_standard
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20epoch_milliseconds%20%3D%20toint\(datetime_diff\('Millisecond'%2C%20_time%2C%20datetime\(1970-01-01\)\)\)%20%7C%20extend%20datetime_standard%20%3D%20unixtime_milliseconds_todatetime\(epoch_milliseconds\)%20%7C%20project%20_time%2C%20epoch_milliseconds%2C%20datetime_standard%22%7D)
**Output**
| \_time | epoch\_milliseconds | datetime\_standard |
| ---------------- | ------------------- | -------------------- |
| May 15, 12:09:22 | 1,747,303,762 | 2025-05-15T10:09:22Z |
This query converts the timestamp to epoch milliseconds and then back to datetime for demonstration purposes.
## List of related functions
* [unixtime\_microseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime): Converts a Unix timestamp expressed in whole microseconds to an APL `datetime` value.
* [unixtime\_nanoseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime): Converts a Unix timestamp expressed in whole nanoseconds to an APL `datetime` value.
* [unixtime\_seconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime): Converts a Unix timestamp expressed in whole seconds to an APL `datetime` value.
# unixtime_nanoseconds_todatetime
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime
This page explains how to use the unixtime_nanoseconds_todatetime function in APL.
`unixtime_nanoseconds_todatetime` converts a Unix timestamp that’s expressed in whole nanoseconds since 1970-01-01 00:00:00 UTC to an APL `datetime` value.
Use the function whenever you ingest data that stores time as epoch nanoseconds (for example, JSON logs from NGINX or metrics that follow the StatsD line protocol). Converting to `datetime` lets you bin, filter, and visualize events with the rest of your time-series data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL usually stores `_time` in seconds and uses functions such as `strftime` or `strptime` for conversion. In APL, you pass the nanosecond integer directly to `unixtime_nanoseconds_todatetime`, so you don’t divide by 1,000,000,000 first.
```sql Splunk example theme={null}
| eval event_time = strftime(epoch_ns/1000000000, "%Y-%m-%dT%H:%M:%S.%N%z")
```
```kusto APL equivalent theme={null}
| extend event_time = unixtime_nanoseconds_todatetime(epoch_ns)
```
Many SQL engines use `TO_TIMESTAMP_LTZ()` or similar functions that expect seconds or microseconds. In APL, you pass the nanosecond value directly, and the function returns a `datetime` (UTC).
```sql SQL example theme={null}
SELECT TO_TIMESTAMP_LTZ(epoch_ns/1e9) AS event_time
FROM events;
```
```kusto APL equivalent theme={null}
events
| extend event_time = unixtime_nanoseconds_todatetime(epoch_ns)
```
## Usage
### Syntax
```kusto theme={null}
unixtime_nanoseconds_todatetime(nanoseconds)
```
### Parameters
| Name | Type | Description |
| ------------- | --------------- | ---------------------------------------------------------------------- |
| `nanoseconds` | `int` or `long` | Whole nanoseconds since the Unix epoch. Fractional input is truncated. |
### Returns
A `datetime` value that represents the given epoch nanoseconds at UTC precision (1 nanosecond).
## Use case example
The HTTP access logs keep the timestamp as epoch nanoseconds and you want to convert the values to datetime.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend epoch_nanoseconds = toint(datetime_diff('Nanosecond', _time, datetime(1970-01-01)))
| extend datetime_standard = unixtime_nanoseconds_todatetime(epoch_nanoseconds)
| project _time, epoch_nanoseconds, datetime_standard
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20epoch_nanoseconds%20%3D%20toint\(datetime_diff\('Nanosecond'%2C%20_time%2C%20datetime\(1970-01-01\)\)\)%20%7C%20extend%20datetime_standard%20%3D%20unixtime_nanoseconds_todatetime\(epoch_nanoseconds\)%20%7C%20project%20_time%2C%20epoch_nanoseconds%2C%20datetime_standard%22%7D)
**Output**
| \_time | epoch\_nanoseconds | datetime\_standard |
| ---------------- | ------------------ | -------------------- |
| May 15, 12:09:22 | 1,747,303,762 | 2025-05-15T10:09:22Z |
This query converts the timestamp to epoch nanoseconds and then back to datetime for demonstration purposes.
## List of related functions
* [unixtime\_microseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime): Converts a Unix timestamp expressed in whole microseconds to an APL `datetime` value.
* [unixtime\_milliseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime): Converts a Unix timestamp expressed in whole milliseconds to an APL `datetime` value.
* [unixtime\_seconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime): Converts a Unix timestamp expressed in whole seconds to an APL `datetime` value.
# unixtime_seconds_todatetime
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions/unixtime-seconds-todatetime
This page explains how to use the unixtime_seconds_todatetime function in APL.
`unixtime_seconds_todatetime` converts a Unix timestamp that’s expressed in whole seconds since 1970-01-01 00:00:00 UTC to an APL [datetime value](/apl/data-types/scalar-data-types).
Use the function whenever you ingest data that stores time as epoch seconds (for example, JSON logs from NGINX or metrics that follow the StatsD line protocol). Converting to `datetime` lets you bin, filter, and visualize events with the rest of your time-series data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
`unixtime_seconds_todatetime` replaces the combination of `eval strftime` / `strptime` that you normally use in Splunk. Pass the epoch value directly and APL returns a `datetime`.
```sql Splunk example theme={null}
eval event_time = strftime(epoch, "%Y-%m-%dT%H:%M:%S")
```
```kusto APL equivalent theme={null}
extend event_time = unixtime_seconds_todatetime(epoch)
```
Most ANSI SQL engines call this conversion with `FROM_UNIXTIME` or `TO_TIMESTAMP`. The APL version has the same single-argument signature, returns a full `datetime`, and automatically interprets the input as seconds (not milliseconds).
```sql SQL example theme={null}
SELECT TO_TIMESTAMP(epoch_seconds) AS event_time FROM events;
```
```kusto APL equivalent theme={null}
['events']
| extend event_time = unixtime_seconds_todatetime(epoch_seconds)
```
## Usage
### Syntax
```kusto theme={null}
unixtime_seconds_todatetime(seconds)
```
### Parameters
| Name | Type | Description |
| --------- | --------------- | ------------------------------------------------------------------ |
| `seconds` | `int` or `long` | Whole seconds since the Unix epoch. Fractional input is truncated. |
### Returns
A `datetime` value that represents the given epoch seconds at UTC precision (1 second).
## Use case example
The HTTP access logs keep the timestamp as epoch seconds and you want to convert the values to datetime.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend epoch_seconds = toint(datetime_diff('Second', _time, datetime(1970-01-01)))
| extend datetime_standard = unixtime_seconds_todatetime(epoch_seconds)
| project _time, epoch_seconds, datetime_standard
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20epoch_seconds%20%3D%20toint\(datetime_diff\('Second'%2C%20_time%2C%20datetime\(1970-01-01\)\)\)%20%7C%20extend%20datetime_standard%20%3D%20unixtime_seconds_todatetime\(epoch_seconds\)%20%7C%20project%20_time%2C%20epoch_seconds%2C%20datetime_standard%22%7D)
**Output**
| \_time | epoch\_seconds | datetime\_standard |
| ---------------- | -------------- | -------------------- |
| May 15, 12:09:22 | 1,747,303,762 | 2025-05-15T10:09:22Z |
This query converts the timestamp to epoch seconds and then back to datetime for demonstration purposes.
## List of related functions
* [unixtime\_microseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-microseconds-todatetime): Converts a Unix timestamp expressed in whole microseconds to an APL `datetime` value.
* [unixtime\_milliseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-milliseconds-todatetime): Converts a Unix timestamp expressed in whole milliseconds to an APL `datetime` value.
* [unixtime\_nanoseconds\_todatetime](/apl/scalar-functions/datetime-functions/unixtime-nanoseconds-todatetime): Converts a Unix timestamp expressed in whole nanoseconds to an APL `datetime` value.
# week_of_year
Source: https://axiom.co/docs/apl/scalar-functions/datetime-functions/week-of-year
This page explains how to use the week_of_year function in APL.
Use the `week_of_year` function in APL to extract the ISO 8601 week number from a datetime expression. The ISO 8601 standard defines the first week of the year as the one that contains the first Thursday of the year, and weeks start on Mondays.
You can use `week_of_year` to group records by week when analyzing trends over time. This is especially useful for weekly aggregation in dashboards, anomaly detection, and cohort analysis.
Use it when you want to:
* Track activity or metrics week by week.
* Normalize data across different timeframes by weekly intervals.
* Generate week-based summaries across log, trace, or security datasets.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use the `strftime` function with the `%V` or `%U` specifiers to extract the week of the year. In APL, the `week_of_year` function directly extracts the ISO 8601 week number from a datetime.
```sql Splunk example theme={null}
... | eval week=strftime(_time, "%V")
```
```kusto APL equivalent theme={null}
... | extend week = week_of_year(_time)
```
In ANSI SQL, you often use `EXTRACT(WEEK FROM timestamp)` or `DATEPART(WEEK, timestamp)`. These implementations may differ slightly across platforms in how they define the first week of the year. APL uses the ISO 8601 definition via `week_of_year`.
```sql SQL example theme={null}
SELECT EXTRACT(WEEK FROM timestamp_column) AS week FROM events;
```
```kusto APL equivalent theme={null}
['dataset']
| extend week = week_of_year(_time)
```
## Usage
### Syntax
```kusto theme={null}
week_of_year(datetime)
```
### Parameters
| Name | Type | Description |
| -------- | ---------- | ------------------------- |
| datetime | `datetime` | The input datetime value. |
### Returns
A `long` representing the ISO 8601 week number (from 1 to 53).
## Use case examples
Group HTTP log events by week to understand traffic trends and average request duration.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend week = week_of_year(_time)
| summarize avg(req_duration_ms) by week
| sort by week asc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20week%20%3D%20week_of_year\(_time\)%20%7C%20summarize%20avg\(req_duration_ms\)%20by%20week%20%7C%20sort%20by%20week%20asc%22%7D)
**Output**
| week | avg\_req\_duration\_ms |
| ---- | ---------------------- |
| 1 | 243.8 |
| 2 | 251.1 |
| 3 | 237.4 |
This query extracts the ISO week number for each record and calculates the average request duration per week.
Use weekly grouping to monitor changes in span durations for frontend services over time.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where ['service.name'] == 'frontend'
| extend week = week_of_year(_time)
| summarize avg_duration = avg(duration) by week
| sort by week asc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'frontend'%20%7C%20extend%20week%20%3D%20week_of_year\(_time\)%20%7C%20summarize%20avg_duration%20%3D%20avg\(duration\)%20by%20week%20%7C%20sort%20by%20week%20asc%22%7D)
**Output**
| week | avg\_duration |
| ---- | ---------------- |
| 1 | 00:00:01.2340000 |
| 2 | 00:00:01.1750000 |
| 3 | 00:00:01.2890000 |
This query shows how the average span duration changes weekly for the `frontend` service.
Count HTTP errors by week to detect unusual spikes in failed requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status startswith '5'
| extend week = week_of_year(_time)
| summarize error_count = count() by week
| sort by week asc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20startswith%20'5'%20%7C%20extend%20week%20%3D%20week_of_year\(_time\)%20%7C%20summarize%20error_count%20%3D%20count\(\)%20by%20week%20%7C%20sort%20by%20week%20asc%22%7D)
**Output**
| week | error\_count |
| ---- | ------------ |
| 1 | 18 |
| 2 | 34 |
| 3 | 12 |
This query detects week-by-week patterns in server error frequency, helping identify problem periods.
# GenAI functions
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions
This page provides an overview of GenAI functions in APL for analyzing and processing GenAI conversation data.
GenAI functions in APL help you analyze and process GenAI conversation data, including messages, token usage, costs, and conversation metadata. These functions are useful when working with logs or data from large language models (LLMs) and AI systems.
## When to use GenAI functions
Use GenAI functions when you need to:
* Extract specific information from AI conversation logs, such as user prompts, assistant responses, or system prompts
* Calculate token costs and usage metrics for LLM API calls
* Analyze conversation structure and flow, including turn counts and message roles
* Process and filter conversation messages based on roles or content
* Determine pricing information for different AI models
* Detect truncation or tool calls in AI responses
## Available GenAI functions
| Function | Description |
| :------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------ |
| [genai\_concat\_contents](/apl/scalar-functions/genai-functions/genai-concat-contents) | Concatenates message contents from a conversation array |
| [genai\_conversation\_turns](/apl/scalar-functions/genai-functions/genai-conversation-turns) | Counts the number of conversation turns |
| [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost) | Calculates the total cost for input and output tokens |
| [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens) | Estimates the number of tokens in a text string |
| [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response) | Extracts the assistant’s response from a conversation |
| [genai\_extract\_function\_results](/apl/scalar-functions/genai-functions/genai-extract-function-results) | Extracts function call results from messages |
| [genai\_extract\_system\_prompt](/apl/scalar-functions/genai-functions/genai-extract-system-prompt) | Extracts the system prompt from a conversation |
| [genai\_extract\_tool\_calls](/apl/scalar-functions/genai-functions/genai-extract-tool-calls) | Extracts tool calls from messages |
| [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt) | Extracts the user prompt from a conversation |
| [genai\_get\_content\_by\_index](/apl/scalar-functions/genai-functions/genai-get-content-by-index) | Gets message content by index position |
| [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role) | Gets message content by role |
| [genai\_get\_pricing](/apl/scalar-functions/genai-functions/genai-get-pricing) | Gets pricing information for a specific model |
| [genai\_get\_role](/apl/scalar-functions/genai-functions/genai-get-role) | Gets the role of a message at a specific index |
| [genai\_has\_tool\_calls](/apl/scalar-functions/genai-functions/genai-has-tool-calls) | Checks if messages contain tool calls |
| [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost) | Calculates the cost for input tokens |
| [genai\_is\_truncated](/apl/scalar-functions/genai-functions/genai-is-truncated) | Checks if a response was truncated |
| [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles) | Extracts all message roles from a conversation |
| [genai\_output\_cost](/apl/scalar-functions/genai-functions/genai-output-cost) | Calculates the cost for output tokens |
# genai_concat_contents
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-concat-contents
This page explains how to use the genai_concat_contents function in APL.
The `genai_concat_contents` function concatenates all message contents from a GenAI conversation array into a single string. This is useful when you need to combine multiple conversation messages into a single text field for analysis, full-text search, or creating a complete conversation transcript.
You can use this function to create searchable conversation transcripts, prepare data for analysis, or consolidate conversation history for reporting.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would typically use multiple `eval` commands with `mvjoin` to concatenate array values, but there’s no direct equivalent for extracting and joining message contents from nested structures.
```sql Splunk example theme={null}
| eval all_content=mvjoin(messages, " ")
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend all_content = genai_concat_contents(messages, ' ')
```
In ANSI SQL, you would need to unnest the array and use `STRING_AGG` or similar functions to concatenate values, which is more verbose.
```sql SQL example theme={null}
SELECT
conversation_id,
STRING_AGG(content, ' ') as all_content
FROM conversations
CROSS JOIN UNNEST(messages) as msg
GROUP BY conversation_id
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend all_content = genai_concat_contents(messages, ' ')
```
## Usage
### Syntax
```kusto theme={null}
genai_concat_contents(messages, separator)
```
### Parameters
| Name | Type | Required | Description |
| --------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains a `role` and `content` field. |
| separator | string | No | The string used to separate message contents. Default is a space character (`' '`). |
### Returns
Returns a string containing all message contents concatenated together with the specified separator.
## Example
Create a searchable conversation transcript from a GenAI conversation array.
**Query**
```kusto theme={null}
['genai-traces']
| extend conversation_text = genai_concat_contents(['attributes.gen_ai.input.messages'], ' / ')
```
**Output**
| conversation\_text |
| ------------------------------------------------------------------------------------------------------------------------------------------------ |
| "Hello, how are you? / I'm good, thank you! / What's your name? / My name is John. / What's your favorite color? / My favorite color is blue. /" |
This query concatenates the message contents of a GenAI conversation array with a separator of `/`.
## List of related functions
* [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt): Extracts only the user's prompt instead of all messages. Use this when you need just the user's input.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts only the assistant's response. Use this when you need just the AI's output.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content filtered by a specific role. Use this when you need messages from a particular role like 'system' or 'tool'.
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Extracts all message roles from a conversation. Use this to understand the conversation structure.
* [strcat\_array](/apl/scalar-functions/array-functions/strcat-array): Concatenates a simple string array. Use this for non-GenAI arrays that don't have the message structure.
# genai_conversation_turns
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-conversation-turns
This page explains how to use the genai_conversation_turns function in APL.
The `genai_conversation_turns` function counts the number of conversation turns in a GenAI messages array. A turn typically represents a user message followed by an assistant response. This metric helps you understand conversation length and engagement patterns in AI applications.
You can use this function to analyze conversation complexity, monitor user engagement, identify outlier conversations, or track conversation metrics for billing and usage analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would typically use `eval` with `mvcount` to count array elements, but there’s no built-in function specifically for counting conversation turns.
```sql Splunk example theme={null}
| eval turn_count=mvcount(messages)/2
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend turn_count = genai_conversation_turns(messages)
```
In ANSI SQL, you would need to unnest the array and count rows, then divide by the number of roles, which is more complex.
```sql SQL example theme={null}
SELECT
conversation_id,
COUNT(*) / 2 as turn_count
FROM conversations
CROSS JOIN UNNEST(messages)
GROUP BY conversation_id
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend turn_count = genai_conversation_turns(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_conversation_turns(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains a `role` and `content` field. |
### Returns
Returns a long integer representing the number of conversation turns. A turn is typically counted as a user-assistant exchange pair.
## Example
Count the number of conversation turns in a GenAI chat operation.
**Query**
```kusto theme={null}
['genai-traces']
| extend turns = genai_conversation_turns(['attributes.gen_ai.input.messages'])
| summarize avg_turns = avg(turns), max_turns = max(turns)
```
**Output**
| avg\_turns | max\_turns |
| ---------- | ---------- |
| 4.2 | 12 |
This query calculates the average and maximum number of conversation turns, helping you understand conversation complexity and engagement patterns.
## List of related functions
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Extracts all message roles to understand conversation structure. Use this when you need to analyze the role distribution in conversations.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the total number of messages (not turns). Use this when you need the raw message count instead of turn count.
* [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost): Calculates the cost of a conversation. Use this in combination with turn count to understand cost per turn.
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates token usage. Use this with turn count to analyze tokens per turn.
# genai_cost
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-cost
This page explains how to use the genai_cost function in APL.
The `genai_cost` function calculates the total cost of a GenAI API call based on the model name, input tokens, and output tokens. This function uses current pricing information for various AI models to provide accurate cost estimates.
You can use this function to track AI spending, analyze cost per conversation, identify expensive queries, or create cost reports and budgets for AI services.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to manually calculate costs using eval and lookup tables.
```sql Splunk example theme={null}
| lookup model_pricing model OUTPUT input_price output_price
| eval total_cost=(input_tokens * input_price / 1000000) + (output_tokens * output_price / 1000000)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend total_cost = genai_cost(model, input_tokens, output_tokens)
```
In ANSI SQL, you would need to join with a pricing table and calculate costs manually.
```sql SQL example theme={null}
SELECT
l.*,
(l.input_tokens * p.input_price / 1000000) +
(l.output_tokens * p.output_price / 1000000) as total_cost
FROM ai_logs l
JOIN model_pricing p ON l.model = p.model_name
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend total_cost = genai_cost(model, input_tokens, output_tokens)
```
## Usage
### Syntax
```kusto theme={null}
genai_cost(model, input_tokens, output_tokens)
```
### Parameters
| Name | Type | Required | Description |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------- |
| model | string | Yes | The name of the AI model (for example, 'gpt-4', 'claude-3-opus', 'gpt-3.5-turbo'). |
| input\_tokens | long | Yes | The number of input tokens (prompt tokens) used in the API call. |
| output\_tokens | long | Yes | The number of output tokens (completion tokens) generated by the API call. |
### Returns
Returns a real number representing the total cost in dollars (USD) for the API call based on the model's pricing.
## Example
Calculate the total cost of a GenAI API call based on model and token usage.
**Query**
```kusto theme={null}
['genai-traces']
| extend model = ['attributes.gen_ai.response.model']
| extend input_tokens = tolong(['attributes.gen_ai.usage.input_tokens'])
| extend output_tokens = tolong(['attributes.gen_ai.usage.output_tokens'])
| extend api_cost = genai_cost(model, input_tokens, output_tokens)
| summarize total_cost = sum(api_cost), avg_cost = avg(api_cost)
```
**Output**
| total\_cost | avg\_cost |
| ----------- | --------- |
| 12.45 | 0.0125 |
This query calculates total and average spending on AI API calls, helping you track costs and identify spending trends.
## List of related functions
* [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost): Calculates only the input token cost. Use this when you need to separate input and output costs.
* [genai\_output\_cost](/apl/scalar-functions/genai-functions/genai-output-cost): Calculates only the output token cost. Use this when analyzing generation costs separately.
* [genai\_get\_pricing](/apl/scalar-functions/genai-functions/genai-get-pricing): Gets the pricing structure for a model. Use this to understand or display pricing information.
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates tokens from text. Use this with genai\_cost to predict costs before making API calls.
# genai_estimate_tokens
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-estimate-tokens
This page explains how to use the genai_estimate_tokens function in APL.
The `genai_estimate_tokens` function estimates the number of tokens in a text string. This estimation helps you predict API costs, validate input sizes, and monitor token usage before making actual API calls to LLM services.
You can use this function to validate prompt sizes, estimate costs before API calls, monitor content length, or analyze token efficiency across different prompts.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, there’s no direct equivalent for token estimation. You would typically use character or word count as a rough approximation.
```sql Splunk example theme={null}
| eval estimated_tokens=len(text)/4
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend estimated_tokens = genai_estimate_tokens(text)
```
In ANSI SQL, you would need to use character-based estimations, which are less accurate than proper token counting.
```sql SQL example theme={null}
SELECT
text,
LENGTH(text) / 4 as estimated_tokens
FROM prompts
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend estimated_tokens = genai_estimate_tokens(text)
```
## Usage
### Syntax
```kusto theme={null}
genai_estimate_tokens(text)
```
### Parameters
| Name | Type | Required | Description |
| ---- | ------ | -------- | --------------------------------------------------------------- |
| text | string | Yes | The text string for which you want to estimate the token count. |
### Returns
Returns a long integer representing the estimated number of tokens in the input text.
## Example
Estimate the number of tokens in a GenAI conversation prompt.
**Query**
```kusto theme={null}
['genai-traces']
| extend user_prompt = genai_extract_user_prompt(['attributes.gen_ai.input.messages'])
| extend estimated_tokens = genai_estimate_tokens(user_prompt)
| summarize avg_tokens = avg(estimated_tokens), max_tokens = max(estimated_tokens)
```
**Output**
| avg\_tokens | max\_tokens |
| ----------- | ----------- |
| 245 | 1024 |
This query analyzes prompt token usage patterns, helping you predict costs and validate input sizes.
## List of related functions
* [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost): Calculates the actual cost based on token usage. Use this in combination with token estimates to predict costs.
* [strlen](/apl/scalar-functions/string-functions#strlen): Returns string length in characters. Use this for a simpler character count without token estimation.
* [string\_size](/apl/scalar-functions/string-functions/string-size): Returns string length in characters. Use this when you need character count instead of token count.
* [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost): Calculates input token cost. Combine with token estimation to predict prompt costs.
# genai_extract_assistant_response
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-extract-assistant-response
This page explains how to use the genai_extract_assistant_response function in APL.
The `genai_extract_assistant_response` function extracts the assistant’s response from a GenAI messages array. It returns the content of the last message with the 'assistant' role, which typically contains the AI model’s generated response to the user.
You can use this function to analyze AI responses, evaluate response quality, perform sentiment analysis on AI outputs, or track specific response patterns for monitoring and debugging.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to use complex eval statements with mvfilter and mvindex to extract assistant messages.
```sql Splunk example theme={null}
| eval assistant_msgs=mvfilter(match(role, "assistant"))
| eval response=mvindex(assistant_msgs, -1)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend response = genai_extract_assistant_response(messages)
```
In ANSI SQL, you would need to unnest arrays, filter by role, and select the last message, which is more verbose.
```sql SQL example theme={null}
SELECT
conversation_id,
content as assistant_response
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY msg_index DESC) as rn
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS msg_index
WHERE role = 'assistant'
) WHERE rn = 1
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend assistant_response = genai_extract_assistant_response(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_extract_assistant_response(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a string containing the content of the last assistant message in the conversation, or an empty string if no assistant message is found.
## Example
Extract the assistant's response from a GenAI conversation.
**Query**
```kusto theme={null}
['genai-traces']
| extend ai_response = genai_extract_assistant_response(['attributes.gen_ai.input.messages'])
| where strlen(ai_response) > 0
| project _time, ai_response
| limit 3
```
**Output**
| \_time | ai\_response |
| -------------------- | ------------------------------------------------------------------------------ |
| 2024-01-15T10:30:00Z | To reset your password, click on the 'Forgot Password' link on the login page. |
| 2024-01-15T10:31:00Z | Our business hours are Monday to Friday, 9 AM to 5 PM EST. |
This query extracts AI responses, helping you analyze response quality and patterns.
## List of related functions
* [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt): Extracts the user's prompt. Use this to analyze what users are asking.
* [genai\_extract\_system\_prompt](/apl/scalar-functions/genai-functions/genai-extract-system-prompt): Extracts the system prompt. Use this to understand how the AI is configured.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content by any role. Use this when you need messages from roles other than assistant.
* [genai\_concat\_contents](/apl/scalar-functions/genai-functions/genai-concat-contents): Concatenates all message contents. Use this when you need the full conversation instead of just the assistant's response.
* [genai\_has\_tool\_calls](/apl/scalar-functions/genai-functions/genai-has-tool-calls): Checks for tool calls in messages. Use this to detect when the assistant made function calls.
# genai_extract_function_results
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-extract-function-results
This page explains how to use the genai_extract_function_results function in APL.
The `genai_extract_function_results` function extracts function call results from GenAI messages. When an AI model uses function calling (also known as tool calling), the results are stored in specific message roles. This function retrieves those results for analysis.
You can use this function to monitor function call outcomes, debug tool integrations, analyze API usage patterns, or track the effectiveness of function calls in AI workflows.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to use complex filtering and extraction logic to isolate function results from nested message structures.
```sql Splunk example theme={null}
| eval function_results=mvfilter(match(role, "function") OR match(role, "tool"))
| eval results=mvindex(function_results, 0)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend results = genai_extract_function_results(messages)
```
In ANSI SQL, you would need to unnest arrays and filter for function or tool roles, which is more complex.
```sql SQL example theme={null}
SELECT
conversation_id,
JSON_EXTRACT(content, '$.result') as function_results
FROM conversations
CROSS JOIN UNNEST(messages)
WHERE role IN ('function', 'tool')
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend function_results = genai_extract_function_results(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_extract_function_results(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a dynamic object containing the function call results from the conversation, or null if no function results are found.
## Example
Extract function call results from a GenAI conversation to analyze tool execution outcomes.
**Query**
```kusto theme={null}
['genai-traces']
| extend function_results = genai_extract_function_results(['attributes.gen_ai.input.messages'])
| where isnotnull(function_results)
| project _time, function_results
| limit 3
```
**Output**
| \_time | function\_results |
| -------------------- | -------------------------------------------------------------------- |
| 2024-01-15T10:30:00Z | `{"status": "success", "data": {"temperature": 72, "humidity": 45}}` |
| 2024-01-15T10:31:00Z | `{"status": "success", "data": {"balance": 1250.50}}` |
This query shows function call results, helping you understand tool execution performance and outcomes.
## List of related functions
* [genai\_extract\_tool\_calls](/apl/scalar-functions/genai-functions/genai-extract-tool-calls): Extracts the tool call requests. Use this to see what functions were requested, while genai\_extract\_function\_results shows the results.
* [genai\_has\_tool\_calls](/apl/scalar-functions/genai-functions/genai-has-tool-calls): Checks if messages contain tool calls. Use this to filter conversations that use function calling.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content by specific role. Use this for more granular extraction when you need specific role messages.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts assistant responses. Use this when you need the AI's text response instead of function results.
# genai_extract_system_prompt
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-extract-system-prompt
This page explains how to use the genai_extract_system_prompt function in APL.
The `genai_extract_system_prompt` function extracts the system prompt from a GenAI messages array. The system prompt typically contains instructions that define the AI assistant’s behavior, personality, and capabilities. It’s usually the first message with role 'system'.
You can use this function to audit AI behavior configurations, monitor prompt changes, analyze consistency across conversations, or validate that correct system instructions are being used.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to filter messages by role and extract the first system message.
```sql Splunk example theme={null}
| eval system_msgs=mvfilter(match(role, "system"))
| eval system_prompt=mvindex(system_msgs, 0)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend system_prompt = genai_extract_system_prompt(messages)
```
In ANSI SQL, you would unnest the array and filter for the first system role message.
```sql SQL example theme={null}
SELECT
conversation_id,
content as system_prompt
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY msg_index) as rn
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS msg_index
WHERE role = 'system'
) WHERE rn = 1
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend system_prompt = genai_extract_system_prompt(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_extract_system_prompt(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a string containing the content of the system message, or an empty string if no system message is found.
## Example
Extract the system prompt from a GenAI conversation to verify AI configuration.
**Query**
```kusto theme={null}
['genai-traces']
| extend system_prompt = genai_extract_system_prompt(['attributes.gen_ai.input.messages'])
| where isnotempty(system_prompt)
| summarize conversation_count = count() by system_prompt
| top 3 by conversation_count
```
**Output**
| system\_prompt | conversation\_count |
| ---------------------------------------------------------------------------- | ------------------- |
| You are a helpful customer service assistant. | 1250 |
| You are a technical support expert specializing in software troubleshooting. | 845 |
This query helps you understand which system prompts are most commonly used and track prompt variations.
## List of related functions
* [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt): Extracts the user's prompt. Use this to analyze what users are asking, while system prompts define AI behavior.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts the assistant's response. Use this to see how the AI responded based on the system prompt.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content by any role. Use this for more flexible extraction when you need other specific roles.
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Lists all message roles. Use this to understand conversation structure and verify system message presence.
# genai_extract_tool_calls
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-extract-tool-calls
This page explains how to use the genai_extract_tool_calls function in APL.
The `genai_extract_tool_calls` function extracts tool call requests from GenAI messages. When an AI model decides to use external tools or functions, it generates tool call messages. This function retrieves those calls so you can analyze what tools are being invoked.
You can use this function to monitor tool usage patterns, debug function calling, track API integrations, or analyze which tools are most frequently requested by your AI applications.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to filter and extract tool call information from nested message structures manually.
```sql Splunk example theme={null}
| eval tool_calls=mvfilter(match(role, "assistant") AND isnotnull(tool_calls))
| eval tools=spath(tool_calls, "tool_calls")
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend tools = genai_extract_tool_calls(messages)
```
In ANSI SQL, you would need to unnest arrays and extract JSON fields for tool calls.
```sql SQL example theme={null}
SELECT
conversation_id,
JSON_EXTRACT(content, '$.tool_calls') as tool_calls
FROM conversations
CROSS JOIN UNNEST(messages)
WHERE JSON_EXTRACT(content, '$.tool_calls') IS NOT NULL
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend tool_calls = genai_extract_tool_calls(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_extract_tool_calls(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a dynamic object containing the tool calls from the conversation, or null if no tool calls are found. Tool calls typically include function name, arguments, and call ID.
## Example
Extract tool calls from a GenAI conversation to analyze which functions are being invoked.
**Query**
```kusto theme={null}
['genai-traces']
| extend tool_calls = genai_extract_tool_calls(['attributes.gen_ai.input.messages'])
| where isnotnull(tool_calls)
| extend tool_name = tostring(tool_calls[0]['function']['name'])
| summarize call_count = count() by tool_name
| top 5 by call_count
```
**Output**
| tool\_name | call\_count |
| ---------------- | ----------- |
| get\_weather | 245 |
| search\_database | 189 |
| send\_email | 123 |
This query shows which tools are most frequently called, helping you understand integration usage patterns.
## List of related functions
* [genai\_extract\_function\_results](/apl/scalar-functions/genai-functions/genai-extract-function-results): Extracts function call results. Use this to see the outcomes of the tool calls.
* [genai\_has\_tool\_calls](/apl/scalar-functions/genai-functions/genai-has-tool-calls): Checks if messages contain tool calls. Use this to quickly filter conversations with function calling.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts assistant text responses. Use this when you need the text response instead of tool calls.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content by role. Use this for more granular extraction of specific message types.
# genai_extract_user_prompt
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-extract-user-prompt
This page explains how to use the genai_extract_user_prompt function in APL.
The `genai_extract_user_prompt` function extracts the user’s prompt from a GenAI messages array. It returns the content of the last message with the 'user' role, which typically contains the user’s question or request to the AI.
You can use this function to analyze user queries, understand common question patterns, perform sentiment analysis on user inputs, or track user behavior and needs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to filter messages by user role and extract the last one.
```sql Splunk example theme={null}
| eval user_msgs=mvfilter(match(role, "user"))
| eval user_prompt=mvindex(user_msgs, -1)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend user_prompt = genai_extract_user_prompt(messages)
```
In ANSI SQL, you would unnest the array, filter by user role, and select the last message.
```sql SQL example theme={null}
SELECT
conversation_id,
content as user_prompt
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY msg_index DESC) as rn
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS msg_index
WHERE role = 'user'
) WHERE rn = 1
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend user_prompt = genai_extract_user_prompt(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_extract_user_prompt(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a string containing the content of the last user message in the conversation, or an empty string if no user message is found.
## Example
Extract the user's prompt from a GenAI conversation to analyze common questions.
**Query**
```kusto theme={null}
['genai-traces']
| extend user_query = genai_extract_user_prompt(['attributes.gen_ai.input.messages'])
| where isnotempty(user_query)
| summarize query_count = count() by user_query
| top 5 by query_count
```
**Output**
| user\_query | query\_count |
| ----------------------------- | ------------ |
| How do I reset my password? | 456 |
| What are your business hours? | 342 |
| How can I track my order? | 298 |
This query identifies the most common user questions, helping you understand user needs and improve responses.
## List of related functions
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts the assistant's response. Use this to analyze AI responses along with user prompts.
* [genai\_extract\_system\_prompt](/apl/scalar-functions/genai-functions/genai-extract-system-prompt): Extracts the system prompt. Use this to understand the AI's configuration when analyzing user queries.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content by any role. Use this for more flexible extraction when you need other specific roles.
* [genai\_concat\_contents](/apl/scalar-functions/genai-functions/genai-concat-contents): Concatenates all messages. Use this when you need the full conversation instead of just the user prompt.
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates token count. Combine with user prompt extraction to analyze prompt sizes.
# genai_get_content_by_index
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-get-content-by-index
This page explains how to use the genai_get_content_by_index function in APL.
The `genai_get_content_by_index` function retrieves the content of a message at a specific position in a GenAI messages array. This allows you to access messages by their position in the conversation sequence.
You can use this function to extract specific messages in a conversation flow, analyze conversation structure, retrieve intermediate messages, or process conversations sequentially.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would use `mvindex` to access array elements by position.
```sql Splunk example theme={null}
| eval message_content=mvindex(messages, 2)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend message_content = genai_get_content_by_index(messages, 2)
```
In ANSI SQL, you would unnest the array and use `OFFSET` to access specific positions.
```sql SQL example theme={null}
SELECT
conversation_id,
content as message_content
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS pos
WHERE pos = 2
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend message_content = genai_get_content_by_index(messages, 2)
```
## Usage
### Syntax
```kusto theme={null}
genai_get_content_by_index(messages, index)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
| index | long | Yes | The zero-based position of the message to retrieve. Use 0 for the first message, 1 for the second, etc. |
### Returns
Returns a string containing the content of the message at the specified index, or an empty string if the index is out of bounds.
## Example
Get the content of the first user message in a GenAI conversation.
**Query**
```kusto theme={null}
['genai-traces']
| extend first_message = genai_get_content_by_index(['attributes.gen_ai.input.messages'], 1)
| where isnotempty(first_message)
| project _time, first_message
| limit 3
```
**Output**
| \_time | first\_message |
| -------------------- | ------------------------------------ |
| 2024-01-15T10:30:00Z | Hello, I need help with my account. |
| 2024-01-15T10:31:00Z | Can you tell me about your services? |
This query helps you understand how users typically start conversations, which can inform greeting messages and initial prompts.
## List of related functions
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content filtered by role. Use this when you need messages from a specific role rather than a specific position.
* [genai\_get\_role](/apl/scalar-functions/genai-functions/genai-get-role): Gets the role at a specific index. Combine with this function to understand both role and content at positions.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns array length. Use this to check message count before accessing by index.
* [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt): Extracts the last user prompt. Use this when you need the most recent user message instead of a specific index.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts the last assistant response. Use this when you need the most recent AI response.
# genai_get_content_by_role
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-get-content-by-role
This page explains how to use the genai_get_content_by_role function in APL.
The `genai_get_content_by_role` function retrieves the content of a message with a specific role from a GenAI messages array. It returns the first message matching the specified role (such as 'user', 'assistant', 'system', or 'tool').
You can use this function to extract messages by role, filter conversations by participant type, analyze specific role patterns, or process messages from particular conversation participants.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would use `mvfilter` to filter by role and then extract the content.
```sql Splunk example theme={null}
| eval filtered_msgs=mvfilter(match(role, "system"))
| eval content=mvindex(filtered_msgs, 0)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend content = genai_get_content_by_role(messages, 'system')
```
In ANSI SQL, you would unnest the array, filter by role, and limit to the first result.
```sql SQL example theme={null}
SELECT
conversation_id,
content
FROM conversations
CROSS JOIN UNNEST(messages)
WHERE role = 'system'
LIMIT 1
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend content = genai_get_content_by_role(messages, 'system')
```
## Usage
### Syntax
```kusto theme={null}
genai_get_content_by_role(messages, role)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
| role | string | Yes | The role to filter by. Common values include 'user', 'assistant', 'system', 'tool', or 'function'. |
### Returns
Returns a string containing the content of the first message with the specified role, or an empty string if no matching message is found.
## Example
Extract the content of the system message from a GenAI conversation.
**Query**
```kusto theme={null}
['genai-traces']
| extend system_content = genai_get_content_by_role(['attributes.gen_ai.input.messages'], 'system')
| where isnotempty(system_content)
| summarize conversation_count = count() by system_content
| top 3 by conversation_count
```
**Output**
| system\_content | conversation\_count |
| ------------------------------------- | ------------------- |
| You are a helpful shopping assistant. | 1250 |
| You are a technical support expert. | 845 |
This query shows the distribution of system prompts being used, helping ensure configuration consistency.
## List of related functions
* [genai\_get\_content\_by\_index](/apl/scalar-functions/genai-functions/genai-get-content-by-index): Gets content by position. Use this when you need a message at a specific index rather than by role.
* [genai\_extract\_user\_prompt](/apl/scalar-functions/genai-functions/genai-extract-user-prompt): Extracts the last user prompt. Use this shorthand when you specifically need the most recent user message.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts the last assistant response. Use this shorthand when you specifically need the most recent AI response.
* [genai\_extract\_system\_prompt](/apl/scalar-functions/genai-functions/genai-extract-system-prompt): Extracts the system prompt. Use this shorthand when you specifically need the system message.
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Lists all roles in the conversation. Use this to understand what roles are present before extracting by role.
# genai_get_pricing
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-get-pricing
This page explains how to use the genai_get_pricing function in APL.
The `genai_get_pricing` function retrieves the pricing information for a specific AI model. It returns a dynamic object containing the input token price and output token price per million tokens, which you can use for cost calculations and budgeting.
You can use this function to display pricing information, create cost calculators, audit pricing data, or understand the cost structure of different AI models.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would typically use a lookup table to retrieve pricing information by model name.
```sql Splunk example theme={null}
| lookup model_pricing model OUTPUT input_price output_price
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend pricing = genai_get_pricing(model)
```
In ANSI SQL, you would join with a pricing table to get model costs.
```sql SQL example theme={null}
SELECT
l.*,
p.input_price,
p.output_price
FROM ai_logs l
JOIN model_pricing p ON l.model = p.model_name
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend pricing = genai_get_pricing(model)
```
## Usage
### Syntax
```kusto theme={null}
genai_get_pricing(model)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ------------------------------------------------------------------------------------------- |
| model | string | Yes | The name of the AI model (for example, 'gpt-4', 'claude-3-opus-20240229', 'gpt-3.5-turbo'). |
### Returns
Returns a dynamic object containing pricing information with the following structure:
```json theme={null}
{
"input_price_per_million": ,
"output_price_per_million":
}
```
Returns null if the model is not recognized.
## Example
Get pricing information for a GenAI model to understand cost structure.
**Query**
```kusto theme={null}
['genai-traces']
| extend model = ['attributes.gen_ai.request.model']
| extend pricing = genai_get_pricing(model)
| where isnotnull(pricing)
| summarize request_count = count() by model, pricing = tostring(pricing)
| top 3 by request_count
```
**Output**
| model | pricing | request\_count |
| ------------- | --------------------------------------------------------------------- | -------------- |
| gpt-4 | `{"input_price_per_million": 30.0, "output_price_per_million": 60.0}` | 450 |
| gpt-3.5-turbo | `{"input_price_per_million": 0.5, "output_price_per_million": 1.5}` | 1250 |
This query shows which models are being used and their associated pricing, helping teams make informed decisions.
## List of related functions
* [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost): Calculates total cost for a conversation. Use this for actual cost calculation after retrieving pricing information.
* [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost): Calculates input token cost. This function uses genai\_get\_pricing internally to determine input costs.
* [genai\_output\_cost](/apl/scalar-functions/genai-functions/genai-output-cost): Calculates output token cost. This function uses genai\_get\_pricing internally to determine output costs.
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates token count. Combine with pricing information to predict costs before making API calls.
# genai_get_role
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-get-role
This page explains how to use the genai_get_role function in APL.
The `genai_get_role` function retrieves the role of a message at a specific position in a GenAI messages array. This allows you to understand who sent a particular message in the conversation (user, assistant, system, tool, etc.).
You can use this function to validate conversation structure, analyze message patterns, verify conversation flow, or process conversations based on role sequences.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would use `mvindex` to access the role field at a specific position.
```sql Splunk example theme={null}
| eval message_role=mvindex(role, 2)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend message_role = genai_get_role(messages, 2)
```
In ANSI SQL, you would unnest the array and access the role at a specific offset.
```sql SQL example theme={null}
SELECT
conversation_id,
role as message_role
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS pos
WHERE pos = 2
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend message_role = genai_get_role(messages, 2)
```
## Usage
### Syntax
```kusto theme={null}
genai_get_role(messages, index)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
| index | long | Yes | The zero-based position of the message whose role you want to retrieve. Use 0 for the first message, 1 for the second, etc. |
### Returns
Returns a string containing the role of the message at the specified index (such as 'user', 'assistant', 'system', 'tool', 'function'), or an empty string if the index is out of bounds.
## Example
Get the role of the first message in a GenAI conversation.
**Query**
```kusto theme={null}
['genai-traces']
| extend first_role = genai_get_role(['attributes.gen_ai.input.messages'], 0)
| summarize conversations_with_system = countif(first_role == 'system'), total_conversations = count()
```
**Output**
| conversations\_with\_system | total\_conversations |
| --------------------------- | -------------------- |
| 1250 | 1450 |
This query verifies that most conversations are properly initialized with system prompts.
## List of related functions
* [genai\_get\_content\_by\_index](/apl/scalar-functions/genai-functions/genai-get-content-by-index): Gets content at a specific index. Combine with genai\_get\_role to understand both role and content at positions.
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Lists all roles in the conversation. Use this to get a complete picture of all roles rather than checking individual positions.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content filtered by role. Use this when you need content from a specific role type.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the total number of messages. Use this to validate index bounds before accessing positions.
# genai_has_tool_calls
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-has-tool-calls
This page explains how to use the genai_has_tool_calls function in APL.
The `genai_has_tool_calls` function checks whether a GenAI messages array contains any tool calls or function calls. It returns a boolean value indicating if the AI model requested to use external tools or functions during the conversation.
You can use this function to filter conversations that use function calling, monitor tool usage patterns, identify integration opportunities, or track feature adoption of function calling capabilities.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would check if tool-related fields exist in the messages.
```sql Splunk example theme={null}
| eval has_tools=if(isnotnull(tool_calls), "true", "false")
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend has_tools = genai_has_tool_calls(messages)
```
In ANSI SQL, you would check for existence of tool calls in the messages array.
```sql SQL example theme={null}
SELECT
conversation_id,
EXISTS(
SELECT 1 FROM UNNEST(messages)
WHERE JSON_EXTRACT(content, '$.tool_calls') IS NOT NULL
) as has_tools
FROM conversations
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend has_tools = genai_has_tool_calls(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_has_tool_calls(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a boolean value: `true` if the messages contain tool calls, `false` otherwise.
## Example
Check if a GenAI conversation contains any tool calls or function calls.
**Query**
```kusto theme={null}
['genai-traces']
| extend has_tools = genai_has_tool_calls(['attributes.gen_ai.input.messages'])
| summarize
conversations_with_tools = countif(has_tools),
total_conversations = count(),
adoption_rate = round(100.0 * countif(has_tools) / count(), 2)
```
**Output**
| conversations\_with\_tools | total\_conversations | adoption\_rate |
| -------------------------- | -------------------- | -------------- |
| 345 | 1450 | 23.79 |
This query tracks function calling adoption, helping you understand feature usage trends.
## List of related functions
* [genai\_extract\_tool\_calls](/apl/scalar-functions/genai-functions/genai-extract-tool-calls): Extracts the actual tool calls. Use this after confirming tool calls exist to analyze what tools are being called.
* [genai\_extract\_function\_results](/apl/scalar-functions/genai-functions/genai-extract-function-results): Extracts function results. Use this to analyze the outcomes of tool calls.
* [genai\_message\_roles](/apl/scalar-functions/genai-functions/genai-message-roles): Lists all message roles. Use this to understand conversation structure when tool calls are present.
* [genai\_conversation\_turns](/apl/scalar-functions/genai-functions/genai-conversation-turns): Counts conversation turns. Analyze this alongside tool usage to understand complexity.
# genai_input_cost
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-input-cost
This page explains how to use the genai_input_cost function in APL.
The `genai_input_cost` function calculates the cost of input tokens (prompt tokens) for a GenAI API call based on the model name and number of input tokens. This helps you understand and track the cost of prompts separately from responses.
You can use this function to analyze prompt costs, optimize prompt engineering for cost efficiency, track input spending separately, or create detailed cost breakdowns.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to lookup pricing and calculate costs manually.
```sql Splunk example theme={null}
| lookup model_pricing model OUTPUT input_price
| eval input_cost=(input_tokens * input_price / 1000000)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend input_cost = genai_input_cost(model, input_tokens)
```
In ANSI SQL, you would join with a pricing table and calculate input costs.
```sql SQL example theme={null}
SELECT
l.*,
(l.input_tokens * p.input_price / 1000000) as input_cost
FROM ai_logs l
JOIN model_pricing p ON l.model = p.model_name
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend input_cost = genai_input_cost(model, input_tokens)
```
## Usage
### Syntax
```kusto theme={null}
genai_input_cost(model, input_tokens)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------------------------------------------- |
| model | string | Yes | The name of the AI model (for example, 'gpt-4', 'claude-3-opus', 'gpt-3.5-turbo'). |
| input\_tokens | long | Yes | The number of input tokens (prompt tokens) used in the API call. |
### Returns
Returns a real number representing the cost in dollars (USD) for the input tokens based on the model's pricing.
## Example
Calculate the cost of input tokens for a GenAI chat operation.
**Query**
```kusto theme={null}
['genai-traces']
| extend model = ['attributes.gen_ai.request.model']
| extend input_tokens = tolong(['attributes.gen_ai.usage.input_tokens'])
| extend input_cost = genai_input_cost(model, input_tokens)
| summarize total_input_cost = sum(input_cost), avg_input_cost = avg(input_cost)
```
**Output**
| total\_input\_cost | avg\_input\_cost |
| ------------------ | ---------------- |
| 45.67 | 0.0187 |
This query calculates the total and average cost of input tokens, helping you understand prompt spending patterns.
## List of related functions
* [genai\_output\_cost](/apl/scalar-functions/genai-functions/genai-output-cost): Calculates output token cost. Use this alongside input costs to understand the full cost breakdown.
* [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost): Calculates total cost (input + output). Use this when you need combined costs.
* [genai\_get\_pricing](/apl/scalar-functions/genai-functions/genai-get-pricing): Gets pricing information. Use this to understand the pricing structure behind cost calculations.
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates token count from text. Combine with input cost to predict prompt costs before API calls.
# genai_is_truncated
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-is-truncated
This page explains how to use the genai_is_truncated function in APL.
The `genai_is_truncated` function checks whether an AI model response was truncated due to reaching token limits or other constraints. It analyzes the finish reason returned by the API to determine if the response was cut short.
You can use this function to identify incomplete responses, monitor quality issues, detect token limit problems, or track when conversations need continuation.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would check the finish\_reason field manually.
```sql Splunk example theme={null}
| eval is_truncated=if(finish_reason="length", "true", "false")
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend is_truncated = genai_is_truncated(messages, finish_reason)
```
In ANSI SQL, you would check the finish\_reason field value.
```sql SQL example theme={null}
SELECT
conversation_id,
CASE WHEN finish_reason = 'length' THEN true ELSE false END as is_truncated
FROM ai_logs
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend is_truncated = genai_is_truncated(messages, finish_reason)
```
## Usage
### Syntax
```kusto theme={null}
genai_is_truncated(messages, finish_reason)
```
### Parameters
| Name | Type | Required | Description |
| -------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
| finish\_reason | string | Yes | The finish reason returned by the AI API (such as 'stop', 'length', 'content\_filter', 'tool\_calls'). |
### Returns
Returns a boolean value: `true` if the response was truncated (typically when finish\_reason is 'length'), `false` otherwise.
## Example
Check if a GenAI response was truncated due to token limits.
**Query**
```kusto theme={null}
['genai-traces']
| extend finish_reason = ['attributes.gen_ai.response.finish_reasons']
| extend is_truncated = genai_is_truncated(['attributes.gen_ai.input.messages'], finish_reason)
| summarize
truncated_count = countif(is_truncated),
total_count = count(),
truncation_rate = round(100.0 * countif(is_truncated) / count(), 2)
```
**Output**
| truncated\_count | total\_count | truncation\_rate |
| ---------------- | ------------ | ---------------- |
| 45 | 1450 | 3.10 |
This query tracks the rate of truncated responses, helping you identify when token limits are causing quality issues.
## List of related functions
* [genai\_estimate\_tokens](/apl/scalar-functions/genai-functions/genai-estimate-tokens): Estimates token count. Use this to predict if responses might be truncated before making API calls.
* [genai\_conversation\_turns](/apl/scalar-functions/genai-functions/genai-conversation-turns): Counts conversation turns. Analyze this alongside truncation to understand context length issues.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts assistant responses. Use this to examine truncated responses.
* [strlen](/apl/scalar-functions/string-functions#strlen): Returns string length. Use this to analyze the length of truncated responses.
# genai_message_roles
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-message-roles
This page explains how to use the genai_message_roles function in APL.
The `genai_message_roles` function extracts an array of all message roles from a GenAI conversation. This provides a sequence view of conversation participants, showing the order and types of messages (user, assistant, system, tool, etc.).
You can use this function to analyze conversation patterns, validate conversation structure, detect role sequences, or understand conversation flow and complexity.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would extract the role field from all messages in an array.
```sql Splunk example theme={null}
| eval roles=mvindex(role, 0, mvcount(role))
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend roles = genai_message_roles(messages)
```
In ANSI SQL, you would unnest the array and collect roles into an array.
```sql SQL example theme={null}
SELECT
conversation_id,
ARRAY_AGG(role ORDER BY msg_index) as roles
FROM conversations
CROSS JOIN UNNEST(messages) WITH OFFSET AS msg_index
GROUP BY conversation_id
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend roles = genai_message_roles(messages)
```
## Usage
### Syntax
```kusto theme={null}
genai_message_roles(messages)
```
### Parameters
| Name | Type | Required | Description |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| messages | dynamic | Yes | An array of message objects from a GenAI conversation. Each message typically contains `role` and `content` fields. |
### Returns
Returns a dynamic array containing all the roles in the conversation in their original order (for example, `['system', 'user', 'assistant', 'user', 'assistant']`).
## Example
Extract all message roles from a GenAI conversation to understand the conversation structure.
**Query**
```kusto theme={null}
['genai-traces']
| extend roles = genai_message_roles(['attributes.gen_ai.input.messages'])
| extend role_sequence = tostring(roles)
| summarize conversation_count = count() by role_sequence
| top 5 by conversation_count
```
**Output**
| role\_sequence | conversation\_count |
| ------------------------------------------------- | ------------------- |
| \["system","user","assistant"] | 850 |
| \["system","user","assistant","user","assistant"] | 345 |
| \["user","assistant"] | 189 |
This query identifies the most common conversation patterns, helping you understand typical user interaction flows.
## List of related functions
* [genai\_get\_role](/apl/scalar-functions/genai-functions/genai-get-role): Gets the role at a specific index. Use this when you need a specific role rather than the full sequence.
* [genai\_conversation\_turns](/apl/scalar-functions/genai-functions/genai-conversation-turns): Counts conversation turns. Use this for a numerical metric of conversation length.
* [genai\_get\_content\_by\_role](/apl/scalar-functions/genai-functions/genai-get-content-by-role): Gets content for a specific role. Use this after identifying roles of interest.
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of messages. Apply this to the roles array to count messages.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Finds the position of a role. Use this to detect if specific roles exist in the conversation.
# genai_output_cost
Source: https://axiom.co/docs/apl/scalar-functions/genai-functions/genai-output-cost
This page explains how to use the genai_output_cost function in APL.
The `genai_output_cost` function calculates the cost of output tokens (completion tokens) for a GenAI API call based on the model name and number of output tokens. This helps you understand and track the cost of generated responses separately from prompts.
You can use this function to analyze generation costs, optimize response length for cost efficiency, track output spending separately, or create detailed cost breakdowns.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you would need to lookup pricing and calculate costs manually.
```sql Splunk example theme={null}
| lookup model_pricing model OUTPUT output_price
| eval output_cost=(output_tokens * output_price / 1000000)
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend output_cost = genai_output_cost(model, output_tokens)
```
In ANSI SQL, you would join with a pricing table and calculate output costs.
```sql SQL example theme={null}
SELECT
l.*,
(l.output_tokens * p.output_price / 1000000) as output_cost
FROM ai_logs l
JOIN model_pricing p ON l.model = p.model_name
```
```kusto APL equivalent theme={null}
['ai-logs']
| extend output_cost = genai_output_cost(model, output_tokens)
```
## Usage
### Syntax
```kusto theme={null}
genai_output_cost(model, output_tokens)
```
### Parameters
| Name | Type | Required | Description |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------- |
| model | string | Yes | The name of the AI model (for example, 'gpt-4', 'claude-3-opus', 'gpt-3.5-turbo'). |
| output\_tokens | long | Yes | The number of output tokens (completion tokens) generated by the API call. |
### Returns
Returns a real number representing the cost in dollars (USD) for the output tokens based on the model's pricing.
## Example
Calculate the cost of output tokens for a GenAI chat operation.
**Query**
```kusto theme={null}
['genai-traces']
| extend model = ['attributes.gen_ai.response.model']
| extend output_tokens = tolong(['attributes.gen_ai.usage.output_tokens'])
| extend output_cost = genai_output_cost(model, output_tokens)
| summarize total_output_cost = sum(output_cost), avg_output_cost = avg(output_cost)
```
**Output**
| total\_output\_cost | avg\_output\_cost |
| ------------------- | ----------------- |
| 78.34 | 0.0321 |
This query calculates the total and average cost of output tokens, helping you understand generation spending patterns.
## List of related functions
* [genai\_input\_cost](/apl/scalar-functions/genai-functions/genai-input-cost): Calculates input token cost. Use this alongside output costs to understand the full cost breakdown.
* [genai\_cost](/apl/scalar-functions/genai-functions/genai-cost): Calculates total cost (input + output). Use this when you need combined costs.
* [genai\_get\_pricing](/apl/scalar-functions/genai-functions/genai-get-pricing): Gets pricing information. Use this to understand the pricing structure behind cost calculations.
* [genai\_extract\_assistant\_response](/apl/scalar-functions/genai-functions/genai-extract-assistant-response): Extracts the response text. Combine with output costs to analyze cost per response.
* [genai\_is\_truncated](/apl/scalar-functions/genai-functions/genai-is-truncated): Checks if responses were truncated. Use this to understand if token limits affected output costs.
# Hash functions
Source: https://axiom.co/docs/apl/scalar-functions/hash-functions
Learn how to use and combine various hash functions in APL
The table summarizes the hash functions available in APL.
| Name | Description |
| ------------------------------------------------- | -------------------------------------------------- |
| [hash](/apl/scalar-functions/hash-functions/hash) | Returns a signed integer hash for the input value. |
| [hash\_md5](#hash-md5) | Returns a MD5 hash value for the input value. |
| [hash\_sha1](#hash-sha1) | Returns a sha1 hash value for the input value. |
| [hash\_sha256](#hash-sha256) | Returns a SHA256 hash value for the input value. |
| [hash\_sha512](#hash-sha512) | Returns a SHA512 hash value for the input value. |
## hash\_md5
Returns an MD5 hash value for the input value.
### Arguments
* source: The value to be hashed.
### Returns
The MD5 hash value of the given scalar, encoded as a hex string (a string of characters, each two of which represent a single Hex number between 0 and 255).
### Examples
```kusto theme={null}
hash_md5(source)
```
```kusto theme={null}
['sample-http-logs']
| project md5_hash_value = hash_md5(content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20md5_hash_value%20%3D%20hash_md5%28content_type%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"md5_hash_value": "b980a9c041dbd33d5893fad65d33284b"
}
```
## hash\_sha1
Returns a SHA1 hash value for the input value.
### Arguments
* source: The value to be hashed.
### Returns
The sha1 hash value of the given scalar, encoded as a hex string
### Examples
```kusto theme={null}
hash_sha1(source)
```
```kusto theme={null}
['sample-http-logs']
| project sha1_hash_value = hash_sha1(content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20sha1_hash_value%20%3D%20hash_sha1%28content_type%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"sha1_hash_value": "9f9af029585ba014e07cd3910ca976cf56160616"
}
```
## hash\_sha256
Returns a SHA256 hash value for the input value.
### Arguments
* source: The value to be hashed.
### Returns
The sha256 hash value of the given scalar, encoded as a hex string (a string of characters, each two of which represent a single Hex number between 0 and 255).
### Examples
```kusto theme={null}
hash_sha256(source)
```
```kusto theme={null}
['sample-http-logs']
| project sha256_hash_value = hash_sha256(content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20sha256_hash_value%20%3D%20hash_sha256%28content_type%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"sha256_hash_value": "bb4770ff4ac5b7d2be41a088cb27d8bcaad53b574b6f27941e8e48e9e10fc25a"
}
```
## hash\_sha512
Returns a SHA512 hash value for the input value.
### Arguments
* source: The value to be hashed.
### Returns
The sha512 hash value of the given scalar, encoded as a hex string (a string of characters, each two of which represent a single Hex number between 0 and 511).
### Examples
```kusto theme={null}
hash_sha512(source)
```
```kusto theme={null}
['sample-http-logs']
| project sha512_hash_value = hash_sha512(status)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20sha512_hash_value%20%3D%20hash_sha512%28status%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"sha512_hash_value": "0878a61b503dd5a9fe9ea3545d6d3bd41c3b50a47f3594cb8bbab3e47558d68fc8fcc409cd0831e91afc4e609ef9da84e0696c50354ad86b25f2609efef6a834"
}
```
***
```kusto theme={null}
['sample-http-logs']
| project sha512_hash_value = hash_sha512(content_type)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20sha512_hash_value%20%3D%20hash_sha512%28content_type%29%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2230d%22%7D%7D)
* Result
```json theme={null}
{
"sha512_hash_value": "95c6eacdd41170b129c3c287cfe088d4fafea34e371422b94eb78b9653a89d4132af33ef39dd6b3d80e18c33b21ae167ec9e9c2d820860689c647ffb725498c4"
}
```
# hash
Source: https://axiom.co/docs/apl/scalar-functions/hash-functions/hash
This page explains how to use the hash function in APL.
Use the `hash` scalar function to transform any data type as a string of bytes into a signed integer. The result is deterministic so the value is always identical given the same input data.
Use the `hash` function to:
* Anonymise personally identifiable information (PII) while preserving joinability.
* Create reproducible buckets for sampling, sharding, or load-balancing.
* Build low-cardinality keys for fast aggregation and look-ups.
* You need a reversible-by-key surrogate or a quick way to distribute rows evenly.
Don’t use `hash` to generate values for long term usage. `hash` is generic and the underlying hashing algorithm may change. For long term stability, use the [other hash functions](/apl/scalar-functions/hash-functions) with specific algorithm like `hash_sha1`.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk’s `hash` (or `md5`, `sha1`, etc.) returns a hexadecimal string and lets you pick an algorithm. In APL `hash` always returns a 64-bit integer that trades cryptographic strength for speed and compactness. Use `hash_sha256` if you need a cryptographically secure digest.
```sql Splunk example theme={null}
... | eval anon_id = md5(id) | stats count by anon_id
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend anon_id = hash(id)
| summarize count() by anon_id
```
Standard SQL often exposes vendor-specific functions such as `HASH` (BigQuery), `HASH_BYTES` (SQL Server), or `MD5`. These return either bytes or hex strings. In APL `hash` always yields an `int64`. To emulate SQL’s modulo bucketing, pipe the result into the arithmetic operator that you need.
```sql SQL example theme={null}
SELECT HASH(id) % 10 AS bucket, COUNT(*) AS requests
FROM sample_http_logs
GROUP BY bucket
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend bucket = abs(hash(id) % 10)
| summarize requests = count() by bucket
```
## Usage
### Syntax
```kusto theme={null}
hash(source [, salt])
```
### Parameters
| Name | Type | Description |
| ----------- | ------ | ----------------------------------------------------------------------------------------- |
| valsourceue | scalar | Any scalar expression except `real`. |
| salt | `int` | (Optional) Salt that lets you derive a different 64-bit domain while keeping determinism. |
### Returns
The signed integer hash of `source` (and `salt` if supplied).
## Use case examples
Hash requesters to see your busiest anonymous users.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend anon_id = hash(id)
| summarize requests = count() by anon_id
| top 5 by requests
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20anon_id%20%3D%20hash%28id%29%20%7C%20summarize%20requests%20%3D%20count%28%29%20by%20anon_id%20%7C%20top%205%20by%20requests%22%7D)
**Output**
| anon\_id | requests |
| -------------------- | -------- |
| -5872831405421830129 | 128 |
| 902175364502087611 | 97 |
| -354879610945237854 | 85 |
| 6423087105927348713 | 74 |
| -919087345721004317 | 69 |
The query replaces raw IDs with hashed surrogates, counts requests per surrogate, then lists the five most active requesters without exposing PII.
Hash trace IDs to see which anonymous trace has the most spans.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_bucket = hash(trace_id)
| summarize spans = count() by trace_bucket
| sort by spans desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20trace_bucket%20%3D%20hash\(trace_id\)%20%7C%20summarize%20spans%20%3D%20count\(\)%20by%20trace_bucket%20%7C%20sort%20by%20spans%20desc%22%7D)
**Output**
| trace\_bucket | spans |
| -------------------------- | ----- |
| 8,858,860,617,655,667,000 | 62 |
| 4,193,515,424,067,409,000 | 62 |
| 1,779,014,838,419,064,000 | 62 |
| 5,399,024,001,804,211,000 | 62 |
| -2,480,347,067,347,939,000 | 62 |
Group suspicious endpoints without leaking the exact URI.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend uri_hash = hash(uri)
| summarize requests = count() by uri_hash, status
| top 10 by requests
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20uri_hash%20%3D%20hash%28uri%29%20%7C%20summarize%20requests%20%3D%20count%28%29%20by%20uri_hash%2C%20status%20%7C%20top%2010%20by%20requests%22%7D)
**Output**
| uri\_hash | status | requests |
| ------------------- | ------ | -------- |
| -123640987553821047 | 404 | 230 |
| 4385902145098764321 | 403 | 145 |
| -85439034872109873 | 401 | 132 |
| 493820743209857311 | 404 | 129 |
| -90348122345872001 | 500 | 118 |
The query hides sensitive path information yet still lets you see which hashed endpoints return the most errors.
# IP functions
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions
This section explains how to use IP functions in APL.
The table summarizes the IP functions available in APL.
| Function | Description |
| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| [format\_ipv4](/apl/scalar-functions/ip-functions/format-ipv4) | Parses input with a netmask and returns string representing IPv4 address. |
| [format\_ipv4\_mask](/apl/scalar-functions/ip-functions/format-ipv4-mask) | Formats an IPv4 address and a bitmask into CIDR notation. |
| [geo\_info\_from\_ip\_address](/apl/scalar-functions/ip-functions/geo-info-from-ip-address) | Extracts geographical, geolocation, and network information from IP addresses. |
| [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4) | Returns a Boolean value indicating whether the specified column contains any of the given IPv4 addresses. |
| [has\_any\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-any-ipv4-prefix) | Returns a Boolean value indicating whether the IPv4 address matches any of the specified prefixes. |
| [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4) | Returns a Boolean value indicating whether the given IPv4 address is valid and found in the source text. |
| [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix) | Returns a Boolean value indicating whether the given IPv4 address starts with a specified prefix. |
| [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare) | Compares two IPv4 addresses. |
| [ipv4\_is\_in\_any\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-any-range) | Returns a Boolean value indicating whether the given IPv4 address is in any specified range. |
| [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range) | Checks if IPv4 string address is in IPv4-prefix notation range. |
| [ipv4\_is\_match](/apl/scalar-functions/ip-functions/ipv4-is-match) | Returns a Boolean value indicating whether the given IPv4 matches the specified pattern. |
| [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private) | Checks if IPv4 string address belongs to a set of private network IPs. |
| [ipv4\_netmask\_suffix](/apl/scalar-functions/ip-functions/ipv4-netmask-suffix) | Returns the value of the IPv4 netmask suffix from IPv4 string address. |
| [ipv6\_compare](/apl/scalar-functions/ip-functions/ipv6-compare) | Compares two IPv6 addresses. |
| [ipv6\_is\_in\_any\_range](/apl/scalar-functions/ip-functions/ipv6-is-in-any-range) | Returns a Boolean value indicating whether the given IPv6 address is in any specified range. |
| [ipv6\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv6-is-in-range) | Checks if IPv6 string address is in IPv6-prefix notation range. |
| [ipv6\_is\_match](/apl/scalar-functions/ip-functions/ipv6-is-match) | Returns a Boolean value indicating whether the given IPv6 matches the specified pattern. |
| [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4) | Converts input to long (signed 64-bit) number representation. |
| [parse\_ipv4\_mask](/apl/scalar-functions/ip-functions/parse-ipv4-mask) | Converts input string and IP-prefix mask to long (signed 64-bit) number representation. |
## IP-prefix notation
You can define IP addresses with IP-prefix notation using a slash (`/`) character. The IP address to the left of the slash is the base IP address. The number (1 to 32) to the right of the slash is the number of contiguous bits in the netmask. For example, `192.168.2.0/24` has an associated net/subnetmask containing 24 contiguous bits or `255.255.255.0` in dotted decimal format.
# format_ipv4
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/format-ipv4
This page explains how to use the format_ipv4 function in APL.
The `format_ipv4` function in APL converts a numeric representation of an IPv4 address into its standard dotted-decimal format. This function is particularly useful when working with logs or datasets where IP addresses are stored as integers, making them hard to interpret directly.
You can use `format_ipv4` to enhance log readability, enrich security logs, or convert raw telemetry data for analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, IPv4 address conversion is typically not a built-in function. You may need to use custom scripts or calculations. APL simplifies this process with the `format_ipv4` function.
ANSI SQL doesn’t have a built-in function for IPv4 formatting. You’d often use string manipulation or external utilities to achieve the same result. In APL, `format_ipv4` offers a straightforward solution.
## Usage
### Syntax
```kusto theme={null}
format_ipv4(ipv4address)
```
### Parameters
* `ipv4address`: A `long` numeric representation of the IPv4 address in network byte order.
### Returns
* Returns a string representing the IPv4 address in dotted-decimal format.
* Returns an empty string if the conversion fails.
## Use case example
When analyzing HTTP request logs, you can convert IP addresses stored as integers into a readable format to identify client locations or troubleshoot issues.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend formatted_ip = format_ipv4(3232235776)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20formatted_ip%20%3D%20format_ipv4\(3232235776\)%22%7D)
**Output**
| \_time | formatted\_ip | status | uri | method |
| ------------------- | ------------- | ------ | ------------- | ------ |
| 2024-11-14 10:00:00 | 192.168.1.0 | 200 | /api/products | GET |
This query decodes raw IP addresses into a human-readable format for easier analysis.
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# format_ipv4_mask
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/format-ipv4-mask
This page explains how to use the format_ipv4_mask function in APL.
Use the `format_ipv4_mask` function to format an IPv4 address and a bitmask into Classless Inter-Domain Routing (CIDR) notation. This function is useful when you need to standardize or analyze network addresses, especially in datasets that contain raw IPs or numerical IP representations. It supports both string-based and numeric IPv4 inputs and can apply an optional prefix to generate a subnet mask.
You can use `format_ipv4_mask` to normalize IP addresses, extract network segments, or apply filtering or grouping logic based on subnet granularity.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
SPL doesn’t have a direct built-in equivalent to `format_ipv4_mask`. To format IPv4 addresses with subnet masks, you typically use custom field extractions or external lookup tables. In contrast, APL provides a native function for this task, simplifying analysis at the network or subnet level.
```sql Splunk example theme={null}
| eval cidr=ip."/24"
```
```kusto APL equivalent theme={null}
format_ipv4_mask('192.168.1.10', 24)
```
Standard SQL lacks native functions for manipulating IP addresses or CIDR notation. This type of transformation usually requires application-side logic or user-defined functions (UDFs). APL simplifies this by offering a first-class function for formatting IPs directly in queries.
```sql SQL example theme={null}
-- Requires custom UDF or external processing
SELECT format_ip_with_mask(ip, 24) FROM connections
```
```kusto APL equivalent theme={null}
format_ipv4_mask(ip, 24)
```
## Usage
### Syntax
```kusto theme={null}
format_ipv4_mask(ip, prefix)
```
### Parameters
| Name | Type | Required | Description |
| ------ | ------ | -------- | ------------------------------------------------------------------------------------------------------- |
| ip | string | ✓ | The IPv4 address in CIDR notation. You can use a string (e.g., `'192.168.1.1'`) or a big-endian number. |
| prefix | int | ✓ | An integer between 0 and 32. Specifies how many leading bits to include in the mask. |
### Returns
A string representing the IPv4 address in CIDR notation if the conversion succeeds. If the conversion fails, the function returns an empty string.
## Example
**Query**
```kusto theme={null}
['sample-http-logs']
| extend subnet = format_ipv4_mask('192.168.1.54', 24)
| project _time, subnet
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20subnet%20%3D%20format_ipv4_mask\('192.168.1.54'%2C%2024\)%20%7C%20project%20_time%2C%20subnet%22%7D)
**Output**
| \_time | subnet |
| ----------------- | -------------- |
| 1Jun 30, 11:11:46 | 192.168.1.0/24 |
## List of related functions
* [format\_ipv4](/apl/scalar-functions/ip-functions/format-ipv4): Converts a 32-bit unsigned integer to an IPv4 address string. Use it when your input is a raw numeric IP instead of a prefix length.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Parses an IPv4 string into a numeric representation. Use it when you want to do arithmetic or masking on IP addresses.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks whether an IPv4 address falls within a given range. Use it when you need to filter or classify IPs against subnets.
# geo_info_from_ip_address
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/geo-info-from-ip-address
This page explains how to use the geo_info_from_ip_address function in APL.
The `geo_info_from_ip_address` function in APL retrieves geographic information based on an IP address. It maps an IP address to attributes such as city, region, and country, allowing you to perform location-based analytics on your datasets. This function is particularly useful for analyzing web logs, security events, and telemetry data to uncover geographic trends or detect anomalies based on location.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, the equivalent process often involves using lookup tables or add-ons to resolve IP addresses into geographic details. In APL, `geo_info_from_ip_address` performs the resolution natively within the query, streamlining the workflow.
```sql Splunk example theme={null}
| eval geo_info = iplocation(client_ip)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend geo_info = geo_info_from_ip_address(client_ip)
```
In SQL, geographic information retrieval typically requires a separate database or API integration. In APL, the `geo_info_from_ip_address` function directly provides geographic details, simplifying the query process.
```sql SQL example theme={null}
SELECT ip_to_location(client_ip) AS geo_info
FROM sample_http_logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend geo_info = geo_info_from_ip_address(client_ip)
```
## Usage
### Syntax
```kusto theme={null}
geo_info_from_ip_address(ip_address)
```
### Parameters
| Parameter | Type | Description |
| ------------ | ------ | ------------------------------------------------------------ |
| `ip_address` | string | The IP address for which to retrieve geographic information. |
### Returns
A dynamic object containing the IP address’s geographic attributes (if available). The object contains the following fields:
| Name | Type | Description |
| ------------ | ------ | -------------------------------------------- |
| country | string | Country name |
| state | string | State (subdivision) name |
| city | string | City name |
| latitude | real | Latitude coordinate |
| longitude | real | Longitude coordinate |
| country\_iso | string | ISO code of the country |
| time\_zone | string | Time zone in which the IP address is located |
## Use case example
Use geographic data to analyze web log traffic.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend geo_info = geo_info_from_ip_address('172.217.22.14')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20geo_info%20%3D%20geo_info_from_ip_address\('172.217.22.14'\)%22%7D)
**Output**
```json geo_info theme={null}
{
"state": "",
"longitude": -97.822,
"latitude": 37.751,
"country_iso": "US",
"country": "United States",
"city": "",
"time_zone": "America/Chicago"
}
```
This query identifies the geographic location of the IP address `172.217.22.14`.
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
## IPv4 Examples
### Extract geolocation information from IPv4 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('172.217.11.4')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%27172.217.11.4%27%29%22%7D)
### Project geolocation information from IPv4 address
```kusto theme={null}
['sample-http-logs']
| project ip_location=geo_info_from_ip_address('20.53.203.50')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20ip_location%3Dgeo_info_from_ip_address%28%2720.53.203.50%27%29%22%7D)
### Filter geolocation information from IPv4 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('20.53.203.50')
| where ip_location.country == "Australia" and ip_location.country_iso == "AU" and ip_location.state == "New South Wales"
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%2720.53.203.50%27%29%5Cn%7C%20where%20ip_location.country%20%3D%3D%20%5C%22Australia%5C%22%20and%20ip_location.country_iso%20%3D%3D%20%5C%22AU%5C%22%20and%20ip_location.state%20%3D%3D%20%5C%22New%20South%20Wales%5C%22%22%7D)
### Group geolocation information from IPv4 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('20.53.203.50')
| summarize Count=count() by ip_location.state, ip_location.city, ip_location.latitude, ip_location.longitude
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%2720.53.203.50%27%29%5Cn%7C%20summarize%20Count%3Dcount%28%29%20by%20ip_location.state%2C%20ip_location.city%2C%20ip_location.latitude%2C%20ip_location.longitude%22%7D)
## IPv6 Examples
### Extract geolocation information from IPv6 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('2607:f8b0:4005:805::200e')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%272607%3Af8b0%3A4005%3A805%3A%3A200e%27%29%22%7D)
### Project geolocation information from IPv6 address
```kusto theme={null}
['sample-http-logs']
| project ip_location=geo_info_from_ip_address('2a03:2880:f12c:83:face:b00c::25de')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20ip_location%3Dgeo_info_from_ip_address%28%272a03%3A2880%3Af12c%3A83%3Aface%3Ab00c%3A%3A25de%27%29%22%7D)
### Filter geolocation information from IPv6 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('2a03:2880:f12c:83:face:b00c::25de')
| where ip_location.country == "United States" and ip_location.country_iso == "US" and ip_location.state == "Florida"
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%272a03%3A2880%3Af12c%3A83%3Aface%3Ab00c%3A%3A25de%27%29%5Cn%7C%20where%20ip_location.country%20%3D%3D%20%5C%22United%20States%5C%22%20and%20ip_location.country_iso%20%3D%3D%20%5C%22US%5C%22%20and%20ip_location.state%20%3D%3D%20%5C%22Florida%5C%22%22%7D)
### Group geolocation information from IPv6 address
```kusto theme={null}
['sample-http-logs']
| extend ip_location = geo_info_from_ip_address('2a03:2880:f12c:83:face:b00c::25de')
| summarize Count=count() by ip_location.state, ip_location.city, ip_location.latitude, ip_location.longitude
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%20%22%5B%27sample-http-logs%27%5D%5Cn%7C%20extend%20ip_location%20%3D%20geo_info_from_ip_address%28%272a03%3A2880%3Af12c%3A83%3Aface%3Ab00c%3A%3A25de%27%29%5Cn%7C%20summarize%20Count%3Dcount%28%29%20by%20ip_location.state%2C%20ip_location.city%2C%20ip_location.latitude%2C%20ip_location.longitude%22%7D)
# has_any_ipv4
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/has-any-ipv4
This page explains how to use the has_any_ipv4 function in APL.
The `has_any_ipv4` function in Axiom Processing Language (APL) allows you to check whether a specified column contains any IPv4 addresses from a given set of IPv4 addresses or CIDR ranges. This function is useful when analyzing logs, tracing OpenTelemetry data, or investigating security events to quickly filter records based on a predefined list of IP addresses or subnets.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you typically use the `cidrmatch` or similar functions for working with IP ranges. In APL, `has_any_ipv4` offers similar functionality by matching any IPv4 address in a column against multiple values or ranges.
```sql Splunk example theme={null}
| where cidrmatch("192.168.1.0/24", ip_field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_any_ipv4('ip_field', dynamic(['192.168.1.0/24']))
```
SQL doesn’t natively support CIDR matching or IP address comparison out of the box. In APL, the `has_any_ipv4` function is designed to simplify these checks with concise syntax.
```sql SQL example theme={null}
SELECT * FROM logs WHERE ip_field = '192.168.1.1' OR ip_field = '192.168.1.2';
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_any_ipv4('ip_field', dynamic(['192.168.1.1', '192.168.1.2']))
```
## Usage
### Syntax
```kusto theme={null}
has_any_ipv4(column, ip_list)
```
### Parameters
| Parameter | Description | Type |
| --------- | ---------------------------------------- | --------- |
| `column` | The column to evaluate. | `string` |
| `ip_list` | A list of IPv4 addresses or CIDR ranges. | `dynamic` |
### Returns
A boolean value indicating whether the specified column contains any of the given IPv4 addresses or matches any of the CIDR ranges in `ip_list`.
## Use case example
When analyzing logs, you can use `has_any_ipv4` to filter requests from specific IPv4 addresses or subnets.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_ip = has_any_ipv4('192.168.1.1', dynamic(['192.168.1.1', '192.168.0.0/16']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20has_ip%20%3D%20has_any_ipv4\('192.168.1.1'%2C%20dynamic\(%5B'192.168.1.1'%2C%20'192.168.0.0%2F16'%5D\)\)%22%7D)
**Output**
| \_time | has\_ip | status |
| ------------------- | ------- | ------ |
| 2024-11-14T10:00:00 | true | 200 |
This query identifies log entries from specific IPs or subnets.
## List of related functions
* [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix): Checks if an IPv4 address matches a single prefix.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
# has_any_ipv4_prefix
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/has-any-ipv4-prefix
This page explains how to use the has_any_ipv4_prefix function in APL.
The `has_any_ipv4_prefix` function in APL lets you determine if an IPv4 address starts with any prefix in a list of specified prefixes. This function is particularly useful for filtering, segmenting, and analyzing data involving IP addresses, such as log data, network traffic, or security events. By efficiently checking prefixes, you can identify IP ranges of interest for purposes like geolocation, access control, or anomaly detection.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, checking if an IP address matches a prefix requires custom search logic with pattern matching or conditional expressions. In APL, `has_any_ipv4_prefix` provides a direct and optimized way to perform this check.
```sql Splunk example theme={null}
| eval is_in_range=if(match(ip, "10.*") OR match(ip, "192.168.*"), 1, 0)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_any_ipv4_prefix(uri, dynamic(['10.', '192.168.']))
```
In ANSI SQL, you need to use `LIKE` clauses combined with `OR` operators to check prefixes. In APL, the `has_any_ipv4_prefix` function simplifies this process by accepting a dynamic list of prefixes.
```sql SQL example theme={null}
SELECT * FROM logs
WHERE ip LIKE '10.%' OR ip LIKE '192.168.%';
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_any_ipv4_prefix(uri, dynamic(['10.', '192.168.']))
```
## Usage
### Syntax
```kusto theme={null}
has_any_ipv4_prefix(ip_column, prefixes)
```
### Parameters
| Parameter | Type | Description |
| ----------- | --------- | ----------------------------------------- |
| `ip_column` | `string` | The column containing the IPv4 address. |
| `prefixes` | `dynamic` | A list of IPv4 prefixes to check against. |
### Returns
* `true` if the IPv4 address matches any of the specified prefixes.
* `false` otherwise.
## Use case example
Detect requests from specific IP ranges.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_ip_prefix = has_any_ipv4_prefix('192.168.0.1', dynamic(['172.16.', '192.168.']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20has_ip_prefix%20%3D%20has_any_ipv4_prefix\('192.168.0.1'%2C%20dynamic\(%5B'172.16.'%2C%20'192.168.'%5D\)\)%22%7D)
**Output**
| \_time | has\_ip\_prefix | status |
| ------------------- | --------------- | ------ |
| 2024-11-14T10:00:00 | true | 200 |
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix): Checks if an IPv4 address matches a single prefix.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
# has_ipv4
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/has-ipv4
This page explains how to use the has_ipv4 function in APL.
## Introduction
The `has_ipv4` function in Axiom Processing Language (APL) allows you to check if a specified IPv4 address appears in a given text. The function is useful for tasks such as analyzing logs, monitoring security events, and processing network data where you need to identify or filter entries based on IP addresses.
To use `has_ipv4`, ensure that IP addresses in the text are properly delimited with non-alphanumeric characters. For example:
* **Valid:** `192.168.1.1` in `"Requests from: 192.168.1.1, 10.1.1.115."`
* **Invalid:** `192.168.1.1` in `"192.168.1.1ThisText"`
The function returns `true` if the IP address is valid and present in the text. Otherwise, it returns `false`.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use `match` or similar regex-based functions to locate IPv4 addresses in a string. In APL, `has_ipv4` provides a simpler and more efficient alternative for detecting specific IPv4 addresses.
```sql Splunk example theme={null}
search sourcetype=access_combined | eval isPresent=match(_raw, "192\.168\.1\.1")
```
```kusto APL equivalent theme={null}
print result=has_ipv4('05:04:54 192.168.1.1 GET /favicon.ico 404', '192.168.1.1')
```
In ANSI SQL, locating IPv4 addresses often involves string manipulation or pattern matching with `LIKE` or regular expressions. APL’s `has_ipv4` function provides a more concise and purpose-built approach.
```sql SQL example theme={null}
SELECT CASE WHEN column_text LIKE '%192.168.1.1%' THEN TRUE ELSE FALSE END AS result
FROM log_table;
```
```kusto APL equivalent theme={null}
print result=has_ipv4('05:04:54 192.168.1.1 GET /favicon.ico 404', '192.168.1.1')
```
## Usage
### Syntax
```kusto theme={null}
has_ipv4(source, ip_address)
```
### Parameters
| Name | Type | Description |
| ------------ | ------ | --------------------------------------------------- |
| `source` | string | The source text where to search for the IP address. |
| `ip_address` | string | The IP address to look for in the source. |
### Returns
* `true` if `ip_address` is a valid IP address and is found in `source`.
* `false` otherwise.
## Use case example
Identify requests coming from a specific IP address in HTTP logs.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_ip = has_ipv4('Requests from: 192.168.1.1, 10.1.1.115.', '192.168.1.1')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20has_ip%20%3D%20has_ipv4\('Requests%20from%3A%20192.168.1.1%2C%2010.1.1.115.'%2C%20'192.168.1.1'\)%22%7D)
**Output**
| \_time | has\_ip | status |
| ------------------- | ------- | ------ |
| 2024-11-14T10:00:00 | true | 200 |
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix): Checks if an IPv4 address matches a single prefix.
# has_ipv4_prefix
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/has-ipv4-prefix
This page explains how to use the has_ipv4_prefix function in APL.
The `has_ipv4_prefix` function checks if an IPv4 address starts with a specified prefix. Use this function to filter or match IPv4 addresses efficiently based on their prefixes. It’s particularly useful when analyzing network traffic, identifying specific address ranges, or working with CIDR-based IP filtering in datasets.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use string-based matching or CIDR functions for IP comparison. In APL, `has_ipv4_prefix` simplifies the process by directly comparing an IP against a prefix.
```sql Splunk example theme={null}
| eval is_match = if(cidrmatch("192.168.0.0/24", ip), true, false)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_ipv4_prefix(uri, "192.168.0")
```
In ANSI SQL, there is no direct equivalent to `has_ipv4_prefix`. You would typically use substring or LIKE operators for partial matching. APL provides a dedicated function for this purpose, ensuring simplicity and accuracy.
```sql SQL example theme={null}
SELECT *
FROM sample_http_logs
WHERE ip LIKE '192.168.0%'
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where has_ipv4_prefix(uri, "192.168.0")
```
## Usage
### Syntax
```kusto theme={null}
has_ipv4_prefix(column_name, prefix)
```
### Parameters
| Parameter | Type | Description |
| ------------- | ------ | --------------------------------------------------------------- |
| `column_name` | string | The column containing the IPv4 addresses to evaluate. |
| `prefix` | string | The prefix to check for, expressed as a string (e.g., "192.0"). |
### Returns
* Returns a Boolean (`true` or `false`) indicating whether the IPv4 address starts with the specified prefix.
## Use case example
Use `has_ipv4_prefix` to filter logs for requests originating from a specific IP range.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_prefix= has_ipv4_prefix('192.168.0.1', '192.168.')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20has_prefix%3D%20has_ipv4_prefix\('192.168.0.1'%2C%20'192.168.'\)%22%7D)
**Output**
| \_time | has\_prefix | status |
| ------------------- | ----------- | ------ |
| 2024-11-14T10:00:00 | true | 200 |
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
# ipv4_compare
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-compare
This page explains how to use the ipv4_compare function in APL.
The `ipv4_compare` function in APL allows you to compare two IPv4 addresses lexicographically or numerically. This is useful for sorting IP addresses, validating CIDR ranges, or detecting overlaps between IP ranges. It’s particularly helpful in analyzing network logs, performing security investigations, and managing IP-based filters or rules.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, similar functionality can be achieved using `sort` or custom commands. In APL, `ipv4_compare` is a dedicated function for comparing two IPv4 addresses.
```sql Splunk example theme={null}
| eval comparison = if(ip1 < ip2, -1, if(ip1 == ip2, 0, 1))
```
```kusto APL equivalent theme={null}
| extend comparison = ipv4_compare(ip1, ip2)
```
In ANSI SQL, you might manually parse or order IP addresses as strings. In APL, `ipv4_compare` simplifies this task with built-in support for IPv4 comparison.
```sql SQL example theme={null}
SELECT CASE
WHEN ip1 < ip2 THEN -1
WHEN ip1 = ip2 THEN 0
ELSE 1
END AS comparison
FROM ips;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend comparison = ipv4_compare(ip1, ip2)
```
## Usage
### Syntax
```kusto theme={null}
ipv4_compare(ip1, ip2)
```
### Parameters
| Parameter | Type | Description |
| --------- | ------ | ----------------------------------- |
| `ip1` | string | The first IPv4 address to compare. |
| `ip2` | string | The second IPv4 address to compare. |
### Returns
* Returns `1` if the long representation of `ip1` is greater than the long representation of `ip2`
* Returns `0` if the long representation of `ip1` is equal to the long representation of `ip2`
* Returns `-1` if the long representation of `ip1` is less than the long representation of `ip2`
* Returns `null` if the conversion fails.
## Use case example
You can use `ipv4_compare` to sort logs based on IP addresses or to identify connections between specific IPs.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ip1 = '192.168.1.1', ip2 = '192.168.1.10'
| extend comparison = ipv4_compare(ip1, ip2)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20ip1%20%3D%20%27192.168.1.1%27%2C%20ip2%20%3D%20%27192.168.1.10%27%20%7C%20extend%20comparison%20%3D%20ipv4_compare\(ip1%2C%20ip2\)%22%7D)
**Output**
| ip1 | ip2 | comparison |
| ----------- | ------------ | ---------- |
| 192.168.1.1 | 192.168.1.10 | -1 |
This query compares two hardcoded IP addresses. It returns `-1`, indicating that `192.168.1.1` is lexicographically less than `192.168.1.10`.
## List of related functions
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# ipv4_is_in_any_range
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-is-in-any-range
This page explains how to use the ipv4_is_in_any_range function in APL.
The `ipv4_is_in_any_range` function checks whether a given IPv4 address belongs to any range of IPv4 subnets. You can use it to evaluate whether an IP address falls within a set of CIDR blocks or IP ranges, which is useful for filtering, monitoring, or analyzing network traffic in your datasets.
This function is particularly helpful for security monitoring, analyzing log data for specific geolocated traffic, or validating access based on allowed IP ranges.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `cidrmatch` to check if an IP belongs to a range. In APL, `ipv4_is_in_any_range` is equivalent, but it supports evaluating against multiple ranges simultaneously.
```sql Splunk example theme={null}
| eval is_in_range = cidrmatch("192.168.0.0/24", ip_address)
```
```kusto APL equivalent theme={null}
['dataset']
| extend is_in_range = ipv4_is_in_any_range(ip_address, dynamic(['192.168.0.0/24', '10.0.0.0/8']))
```
ANSI SQL doesn’t have a built-in function for checking IP ranges. Instead, you use custom functions or comparisons. APL’s `ipv4_is_in_any_range` simplifies this by handling multiple CIDR blocks and ranges in a single function.
```sql SQL example theme={null}
SELECT *,
CASE WHEN ip_address BETWEEN '192.168.0.0' AND '192.168.0.255' THEN 1 ELSE 0 END AS is_in_range
FROM dataset;
```
```kusto APL equivalent theme={null}
['dataset']
| extend is_in_range = ipv4_is_in_any_range(ip_address, dynamic(['192.168.0.0/24', '10.0.0.0/8']))
```
## Usage
### Syntax
```kusto theme={null}
ipv4_is_in_any_range(ip_address: string, ranges: dynamic)
```
### Parameters
| Parameter | Type | Description |
| ------------ | ------- | --------------------------------------------------------------------------- |
| `ip_address` | string | The IPv4 address to evaluate. |
| `ranges` | dynamic | A list of IPv4 ranges or CIDR blocks to check against (in JSON array form). |
### Returns
* `true` if the IP address is in any specified range.
* `false` otherwise.
* `null` if the conversion of a string wasn’t successful.
## Use case example
Identify log entries from specific subnets, such as local office IP ranges.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend is_in_range = ipv4_is_in_any_range('192.168.0.0', dynamic(['192.168.0.0/24', '10.0.0.0/8']))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%20%7C%20extend%20is_in_range%20%3D%20ipv4_is_in_any_range\('192.168.0.0'%2C%20dynamic\(%5B'192.168.0.0%2F24'%2C%20'10.0.0.0%2F8'%5D\)\)%22%7D)
**Output**
| \_time | id | method | uri | status | is\_in\_range |
| ------------------- | ------- | ------ | ----- | ------ | ------------- |
| 2024-11-14 10:00:00 | user123 | GET | /home | 200 | true |
## List of related functions
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# ipv4_is_in_range
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-is-in-range
This page explains how to use the ipv4_is_in_range function in APL.
The `ipv4_is_in_range` function in Axiom Processing Language (APL) determines whether an IPv4 address falls within a specified range of addresses. This function is particularly useful for filtering or grouping logs based on geographic regions, network blocks, or security zones.
You can use this function to:
* Analyze logs for requests originating from specific IP address ranges.
* Detect unauthorized or suspicious activity by isolating traffic outside trusted IP ranges.
* Aggregate metrics for specific IP blocks or subnets.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
The `ipv4_is_in_range` function in APL operates similarly to the `cidrmatch` function in Splunk SPL. Both determine whether an IP address belongs to a specified range, but APL uses a different syntax and format.
```sql Splunk example theme={null}
| eval in_range = cidrmatch("192.168.0.0/24", ip_address)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend in_range = ipv4_is_in_range(ip_address, '192.168.0.0/24')
```
ANSI SQL doesn’t have a built-in equivalent for determining if an IP address belongs to a CIDR range. In SQL, you would typically need custom functions or expressions to achieve this. APL’s `ipv4_is_in_range` provides a concise way to perform this operation.
```sql SQL example theme={null}
SELECT CASE
WHEN ip_address BETWEEN '192.168.0.0' AND '192.168.0.255' THEN 1
ELSE 0
END AS in_range
FROM logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend in_range = ipv4_is_in_range(ip_address, '192.168.0.0/24')
```
## Usage
### Syntax
```kusto theme={null}
ipv4_is_in_range(ip: string, range: string)
```
### Parameters
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------------------------- |
| `ip` | string | The IPv4 address to evaluate. |
| `range` | string | The IPv4 range in CIDR notation (e.g., `192.168.1.0/24`). |
### Returns
* `true` if the IPv4 address is in the range.
* `false` otherwise.
* `null` if the conversion of a string wasn’t successful.
## Use case example
You can use `ipv4_is_in_range` to identify traffic from specific geographic regions or service provider IP blocks.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend in_range = ipv4_is_in_range('192.168.1.0', '192.168.1.0/24')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20in_range%20%3D%20ipv4_is_in_range\('192.168.1.0'%2C%20'192.168.1.0%2F24'\)%22%7D)
**Output**
| geo.city | in\_range |
| -------- | --------- |
| Seattle | true |
| Denver | true |
This query identifies the number of requests from IP addresses in the specified range.
## List of related functions
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# ipv4_is_match
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-is-match
This page explains how to use the ipv4_is_match function in APL.
The `ipv4_is_match` function in APL helps you determine whether a given IPv4 address matches a specific IPv4 pattern. This function is especially useful for tasks that involve IP address filtering, including network security analyses, log file inspections, and geo-locational data processing. By specifying patterns that include wildcards or CIDR notations, you can efficiently check if an IP address falls within defined ranges or meets specific conditions.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
The `ipv4_is_match` function in APL resembles the `cidrmatch` function in Splunk SPL. Both functions assess whether an IP address falls within a designated CIDR range, but `ipv4_is_match` also supports wildcard pattern matching, providing additional flexibility.
```sql Splunk example theme={null}
cidrmatch("192.168.1.0/24", ip)
```
```kusto APL equivalent theme={null}
ipv4_is_match(ip, "192.168.1.0/24")
```
ANSI SQL lacks a direct equivalent to the `ipv4_is_match` function, but you can replicate similar functionality with a combination of `LIKE` and range checking. However, these approaches can be complex and less efficient than `ipv4_is_match`, which simplifies CIDR and wildcard-based IP matching.
```sql SQL example theme={null}
ip LIKE '192.168.1.0'
```
```kusto APL equivalent theme={null}
ipv4_is_match(ip, "192.168.1.0")
```
## Usage
### Syntax
```kusto theme={null}
ipv4_is_match(ipaddress1, ipaddress2, prefix)
```
### Parameters
* **ipaddress1**: A string representing the first IPv4 address you want to evaluate. Use CIDR notation (for example, `192.168.1.0/24`).
* **ipaddress2**: A string representing the second IPv4 address you want to evaluate. Use CIDR notation (for example, `192.168.1.0/24`).
* **prefix**: Optionally, a number between 0 and 32 that specifies the number of most-significant bits taken into account.
### Returns
* `true` if the IPv4 addresses match.
* `false` otherwise.
* `null` if the conversion of an IPv4 string wasn’t successful.
## Use case example
The `ipv4_is_match` function allows you to identify traffic based on IP addresses, enabling faster identification of traffic patterns and potential issues.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend is_match = ipv4_is_match('203.0.113.112', '203.0.113.112')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20is_match%20%3D%20ipv4_is_match\('203.0.113.112'%2C%20'203.0.113.112'\)%22%7D)
**Output**
| \_time | id | status | method | uri | is\_match |
| ------------------- | ------------- | ------ | ------ | ----------- | --------- |
| 2023-11-11T13:20:14 | 203.0.113.45 | 403 | GET | /admin | true |
| 2023-11-11T13:30:32 | 203.0.113.101 | 401 | POST | /restricted | true |
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix): Checks if an IPv4 address matches a single prefix.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
# ipv4_is_private
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-is-private
This page explains how to use the ipv4_is_private function in APL.
The `ipv4_is_private` function determines if an IPv4 address belongs to a private range, as defined by [RFC 1918](https://www.rfc-editor.org/rfc/rfc1918). You can use this function to filter private addresses in datasets such as server logs, network traffic, and other IP-based data.
This function is especially useful in scenarios where you want to:
* Exclude private IPs from logs to focus on public traffic.
* Identify traffic originating from within an internal network.
* Simplify security analysis by categorizing IP addresses.
The private IPv4 addresses reserved for private networks by the Internet Assigned Numbers Authority (IANA) are the following:
| IP address range | Number of addresses | Largest CIDR block (subnet mask) |
| ----------------------------- | ------------------- | -------------------------------- |
| 10.0.0.0 – 10.255.255.255 | 16777216 | 10.0.0.0/8 (255.0.0.0) |
| 172.16.0.0 – 172.31.255.255 | 1048576 | 172.16.0.0/12 (255.240.0.0) |
| 192.168.0.0 – 192.168.255.255 | 65536 | 192.168.0.0/16 (255.255.0.0) |
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use a combination of CIDR matching functions or regex to check for private IPs. In APL, the `ipv4_is_private` function offers a built-in and concise way to achieve the same result.
```sql Splunk example theme={null}
eval is_private=if(cidrmatch("10.0.0.0/8", ip) OR cidrmatch("172.16.0.0/12", ip) OR cidrmatch("192.168.0.0/16", ip), 1, 0)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend is_private=ipv4_is_private(client_ip)
```
In ANSI SQL, you might use `CASE` statements with CIDR-based checks or regex patterns to detect private IPs. In APL, the `ipv4_is_private` function simplifies this with a single call.
```sql SQL example theme={null}
SELECT ip,
CASE
WHEN ip LIKE '10.%' OR ip LIKE '172.16.%' OR ip LIKE '192.168.%' THEN 'true'
ELSE 'false'
END AS is_private
FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend is_private=ipv4_is_private(client_ip)
```
## Usage
### Syntax
```kusto theme={null}
ipv4_is_private(ip: string)
```
### Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------------ |
| `ip` | string | The IPv4 address to evaluate for private range status. |
### Returns
* `true`: The input IP address is private.
* `false`: The input IP address isn’t private.
## Use case example
You can use `ipv4_is_private` to filter logs and focus on public traffic for external analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend is_private = ipv4_is_private('192.168.0.1')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20is_private%20%3D%20ipv4_is_private\('192.168.0.1'\)%22%7D)
**Output**
| geo.country | is\_private |
| ----------- | ----------- |
| USA | true |
| UK | true |
## List of related functions
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# ipv4_netmask_suffix
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv4-netmask-suffix
This page explains how to use the ipv4_netmask_suffix function in APL.
The `ipv4_netmask_suffix` function in APL extracts the netmask suffix from an IPv4 address. The netmask suffix, also known as the subnet prefix length, specifies how many bits are used for the network portion of the address.
This function is useful for network log analysis, security auditing, and infrastructure monitoring. It helps you categorize IP addresses by their subnets, enabling you to detect patterns or anomalies in network traffic or to manage IP allocations effectively.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, netmask suffix extraction typically requires manual parsing or custom scripts. In APL, the `ipv4_netmask_suffix` function simplifies this task by directly extracting the suffix from an IPv4 address in CIDR notation.
```spl Splunk example theme={null}
eval netmask = replace(ip, "^.*?/", "")
```
```kusto APL equivalent theme={null}
extend netmask = ipv4_netmask_suffix(ip)
```
In ANSI SQL, extracting the netmask suffix often involves using string functions like `SUBSTRING` or `CHARINDEX`. In APL, the `ipv4_netmask_suffix` function provides a direct and efficient alternative.
```sql SQL example theme={null}
SELECT SUBSTRING(ip, CHARINDEX('/', ip) + 1, LEN(ip)) AS netmask FROM logs;
```
```kusto APL equivalent theme={null}
extend netmask = ipv4_netmask_suffix(ip)
```
## Usage
### Syntax
```kusto theme={null}
ipv4_netmask_suffix(ipv4address)
```
### Parameters
| Parameter | Type | Description |
| ------------- | ------ | ----------------------------------------------------------- |
| `ipv4address` | string | The IPv4 address in CIDR notation (e.g., `192.168.1.1/24`). |
### Returns
* Returns an integer representing the netmask suffix. For example, `24` for `192.168.1.1/24`.
* Returns the value `32` when the input IPv4 address doesn’t contain the suffix.
* Returns `null` if the input isn’t a valid IPv4 address in CIDR notation.
## Use case example
When analyzing network traffic logs, you can extract the netmask suffix to group or filter traffic by subnets.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend netmask = ipv4_netmask_suffix('192.168.1.1/24')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20netmask%20%3D%20ipv4_netmask_suffix\('192.168.1.1%2F24'\)%22%7D)
**Output**
| geo.country | netmask |
| ----------- | ------- |
| USA | 24 |
| UK | 24 |
## List of related functions
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# ipv6_compare
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv6-compare
This page explains how to use the ipv6_compare function in APL.
Use the `ipv6_compare` function to compare two IPv6 addresses and determine their relative order. This function helps you evaluate whether one address is less than, equal to, or greater than another. It returns `-1`, `0`, or `1` accordingly.
You can use `ipv6_compare` in scenarios where IPv6 addresses are relevant, such as sorting traffic logs, grouping metrics by address ranges, or identifying duplicate or misordered entries. It’s especially useful in network observability and security use cases where working with IPv6 is common.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have a built-in function for directly comparing IPv6 addresses. Users often work around this limitation by converting the addresses into a comparable numeric format using external scripts or custom commands.
```sql Splunk example theme={null}
| eval ip1 = "2001:db8::1", ip2 = "2001:db8::2"
| eval comparison = if(ip1 == ip2, 0, if(ip1 < ip2, -1, 1))
```
```kusto APL equivalent theme={null}
print comparison = ipv6_compare('2001:db8::1', '2001:db8::2')
```
ANSI SQL doesn’t natively support IPv6 comparisons. Typically, users must store IPv6 addresses as strings or binary values and write custom logic to compare them.
```sql SQL example theme={null}
SELECT CASE
WHEN ip1 = ip2 THEN 0
WHEN ip1 < ip2 THEN -1
ELSE 1
END AS comparison
FROM my_table
```
```kusto APL equivalent theme={null}
print comparison = ipv6_compare('2001:db8::1', '2001:db8::2')
```
## Usage
### Syntax
```kusto theme={null}
ipv6_compare(ipv6_1, ipv6_2)
```
### Parameters
| Name | Type | Description |
| -------- | ------ | ----------------------------------- |
| `ipv6_1` | string | The first IPv6 address to compare. |
| `ipv6_2` | string | The second IPv6 address to compare. |
### Returns
An integer that represents the result of the comparison:
* `-1` if `ipv6_1` is less than `ipv6_2`
* `0` if `ipv6_1` is equal to `ipv6_2`
* `1` if `ipv6_1` is greater than `ipv6_2`
## Example
Use `ipv6_compare` to identify whether requests from certain IPv6 addresses fall into specific ranges or appear out of expected order.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend comparison = ipv6_compare('2001:db8::1', '2001:db8::abcd')
| project _time, uri, method, status, comparison
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20comparison%20%3D%20ipv6_compare\('2001%3Adb8%3A%3A1'%2C%20'2001%3Adb8%3A%3Aabcd'\)%20%7C%20project%20_time%2C%20uri%2C%20method%2C%20status%2C%20comparison%22%7D)
**Output**
| \_time | uri | method | status | comparison |
| -------------------- | ----------- | ------ | ------ | ---------- |
| 2025-06-29T22:10:00Z | /products/1 | GET | 200 | -1 |
This example compares two static IPv6 addresses and attaches the result to each row for further filtering or grouping.
## List of related functions
* [ipv6\_is\_match](/apl/scalar-functions/ip-functions/ipv6-is-match): Checks if an IPv6 address matches a given subnet. Use it for range filtering instead of sorting or comparison.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Determines whether an IPv4 address is in a private range. Use this to filter non-public traffic.
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Works the same way as `ipv6_compare` but for IPv4 addresses. Use it when your data contains IPv4 instead of IPv6.
# ipv6_is_in_any_range
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv6-is-in-any-range
This page explains how to use the ipv6_is_in_any_range function in APL.
Use the `ipv6_is_in_any_range` function to determine whether a given IPv6 address belongs to any of a specified set of IPv6 CIDR ranges. This function is particularly useful in log enrichment, threat detection, and network analysis tasks that involve validating or filtering IP addresses against allowlists or blocklists.
You can use this function to:
* Detect whether traffic originates from known internal or external networks.
* Match IPv6 addresses against predefined address ranges for compliance or security auditing.
* Filter datasets based on whether requesters fall into allowed or disallowed IP zones.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk doesn’t offer a built-in function that directly checks if an IP falls within a list of CIDR ranges. Typically, SPL users must write custom logic using `cidrmatch()` repeatedly or rely on lookup tables.
```sql Splunk example theme={null}
| eval is_internal = if(cidrmatch("2001:db8::/32", ip), "true", "false")
```
```kusto APL equivalent theme={null}
ipv6_is_in_any_range('2001:db8::1', dynamic(['2001:db8::/32']))
```
ANSI SQL doesn’t natively support IPv6-aware CIDR range checks. Such functionality usually requires user-defined functions or external extensions.
```sql SQL example theme={null}
-- Typically handled via stored procedures or UDFs in extended SQL environments
SELECT ip, is_in_range(ip, '2001:db8::/32') FROM traffic_logs
```
```kusto APL equivalent theme={null}
ipv6_is_in_any_range('2001:db8::1', dynamic(['2001:db8::/32']))
```
## Usage
### Syntax
```kusto theme={null}
ipv6_is_in_any_range(ipv6_address, ipv6_ranges)
```
### Parameters
| Name | Type | Description |
| -------------- | --------------- | --------------------------------------------------------- |
| `ipv6_address` | `string` | An IPv6 address in standard format (e.g., `2001:db8::1`). |
| `ipv6_ranges` | `dynamic array` | A JSON array of IPv6 CIDR strings to compare against. |
### Returns
A `bool` value:
* `true` if the given IPv6 address is within any of the provided CIDR ranges.
* `false` otherwise.
## Example
You want to detect HTTP requests from a specific internal IPv6 block.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend inRange = ipv6_is_in_any_range('2001:db8::1234', dynamic(['2001:db8::/32', 'fd00::/8']))
| project _time, uri, method, status, inRange
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20inRange%20%3D%20ipv6_is_in_any_range\('2001%3Adb8%3A%3A1234'%2C%20dynamic\(%5B'2001%3Adb8%3A%3A%2F32'%2C%20'fd00%3A%3A%2F8'%5D\)\)%20%7C%20project%20_time%2C%20id%2C%20uri%2C%20method%2C%20status%2C%20inRange%22%7D)
**Output**
| \_time | uri | method | status | inRange |
| -------------------- | ------------ | ------ | ------ | ------- |
| 2025-06-30T01:00:00Z | /api/login | POST | 200 | true |
| 2025-06-30T01:01:00Z | /healthcheck | GET | 204 | true |
## List of related functions
* [ipv4\_is\_in\_any\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-any-range): Use this function when working with IPv4 addresses instead of IPv6.
* [ipv6\_compare](/apl/scalar-functions/ip-functions/ipv6-compare): Compares two IPv6 addresses. Use this for sorting or deduplication rather than range matching.
* [ipv6\_is\_match](/apl/scalar-functions/ip-functions/ipv6-is-match): Checks whether an IPv6 address matches a specific range. Use this if you need to test against a single CIDR block.
# ipv6_is_in_range
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv6-is-in-range
This page explains how to use the ipv6_is_in_range function in APL.
Use the `ipv6_is_in_range` function to check whether an IPv6 address falls within a specified IPv6 CIDR range. This is useful when you need to classify, filter, or segment network traffic by address range—such as identifying requests from internal subnets, geo-localized regional blocks, or known malicious networks.
You can use this function when analyzing HTTP logs, trace telemetry, or security events where IPv6 addresses are present, and you want to restrict attention to or exclude certain address ranges.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, IP range checking for IPv6 addresses typically requires custom scripts or manual logic, as there is no built-in function equivalent to `ipv6_is_in_range`.
```sql Splunk example theme={null}
| eval inRange=if(cidrmatch("2001:db8::/32", src_ip), "yes", "no")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend inRange = ipv6_is_in_range(src_ip, '2001:db8::/32')
```
ANSI SQL doesn’t have native functions for CIDR range checks on IPv6 addresses. You typically rely on user-defined functions (UDFs) or external tooling. In APL, `ipv6_is_in_range` provides this capability out of the box.
```sql SQL example theme={null}
-- Using a hypothetical UDF
SELECT ipv6_in_range(ip_address, '2001:db8::/32') AS in_range FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend inRange = ipv6_is_in_range(src_ip, '2001:db8::/32')
```
## Usage
### Syntax
```kusto theme={null}
ipv6_is_in_range(ipv6: string, cidr_range: string)
```
### Parameters
| Name | Type | Description |
| ------------ | ------ | --------------------------------------------- |
| `ipv6` | string | The IPv6 address to check. |
| `cidr_range` | string | The IPv6 CIDR block (e.g. `'2001:db8::/32'`). |
### Returns
A `bool` value:
* `true` if the IPv6 address is within the specified CIDR range.
* `false` otherwise.
## Example
Use this function to isolate internal service calls originating from a designated IPv6 block.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend inRange = ipv6_is_in_range('fd00::a1b2', 'fd00::/8')
| project _time, span_id, ['service.name'], duration, inRange
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20inRange%20%3D%20ipv6_is_in_range\('fd00%3A%3Aa1b2'%2C%20'fd00%3A%3A%2F8'\)%20%7C%20project%20_time%2C%20span_id%2C%20%5B'service.name'%5D%2C%20duration%2C%20inRange%22%7D)
**Output**
| \_time | span\_id | \['service.name'] | duration | inRange |
| -------------------- | -------- | ----------------- | ---------- | ------- |
| 2025-06-28T11:20:00Z | span-124 | frontend | 00:00:02.4 | true |
| 2025-06-28T11:21:03Z | span-209 | cartservice | 00:00:01.1 | true |
## List of related functions
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks whether an IPv4 address is within a specified CIDR range. Use this function when working with IPv4 instead of IPv6.
* [ipv6\_compare](/apl/scalar-functions/ip-functions/ipv6-compare): Compares two IPv6 addresses. Use when you want to sort or test address equality or ordering.
* [ipv6\_is\_match](/apl/scalar-functions/ip-functions/ipv6-is-match): Checks whether an IPv6 address matches a pattern. Use for wildcard or partial-match filtering rather than range checking.
# ipv6_is_match
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/ipv6-is-match
This page explains how to use the ipv6_is_match function in APL.
Use the `ipv6_is_match` function to determine whether an IPv6 address belongs to a specified IPv6 subnet. This function is useful when you want to classify, filter, or route network events based on IPv6 subnet membership.
You can use `ipv6_is_match` in scenarios such as identifying traffic from a known address range, enforcing access control policies, or correlating logs to specific networks. It supports CIDR notation for subnet specification and returns a boolean value for each row in your dataset.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have a dedicated function for matching IPv6 addresses against CIDR blocks. You typically use regular expressions or custom lookups to perform similar checks. In contrast, APL provides a built-in function that directly evaluates IPv6 CIDR membership.
```sql Splunk example theme={null}
| eval is_in_subnet=if(match(ipv6_field, "^2001:db8::/32"), "true", "false")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend is_in_subnet = ipv6_is_match('2001:db8:abcd:0012::0', '2001:db8::/32')
```
ANSI SQL doesn’t have a standard function to check if an IPv6 address belongs to a subnet. You often implement this logic with string manipulation or rely on database-specific functions. APL simplifies this with `ipv6_is_match`, which accepts a full IPv6 address and a subnet in CIDR notation.
```sql SQL example theme={null}
SELECT CASE
WHEN ip_address LIKE '2001:db8:%' THEN TRUE
ELSE FALSE
END AS is_in_subnet
FROM logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend is_in_subnet = ipv6_is_match('2001:db8:abcd:0012::0', '2001:db8::/32')
```
## Usage
### Syntax
```kusto theme={null}
ipv6_is_match(ipv6_address, ipv6_subnet)
```
### Parameters
| Name | Type | Description |
| -------------- | ------ | ---------------------------------------------------------- |
| `ipv6_address` | string | The full IPv6 address you want to check. |
| `ipv6_subnet` | string | The target subnet in CIDR notation, e.g., `2001:db8::/32`. |
### Returns
A boolean value:
* `true` if the `ipv6_address` belongs to the specified `ipv6_subnet`.
* `false` otherwise.
## Example
Identify requests that originate from a known IPv6 subnet.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend isInternal = ipv6_is_match('2001:db8:abcd::1', '2001:db8::/32')
| project _time, uri, method, status, isInternal
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20isInternal%20%3D%20ipv6_is_match\('2001%3Adb8%3Aabcd%3A%3A1'%2C%20'2001%3Adb8%3A%3A%2F32'\)%20%7C%20project%20_time%2C%20uri%2C%20method%2C%20status%2C%20isInternal%22%7D)
**Output**
| \_time | uri | method | status | isInternal |
| -------------------- | ----------- | ------ | ------ | ---------- |
| 2025-06-28T13:04:10Z | /health | GET | 200 | true |
| 2025-06-28T13:05:22Z | /api/orders | POST | 201 | true |
## List of related functions
* [ipv4\_is\_match](/apl/scalar-functions/ip-functions/ipv4-is-match): Checks whether an IPv4 address belongs to a specified IPv4 subnet. Use it when working with IPv4 addresses.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Parses a string into an IPv4 address. Use it when working with raw IPv4 strings.
# parse_ipv4
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/parse-ipv4
This page explains how to use the parse_ipv4 function in APL.
The `parse_ipv4` function in APL converts an IPv4 address and represents it as a long number. You can use this function to convert an IPv4 address for advanced analysis, filtering, or comparisons. It’s especially useful for tasks like analyzing network traffic logs, identifying trends in IP address usage, or performing security-related queries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk doesn’t provide a direct function for converting an IPv4 address into a long number. However, you can achieve similar functionality using custom SPL expressions.
```sql Splunk example theme={null}
| eval ip_int = tonumber(replace(ip, "\\.", ""))
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend ip_long = parse_ipv4(uri)
```
SQL doesn’t have a built-in function equivalent to `parse_ipv4`, but you can use bitwise operations to achieve a similar result.
```sql SQL example theme={null}
SELECT
(CAST(SPLIT_PART(ip, '.', 1) AS INT) << 24) +
(CAST(SPLIT_PART(ip, '.', 2) AS INT) << 16) +
(CAST(SPLIT_PART(ip, '.', 3) AS INT) << 8) +
CAST(SPLIT_PART(ip, '.', 4) AS INT) AS ip_int
FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend ip_long = parse_ipv4(uri)
```
## Usage
### Syntax
```kusto theme={null}
parse_ipv4(ipv4_address)
```
### Parameters
| Parameter | Type | Description |
| -------------- | ------ | --------------------------------------------- |
| `ipv4_address` | string | The IPv4 address to parse into a long number. |
### Returns
The function returns the IPv4 address as a long number if the conversion succeeds. If the conversion fails, the function returns `null`.
## Use case example
You can use the `parse_ipv4` function to analyze web traffic by representing IP addresses as long numbers.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ip_long = parse_ipv4('192.168.1.1')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20ip_octets%20%3D%20parse_ipv4\('192.168.1.1'\)%22%7D)
**Output**
| \_time | uri | method | ip\_long |
| ------------------- | ----------- | ------ | ------------- |
| 2024-11-14T10:00:00 | /index.html | GET | 3,232,235,777 |
## List of related functions
* [has\_any\_ipv4](/apl/scalar-functions/ip-functions/has-any-ipv4): Matches any IP address in a string column with a list of IP addresses or ranges.
* [has\_ipv4\_prefix](/apl/scalar-functions/ip-functions/has-ipv4-prefix): Checks if an IPv4 address matches a single prefix.
* [has\_ipv4](/apl/scalar-functions/ip-functions/has-ipv4): Checks if a single IP address is present in a string column.
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [ipv4\_is\_private](/apl/scalar-functions/ip-functions/ipv4-is-private): Checks if an IPv4 address is within private IP ranges.
# parse_ipv4_mask
Source: https://axiom.co/docs/apl/scalar-functions/ip-functions/parse-ipv4-mask
This page explains how to use the parse_ipv4_mask function in APL.
## Introduction
The `parse_ipv4_mask` function in APL converts an IPv4 address and its associated netmask into a signed 64-bit wide, long number representation in big-endian order. Use this function when you need to process or compare IPv4 addresses efficiently as numerical values, such as for IP range filtering, subnet calculations, or network analysis.
This function is particularly useful in scenarios where you need a compact and precise way to represent IP addresses and their masks for further aggregation or filtering.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use functions like `cidrmatch` for subnet operations. In APL, `parse_ipv4_mask` focuses on converting an IP and mask into a numerical representation for low-level processing.
```sql Splunk example theme={null}
| eval converted_ip = cidrmatch("192.168.1.0/24", ip)
```
```kusto APL equivalent theme={null}
print converted_ip = parse_ipv4_mask("192.168.1.0", 24)
```
In ANSI SQL, you typically use custom expressions or stored procedures to perform similar IP address transformations. In APL, `parse_ipv4_mask` offers a built-in, optimized function for this task.
```sql SQL example theme={null}
SELECT inet_aton('192.168.1.0') & (0xFFFFFFFF << (32 - 24)) AS converted_ip
```
```kusto APL equivalent theme={null}
print converted_ip = parse_ipv4_mask("192.168.1.0", 24)
```
## Usage
### Syntax
```kusto theme={null}
parse_ipv4_mask(ip, prefix)
```
### Parameters
| Name | Type | Description |
| -------- | ------ | ------------------------------------------------------------------------- |
| `ip` | string | The IPv4 address to convert to a long number. |
| `prefix` | int | An integer from 0 to 32 representing the number of most-significant bits. |
### Returns
* A signed, 64-bit long number in big-endian order if the conversion is successful.
* `null` if the conversion is unsuccessful.
### Example
```kusto theme={null}
print parse_ipv4_mask("127.0.0.1", 24)
```
## Use case example
Use `parse_ipv4_mask` to analyze logs and filter entries based on IP ranges.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend masked_ip = parse_ipv4_mask('192.168.0.1', 24)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20masked_ip%20%3D%20parse_ipv4_mask\('192.168.0.1'%2C%2024\)%22%7D)
**Output**
| \_time | uri | method | masked\_ip |
| ------------------- | ----------- | ------ | ------------- |
| 2024-11-14T10:00:00 | /index.html | GET | 3,232,235,520 |
## List of related functions
* [ipv4\_compare](/apl/scalar-functions/ip-functions/ipv4-compare): Compares two IPv4 addresses lexicographically. Use for sorting or range evaluations.
* [ipv4\_is\_in\_range](/apl/scalar-functions/ip-functions/ipv4-is-in-range): Checks if an IP address is within a specified range.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Converts a dotted-decimal IP address into a numeric representation.
# Mathematical functions
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions
Learn how to use and combine different mathematical functions in APL.
The table summarizes the mathematical functions available in APL.
| Name | Description |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| [abs](#abs) | Calculates the absolute value of the input. |
| [acos](#acos) | Returns the angle whose cosine is the specified number (the inverse operation of cos()). |
| [asin](#asin) | Returns the angle whose sine is the specified number (the inverse operation of sin()). |
| [atan](#atan) | Returns the angle whose tangent is the specified number (the inverse operation of tan()). |
| [atan2](#atan2) | Calculates the angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x). |
| [cos](#cos) | Returns the cosine function. |
| [cot](#cot) | Returns the cotangent function. |
| [degrees](#degrees) | Converts angle value in radians into value in degrees, using formula degrees = (180 / PI) \* angle-in-radians. |
| [exp](#exp) | The base-e exponential function of x, which is e raised to the power x: e^x. |
| [exp10](#exp10) | The base-10 exponential function of x, which is 10 raised to the power x: 10^x. |
| [exp2](#exp2) | The base-2 exponential function of x, which is 2 raised to the power x: 2^x. |
| [gamma](#gamma) | Computes gamma function. |
| [isfinite](#isfinite) | Returns whether input is a finite value (neither infinite nor NaN). |
| [isinf](#isinf) | Returns whether input is an infinite (positive or negative) value. |
| [isint](#isint) | Returns whether input is an integer (positive or negative) value |
| [isnan](#isnan) | Returns whether input is a Not a Number (NaN) value. |
| [log](#log) | Returns the natural logarithm function. |
| [log10](#log10) | Returns the common (base-10) logarithm function. |
| [log2](#log2) | Returns the base-2 logarithm function. |
| [loggamma](#loggamma) | Computes log of absolute value of the gamma function. |
| [max\_of](/apl/scalar-functions/mathematical-functions/max-of) | Returns the largest of the provided values. |
| [min\_of](/apl/scalar-functions/mathematical-functions/min-of) | Returns the smallest of the provided values. |
| [not](#not) | Reverses the value of its bool argument. |
| [pi](#pi) | Returns the constant value of Pi (π). |
| [pow](#pow) | Returns a result of raising to power. |
| [radians](#radians) | Converts angle value in degrees into value in radians, using formula radians = (PI / 180) \* angle-in-degrees. |
| [rand](/apl/scalar-functions/mathematical-functions/rand) | Returns pseudo-random numbers. |
| [range](/apl/scalar-functions/mathematical-functions/range) | Returns a dynamic array of evenly spaced values. |
| [round](#round) | Returns the rounded source to the specified precision. |
| [set\_difference](/apl/scalar-functions/mathematical-functions/set-difference) | Returns the difference between two arrays. |
| [set\_has\_element](/apl/scalar-functions/mathematical-functions/set-has-element) | Determines if a set contains a specific value. |
| [set\_intersect](/apl/scalar-functions/mathematical-functions/set-intersect) | Returns the intersection of two arrays. |
| [set\_union](/apl/scalar-functions/mathematical-functions/set-union) | Returns the union of two arrays. |
| [sign](#sign) | Sign of a numeric expression. |
| [sin](#sin) | Returns the sine function. |
| [sqrt](#sqrt) | Returns the square root function. |
| [tan](#tan) | Returns the tangent function. |
## abs
Calculates the absolute value of the input.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | --------------------- | ------------------------ | -------------------------- |
| x | int, real or timespan | Required | The value to make absolute |
### Returns
* Absolute value of x.
### Examples
```kusto theme={null}
abs(x)
```
```kusto theme={null}
abs(80.5) == 80.5
```
```kusto theme={null}
['sample-http-logs']
| project absolute_value = abs(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20absolute_value%20%3D%20abs%28req_duration_ms%29%22%7D)
## acos
Returns the angle whose cosine is the specified number (the inverse operation of cos()) .
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | -------------------------------- |
| x | real | Required | A real number in range \[-1,. 1] |
### Returns
* The value of the arc cosine of x
* `null` if `x` \< -1 or `x` > 1
### Examples
```kusto theme={null}
acos(x)
```
```kusto theme={null}
acos(-1) == 3.141592653589793
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20cosine_angle%20%3D%20acos%28-1%29%22%7D)
## asin
Returns the angle whose sine is the specified number (the inverse operation of sin()) .
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | -------------------------------- |
| x | real | Required | A real number in range \[-1,. 1] |
* x: A real number in range \[-1, 1].
### Returns
* The value of the arc sine of x
* null if x \< -1 or x > 1
### Examples
```kusto theme={null}
asin(x)
```
```kusto theme={null}
['sample-http-logs']
| project inverse_sin_angle = asin(-1)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20inverse_sin_angle%20%3D%20asin%28-1%29%22%7D)
## atan
Returns the angle whose tangent is the specified number (the inverse operation of tan()) .
### Arguments
x: A real number.
### Returns
The value of the arc tangent of x
### Examples
```kusto theme={null}
atan(x)
```
```kusto theme={null}
atan(-1) == -0.7853981633974483
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20inverse_tan_angle%20%3D%20atan%28-1%29%22%7D)
## atan2
Calculates the angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x).
### Arguments
x: X coordinate (a real number).
y: Y coordinate (a real number).
### Returns
The angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x).
### Examples
```kusto theme={null}
atan2(y,x)
```
```kusto theme={null}
atan2(-1, 1) == -0.7853981633974483
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20angle_in_rads%20%3D%20atan2%28-1%2C%201%29%22%7D)
## cos
Returns the cosine function.
### Arguments
x: A real number.
### Returns
The result of cosine of x.
### Examples
```kusto theme={null}
cos(x)
```
```kusto theme={null}
cos(-1) == 0.5403023058681398
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20cosine_function%20%3D%20cos%28-1%29%22%7D)
## cot
Returns the cotangent function.
### Arguments
x: A real number.
### Returns
The result of cotangent of x.
### Examples
```kusto theme={null}
cot(x)
```
```kusto theme={null}
cot(-1) == -0.6420926159343308
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20cotangent_function%20%3D%20cot%28-1%29%22%7D)
## degrees
Converts angle value in radians into value in degrees, using formula degrees = (180 / PI ) \* angle\_in\_radians
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ----------------- |
| a | real | Required | Angle in radians. |
### Returns
The corresponding angle in degrees for an angle specified in radians.
### Examples
```kusto theme={null}
degrees(a)
```
```kusto theme={null}
degrees(3.14) == 179.9087476710785
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20degree_rads%20%3D%20degrees%283.14%29%22%7D)
## exp
The base-e exponential function of x, which is e raised to the power x: e^x.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | ----------- | ------------------------ | ---------------------- |
| x | real number | Required | Value of the exponent. |
### Returns
* Exponential value of x.
* For natural (base-e) logarithms, see [log](/apl/scalar-functions/mathematical-functions#log\(\)).
* For exponential functions of base-2 and base-10 logarithms, see [exp2](/apl/scalar-functions/mathematical-functions#exp2\(\)), [exp10](/apl/scalar-functions/mathematical-functions#exp10\(\))
### Examples
```kusto theme={null}
exp(x)
```
```kusto theme={null}
exp(1) == 2.718281828459045
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20exponential_value%20%3D%20exp%281%29%22%7D)
## exp2
The base-2 exponential function of x, which is 2 raised to the power x: 2^x.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | ----------- | ------------------------ | ---------------------- |
| x | real number | Required | Value of the exponent. |
### Returns
* Exponential value of x.
* For natural (base-2) logarithms, see [log2](/apl/scalar-functions/mathematical-functions#log2\(\)).
* For exponential functions of base-e and base-10 logarithms, see [exp](/apl/scalar-functions/mathematical-functions#exp\(\)), [exp10](/apl/scalar-functions/mathematical-functions#exp10\(\))
### Examples
```kusto theme={null}
exp2(x)
```
```kusto theme={null}
| project base_2_exponential_value = exp2(req_duration_ms)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20base_2_exponential_value%20%3D%20exp2%28req_duration_ms%29%22%7D)
## gamma
Computes [gamma function](https://en.wikipedia.org/wiki/Gamma_function)
### Arguments
* x: Parameter for the gamma function
### Returns
* Gamma function of x.
* For computing log-gamma function, see loggamma().
### Examples
```kusto theme={null}
gamma(x)
```
```kusto theme={null}
gamma(4) == 6
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20gamma_function%20%3D%20gamma%284%29%22%7D)
## isinf
Returns whether input is an infinite (positive or negative) value.
### Example
```kusto theme={null}
isinf(x)
```
```kusto theme={null}
isinf(45.56) == false
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20infinite_value%20%3D%20isinf%2845.56%29%22%7D)
### Arguments
x: A real number.
### Returns
A non-zero value (true) if x is a positive or negative infinite; and zero (false) otherwise.
## isnan
Returns whether input is Not-a-Number (NaN) value.
### Arguments
x: A real number.
### Returns
A non-zero value (true) if x is NaN; and zero (false) otherwise.
### Examples
```kusto theme={null}
isnan(x)
```
```kusto theme={null}
isnan(45.56) == false
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20nan%20%3D%20isnan%2845.56%29%22%7D)
## log
log() returns the natural logarithm function.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ------------------ |
| x | real | Required | A real number > 0. |
### Returns
The natural logarithm is the base-e logarithm: the inverse of the natural exponential function (exp).
null if the argument is negative or null or can’t be converted to a real value.
### Examples
```kusto theme={null}
log(x)
```
```kusto theme={null}
log(1) == 0
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20natural_log%20%3D%20log%281%29%22%7D)
## log10
log10() returns the common (base-10) logarithm function.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ------------------ |
| x | real | Required | A real number > 0. |
### Returns
The common logarithm is the base-10 logarithm: the inverse of the exponential function (exp) with base 10.
null if the argument is negative or null or can’t be converted to a real value.
### Examples
```kusto theme={null}
log10(x)
```
```kusto theme={null}
log10(4) == 0.6020599913279624
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20base10%20%3D%20log10%284%29%22%7D)
## log2
log2() returns the base-2 logarithm function.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ------------------ |
| x | real | Required | A real number > 0. |
### Returns
The logarithm is the base-2 logarithm: the inverse of the exponential function (exp) with base 2.
null if the argument is negative or null or can’t be converted to a real value.
### Examples
```kusto theme={null}
log2(x)
```
```kusto theme={null}
log2(6) == 2.584962500721156
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20base2_log%20%3D%20log2%286%29%22%7D)
## loggamma
Computes log of absolute value of the [gamma function](https://en.wikipedia.org/wiki/Gamma_function)
### Arguments
x: Parameter for the gamma function
### Returns
* Returns the natural logarithm of the absolute value of the gamma function of x.
### Examples
````kusto theme={null}
loggamma(x)
```kusto
loggamma(16) == 27.89927138384089
````
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20gamma_function%20%3D%20loggamma%2816%29%22%7D)
## not
Reverses the value of its bool argument.
### Examples
```kusto theme={null}
not(expr)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20reverse%20%3D%20not%28false%29%22%7D)
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ----------------------------------- |
| Expr | bool | Required | A `bool` expression to be reversed. |
### Returns
Returns the reversed logical value of its bool argument.
## pi
Returns the constant value of Pi.
### Returns
* The double value of Pi (3.1415926...)
### Examples
```kusto theme={null}
pi()
```
```kusto theme={null}
['sample-http-logs']
| project pie = pi()
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20pie%20%3D%20pi%28%29%22%7D)
## pow
Returns a result of raising to power
### Examples
```kusto theme={null}
pow(base, exponent )
```
```kusto theme={null}
pow(2, 6) == 64
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20power%20%3D%20pow%282%2C%206%29%22%7D)
### Arguments
* *base:* Base value.
* *exponent:* Exponent value.
### Returns
Returns base raised to the power exponent: base ^ exponent.
## radians
Converts angle value in degrees into value in radians, using formula `radians = (PI / 180 ) * angle_in_degrees`
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | --------------------------------- |
| a | real | Required | Angle in degrees (a real number). |
### Returns
The corresponding angle in radians for an angle specified in degrees.
### Examples
```kusto theme={null}
radians(a)
```
```kusto theme={null}
radians(60) == 1.0471975511965976
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20radians%20%3D%20radians%2860%29%22%7D)
## round
Returns the rounded source to the specified precision.
### Arguments
* source: The source scalar the round is calculated on.
* Precision: Number of digits the source will be rounded to.(default value is 0)
### Returns
The rounded source to the specified precision.
### Examples
```kusto theme={null}
round(source [, Precision])
```
```kusto theme={null}
round(25.563663) == 26
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20rounded_value%20%3D%20round%2825.563663%29%22%7D)
## sign
Sign of a numeric expression
### Examples
```kusto theme={null}
sign(x)
```
```kusto theme={null}
sign(25.563663) == 1
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20numeric_expression%20%3D%20sign%2825.563663%29%22%7D)
### Arguments
* x: A real number.
### Returns
* The positive (+1), zero (0), or negative (-1) sign of the specified expression.
## sin
Returns the sine function.
### Examples
```kusto theme={null}
sin(x)
```
```kusto theme={null}
sin(25.563663) == 0.41770848373492825
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20sine_function%20%3D%20sin%2825.563663%29%22%7D)
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | --------------- |
| x | real | Required | A real number. |
### Returns
The result of sin(x)
## sqrt
Returns the square root function.
### Examples
```kusto theme={null}
sqrt(x)
```
```kusto theme={null}
sqrt(25.563663) == 5.0560521160288685
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20squareroot%20%3D%20sqrt%2825.563663%29%22%7D)
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ------------------- |
| x | real | Required | A real number >= 0. |
### Returns
* A positive number such that \_sqrt(x) \_ sqrt(x) == x\*
* null if the argument is negative or can’t be converted to a real value.
## tan
Returns the tangent function.
### Examples
```kusto theme={null}
tan(x)
```
```kusto theme={null}
tan(25.563663) == 0.4597371460602336
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20tangent_function%20%3D%20tan%2825.563663%29%22%7D)
### Argument
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | --------------- |
| x | real | Required | A real number. |
### Returns
* The result of `tan(x)`
## exp10
The base-10 exponential function of x, which is 10 raised to the power x: 10^x.
### Examples
```kusto theme={null}
exp10(x)
```
```kusto theme={null}
exp10(25.563663) == 36,615,333,994,520,800,000,000,000
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20base_10_exponential%20%3D%20pow%2810%2C%2025.563663%29%22%7D)
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------- | -------- | ------------------------ | ------------------------------------- |
| x | real | Required | A real number, value of the exponent. |
### Returns
* Exponential value of x.
* For natural (base-10) logarithms, see [log10](/apl/scalar-functions/mathematical-functions#log10\(\)).
* For exponential functions of base-e and base-2 logarithms, see [exp](/apl/scalar-functions/mathematical-functions#exp\(\)), [exp2](/apl/scalar-functions/mathematical-functions#exp2\(\))
## isint
Returns whether input is an integer (positive or negative) value.
### Arguments
* Expr: expression value which can be a real number
### Returns
A non-zero value (true) if expression is a positive or negative integer; and zero (false) otherwise.
### Examples
```kusto theme={null}
isint(expression)
```
```kusto theme={null}
isint(resp_body_size_bytes) == true
```
```kusto theme={null}
isint(25.563663) == false
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20integer_value%20%3D%20isint%2825.563663%29%22%7D)
## isfinite
Returns whether input is a finite value (is neither infinite nor NaN).
### Arguments
* number: A real number.
### Returns
A non-zero value (true) if x is finite; and zero (false) otherwise.
### Examples
```kusto theme={null}
isfinite(number)
```
```kusto theme={null}
isfinite(25.563663) == true
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%5Cn%7C%20project%20isfinite_value%20%3D%20isfinite%2825.563663%29%22%7D)
# max_of
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/max-of
This page explains how to use the max_of function in APL.
Use the `max_of` function in APL (Axiom Processing Language) to return the maximum value from a list of scalar expressions. You can use it when you need to compute the maximum of a fixed set of values within each row, rather than across rows like with [aggregation functions](/apl/aggregation-function/statistical-functions). It’s especially useful when the values you want to compare come from different columns or are dynamically calculated within the same row.
Use `max_of` when you want to:
* Compare multiple fields in a single event to determine the highest value.
* Perform element-wise maximum calculations in datasets where values are spread across columns.
* Evaluate conditional values and select the highest one on a per-row basis.
* Ensure a minimum value. For example, `max_of(value, 0)` always returns greater than 0.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk doesn’t provide a direct function equivalent to `max_of`. However, you can use the `eval` command with nested `if` statements or custom logic to emulate similar functionality on a per-event basis.
```sql Splunk example theme={null}
eval max_value=if(a > b and a > c, a, if(b > c, b, c))
```
```kusto APL equivalent theme={null}
extend max_value = max_of(a, b, c)
```
ANSI SQL doesn’t offer a built-in function like `max_of` to compute the maximum across expressions in a single row. Instead, you typically use `GREATEST`, which serves a similar purpose.
```sql SQL example theme={null}
SELECT GREATEST(a, b, c) AS max_value FROM table
```
```kusto APL equivalent theme={null}
extend max_value = max_of(a, b, c)
```
## Usage
### Syntax
```kusto theme={null}
max_of(Expr1, Expr2, ..., ExprN)
```
### Parameters
The function takes a comma-separated list of expressions to compare. All values must be of the same type.
### Returns
The function returns the maximum value among the input expressions. The type of the result matches the type of the input expressions. All expressions must be of the same or compatible types.
## Use case example
You have two data points for the size of HTTP responses: header size and body size. You want to find the maximum of these two values for each event.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend max_size = max_of(resp_header_size_bytes, resp_body_size_bytes)
| project _time, id, resp_header_size_bytes, resp_body_size_bytes, max_size
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20max_size%20%3D%20max_of\(resp_header_size_bytes%2C%20resp_body_size_bytes\)%20%7C%20project%20_time%2C%20id%2C%20resp_header_size_bytes%2C%20resp_body_size_bytes%2C%20max_size%22%7D)
**Output**
| \_time | id | resp\_header\_size\_bytes | resp\_body\_size\_bytes | max\_size |
| ---------------- | ------------------------------------ | ------------------------- | ----------------------- | --------- |
| May 15, 11:18:53 | 4baad81e-2bca-408f-8a47-092065274037 | 39 B | 2,805 B | 2,805 |
| May 15, 11:18:53 | 05b257c0-8f9d-4b23-8901-c5f288abc30b | 24 B | 988 B | 988 |
| May 15, 11:18:53 | b34d937c-527a-4a05-b88f-5f3dba645de6 | 72 B | 4,399 B | 4,399 |
| May 15, 11:18:53 | 12a623ec-8b0d-4149-a9eb-d3e18ad5b1cd | 34 B | 1,608 B | 1,608 |
| May 15, 11:18:53 | d24f22a7-8748-4d3d-a815-ed93081fd5d1 | 84 B | 4,080 B | 4,080 |
| May 15, 11:18:53 | 3cc68be1-bb9a-4199-bf75-62eef59e3a09 | 76 B | 5,117 B | 5,117 |
| May 15, 11:18:53 | abadabac-a6c0-4ff2-80a1-11143d7c408b | 41 B | 2,845 B | 2,845 |
# min_of
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/min-of
This page explains how to use the min_of function in APL.
Use the `min_of` function in APL to determine the minimum value among two or more scalar values. The function returns the smallest of its arguments, making it especially useful when you want to compare metrics, constants, or calculated expressions in queries.
You typically use `min_of` when you want to:
* Compare numeric or time-based values across multiple fields or constants.
* Apply conditional logic in summarization or filtering steps.
* Normalize or bound values when computing metrics.
Unlike aggregation functions such as `min()`, which work across rows in a group, `min_of` operates on values within a single row or context.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you often use the `eval` command with the `min` function to compare multiple values. APL’s `min_of` is similar, but used as a scalar function directly in expressions.
```sql Splunk example theme={null}
eval smallest=min(field1, field2)
```
```kusto APL equivalent theme={null}
extend smallest = min_of(field1, field2)
```
In SQL, you typically use `LEAST()` to find the smallest of multiple values. APL’s `min_of` is the equivalent of `LEAST()`.
```sql SQL example theme={null}
SELECT LEAST(col1, col2, col3) AS min_val FROM table;
```
```kusto APL equivalent theme={null}
extend min_val = min_of(col1, col2, col3)
```
## Usage
### Syntax
```kusto theme={null}
min_of(Expr1, Expr2, ..., ExprN)
```
### Parameters
The function takes a comma-separated list of expressions to compare. All values must be of the same type.
### Returns
The function returns the smallest of the provided values. The type of the return value matches the type of the input arguments.
## Use case example
You have two data points for the size of HTTP responses: header size and body size. You want to find the minimum of these two values for each event.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend min_size = min_of(resp_header_size_bytes, resp_body_size_bytes)
| project _time, id, resp_header_size_bytes, resp_body_size_bytes, min_size
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20min_size%20%3D%20min_of\(resp_header_size_bytes%2C%20resp_body_size_bytes\)%20%7C%20project%20_time%2C%20id%2C%20resp_header_size_bytes%2C%20resp_body_size_bytes%2C%20min_size%22%7D)
**Output**
| \_time | id | resp\_header\_size\_bytes | resp\_body\_size\_bytes | min\_size |
| ---------------- | ------------------------------------ | ------------------------- | ----------------------- | --------- |
| May 15, 11:31:05 | 739b0433-39aa-4891-a5e0-3bde3cb40386 | 41 B | 3,410 B | 41 |
| May 15, 11:31:05 | 3016c439-ea30-454b-858b-06f0a66f44b9 | 53 B | 5,333 B | 53 |
| May 15, 11:31:05 | b26b0a5c-bc73-4693-86ad-be9e0cc767d6 | 60 B | 2,936 B | 60 |
| May 15, 11:31:05 | 8d939423-26ae-43f7-9927-13499e7cc7d3 | 60 B | 2,896 B | 60 |
| May 15, 11:31:05 | 10c37b1a-5639-4c99-a232-c8295e3ce664 | 63 B | 4,871 B | 63 |
| May 15, 11:31:05 | 4aa1821a-6906-4ede-9417-3097efb76b89 | 78 B | 1,729 B | 78 |
| May 15, 11:31:05 | 6325de66-0033-4133-b2f3-99fa70f8c9c0 | 96 B | 4,232 B | 96 |
# rand
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/rand
This page explains how to use the rand function in APL.
Use the `rand` function in APL to generate pseudo-random numbers. This function is useful when you want to introduce randomness into your queries. For example, to sample a subset of data, generate test data, or simulate probabilistic scenarios.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, the `random()` function returns a pseudo-random integer between 0 and 2^31-1. You often divide this value to produce a float between 0 and 1. In APL, the `rand()` function directly returns a float in the range `[0, 1)`, so there’s no need to divide or scale.
```sql Splunk example theme={null}
| eval r=random()/2147483647
```
```kusto APL equivalent theme={null}
print r = rand()
```
ANSI SQL uses the `RAND()` function to generate a float between 0 and 1. However, SQL typically doesn’t generate a new random value for every row unless you call `RAND()` inside a subquery or use it with a specific expression. In APL, `rand()` behaves like a row-level function and produces a new value for each row automatically.
```sql SQL example theme={null}
SELECT RAND() as r;
```
```kusto APL equivalent theme={null}
print r = rand()
```
## Usage
### Syntax
```kusto theme={null}
rand()
rand(range)
```
### Parameters
| Name | Type | Description |
| ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `range` | integer | Optional: A positive integer that specifies the upper exclusive limit of the range where you want to generate pseudo-random integers. The lower inclusive limit is 0. |
### Returns
Without `range`: A real number in the range between 0 (inclusive) and 1 (exclusive). Each call returns a pseudo-random float.
With `range`: An integer in the range between 0 (inclusive) and `range` (exclusive).
## Example
**Query**
```kusto theme={null}
['sample-http-logs']
| extend random = rand()
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20random%20%3D%20rand\(\)%22%7D)
**Output**
| \_time | random |
| -------------------- | ------------------ |
| 2024-07-10T14:32:00Z | 0.6324890121902683 |
# range
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/range
This page explains how to use the range function in APL.
Use the `range` function in APL to create a dynamic array of evenly spaced values. You can generate numeric, datetime, or timespan sequences that increase by a constant step, which defaults to 1 for numbers and 1 hour for time-based types. The function stops once the value exceeds the specified endpoint or the maximum result size.
`range` is useful when you want to produce test values, synthetic sequences, time intervals, or loop-like constructs without relying on input data. It helps you populate arrays that can be expanded or joined with real data for further analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In SPL, generating sequences often involves `makeresults` combined with `streamstats` or manual iteration logic. APL’s `range` function simplifies this by producing arrays of equally spaced values directly.
```sql Splunk example theme={null}
| makeresults count=10
| streamstats count as x
```
```kusto APL equivalent theme={null}
print r = range(1, 10, 1)
```
In ANSI SQL, generating a series of numbers usually involves recursive CTEs. APL’s `range` is more concise and efficient for creating sequences without writing complex recursion logic.
```sql SQL example theme={null}
WITH RECURSIVE seq AS (
SELECT 1 AS x
UNION ALL
SELECT x + 1 FROM seq WHERE x < 10
)
SELECT * FROM seq;
```
```kusto APL equivalent theme={null}
print r = range(1, 10, 1)
```
## Usage
### Syntax
```kusto theme={null}
range(start, stop [, step])
```
### Parameters
| Name | Type | Required | Description |
| ----- | ----------------------------------- | -------- | ------------------------------------------------------------------------- |
| start | scalar (number, datetime, timespan) | ✓ | First value in the array. |
| stop | scalar (same type as `start`) | ✓ | Upper bound of the array. The last value is less than or equal to `stop`. |
| step | scalar (same type as `start`) | | Difference between values. Defaults to 1 (numeric) or 1h (time). |
### Returns
A dynamic array that includes values starting at `start`, incremented by `step`, up to and including `stop` (if it aligns exactly with a step). The array truncates if it reaches the system limit of 1,048,576 elements.
## Use case examples
Generate an array of durations to help classify HTTP request latencies.
**Query**
```kusto theme={null}
print r = range(100, 500, 100)
| project r
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22print%20r%20%3D%20range\(100%2C%20500%2C%20100\)%20%7C%20project%20r%22%7D)
**Output**
```json theme={null}
[
100,
200,
300,
400,
500
]
```
This creates an array of thresholds that you can use to bucket or filter request durations in `['sample-http-logs']`.
Build a set of time intervals to align span activity over a fixed period.
**Query**
```kusto theme={null}
print intervals = range(datetime(2025-07-29T12:00:00Z), datetime(2025-07-29T13:00:00Z), 15m)
| project intervals
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22print%20intervals%20%3D%20range\(datetime\(2025-07-29T12%3A00%3A00Z\)%2C%20datetime\(2025-07-29T13%3A00%3A00Z\)%2C%2015m\)%20%7C%20project%20intervals%22%7D)
**Output**
```json theme={null}
[
"2025-07-29T12:00:00Z",
"2025-07-29T12:15:00Z",
"2025-07-29T12:30:00Z",
"2025-07-29T12:45:00Z",
"2025-07-29T13:00:00Z"
]
```
This array helps divide the hour into 15-minute blocks for analyzing activity in `['otel-demo-traces']`.
Create a list of expected hourly intervals to detect missing logs.
**Query**
```kusto theme={null}
print expected = range(datetime(2025-07-29T00:00:00Z), datetime(2025-07-29T05:00:00Z), 1h)
| project expected
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22print%20expected%20%3D%20range\(datetime\(2025-07-29T00%3A00%3A00Z\)%2C%20datetime\(2025-07-29T05%3A00%3A00Z\)%2C%201h\)%20%7C%20project%20expected%22%7D)
**Output**
```json theme={null}
[
"2025-07-29T00:00:00Z",
"2025-07-29T01:00:00Z",
"2025-07-29T02:00:00Z",
"2025-07-29T03:00:00Z",
"2025-07-29T04:00:00Z",
"2025-07-29T05:00:00Z"
]
```
This produces an array of expected log collection times for the `['sample-http-logs']` dataset. You can join this with actual data to detect missing records.
# set_difference
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/set-difference
This page explains how to use the set_difference function in APL.
Use the `set_difference` function in APL to compute the distinct elements in one array that aren’t present in another. This function helps you filter out shared values between two arrays, producing a new array that includes only the unique values from the first input array.
Use `set_difference` when you need to identify new or missing elements, such as:
* Users who visited today but not yesterday.
* Error codes that occurred in one region but not another.
* Service calls that appear in staging but not production.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, similar logic often uses the `setdiff` function from the `mv` (multivalue) function family. APL’s `set_difference` behaves similarly, returning values that are only in the first multivalue field.
```sql Splunk example theme={null}
| eval a=mvappend("a", "b", "c"), b=mvappend("b", "c")
| eval diff=mvfilter(NOT match(a, b))
```
```kusto APL equivalent theme={null}
print a=dynamic(['a', 'b', 'c']), b=dynamic(['b', 'c'])
| extend diff=set_difference(a, b)
```
ANSI SQL doesn’t support array operations directly, but you can emulate set difference with `EXCEPT` when working with rows, not arrays. APL provides native array functions like `set_difference` for this purpose.
```sql SQL example theme={null}
SELECT value FROM array1
EXCEPT
SELECT value FROM array2;
```
```kusto APL equivalent theme={null}
print a=dynamic(['a', 'b', 'c']), b=dynamic(['b', 'c'])
| extend diff=set_difference(a, b)
```
## Usage
### Syntax
```kusto theme={null}
set_difference(Array1, Array2)
```
### Parameters
| Name | Type | Description |
| -------- | ----- | ---------------------------------------------------- |
| `Array1` | array | The array to subtract from. |
| `Array2` | array | The array containing values to remove from `Array1`. |
### Returns
An array that includes all values from `Array1` that aren’t present in `Array2`. The result doesn’t include duplicates.
## Example
Use `set_difference` to return the difference between two arrays.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend difference = set_difference(dynamic([1, 2, 3]), dynamic([2, 3, 4, 5]))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20difference%20%3D%20set_difference\(dynamic\(%5B1%2C%202%2C%203%5D\)%2C%20dynamic\(%5B2%2C%203%2C%204%2C%205%5D\)\)%22%7D)
**Output**
| \_time | difference |
| ---------------- | ---------- |
| May 22, 11:42:52 | \[5, 1, 4] |
## List of related functions
* [set\_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions.
* [set\_has\_element](/apl/scalar-functions/mathematical-functions/set-has-element): Tests whether a set contains a specific value. Prefer it when you only need a Boolean result.
* [set\_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set.
# set_has_element
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/set-has-element
This page explains how to use the set_has_element function in APL.
`set_has_element` returns true when a dynamic array contains a specific element and false when it doesn’t. Use it to perform fast membership checks on values that you have already aggregated into a set with functions such as `make_set`.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, you usually call `in` for scalar membership or use multivalue functions such as `mvfind` for arrays. `set_has_element` plays the role of those helpers after you build a multivalue field with `stats values`.
```sql Splunk example theme={null}
index=web
| stats values(uri) AS uris BY id
| where "/checkout" in uris
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize uris=make_set(uri) by id
| where set_has_element(uris, '/checkout')
```
Standard SQL has no built-in array type, but dialects that implement arrays (for example PostgreSQL) use the `ANY` or `member of` operators. `set_has_element` is the APL counterpart and is applied after you build an array with `ARRAY_AGG` equivalents such as `make_set`.
```sql SQL example theme={null}
SELECT id
FROM sample_http_logs
GROUP BY id
HAVING 'US' = ANY(ARRAY_AGG(country));
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| summarize countries=make_set(['geo.country']) by id
| where set_has_element(countries, 'US')
```
## Usage
### Syntax
```kusto theme={null}
set_has_element(set, value)
```
### Parameters
| Name | Type | Description |
| ------- | ------- | ------------------------------------------------------------------------------------------ |
| `set` | dynamic | The array to search. |
| `value` | scalar | The element to look for. Accepts `long`, `real`, `datetime`, `timespan`, `string`, `bool`. |
### Returns
A `bool` that’s true when `value` exists in `set` and false otherwise.
## Example
Use `set_has_element` to determine if a set contains a specific value.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend hasElement = set_has_element(dynamic([1, 2, 3]), 2)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20hasElement%20%3D%20set_has_element\(dynamic\(%5B1%2C%202%2C%203%5D\)%2C%202\)%22%7D)
**Output**
| \_time | hasElement |
| ---------------- | ---------- |
| May 22, 11:42:52 | true |
## List of related functions
* [set\_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions.
* [set\_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set.
# set_intersect
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/set-intersect
This page explains how to use the set_intersect function in APL.
Use the `set_intersect` function in APL to find common elements between two dynamic arrays. This function returns a new array that contains only the elements that appear in both input arrays, preserving the order from the first array and eliminating duplicates.
You can use `set_intersect` when you need to compare sets of values—for example, to find users who accessed two different URLs, or to identify traces that passed through multiple services. This function is especially useful for working with dynamic fields generated during aggregations or transformations.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have a direct equivalent to `set_intersect`, but you can achieve similar functionality using `mvfilter` with conditions based on a lookup or manually defined set. APL simplifies this process by offering a built-in array intersection function.
```sql Splunk example theme={null}
| eval A=split("apple,banana,cherry", ",")
| eval B=split("banana,cherry,dragonfruit", ",")
| eval C=mvfilter(match(A, B))
```
```kusto APL equivalent theme={null}
print A=dynamic(['apple', 'banana', 'cherry']), B=dynamic(['banana', 'cherry', 'dragonfruit'])
| extend C = set_intersect(A, B)
```
ANSI SQL doesn’t natively support array data types or set operations over arrays. To perform an intersection, you usually need to normalize the arrays using `UNNEST` or `JOIN`, which can be verbose. In APL, `set_intersect` performs this in a single step.
```sql SQL example theme={null}
-- Using PostgreSQL syntax
SELECT ARRAY(
SELECT unnest(array['apple','banana','cherry'])
INTERSECT
SELECT unnest(array['banana','cherry','dragonfruit'])
);
```
```kusto APL equivalent theme={null}
print A=dynamic(['apple', 'banana', 'cherry']), B=dynamic(['banana', 'cherry', 'dragonfruit'])
| extend C = set_intersect(A, B)
```
## Usage
### Syntax
```kusto theme={null}
set_intersect(Array1, Array2)
```
### Parameters
| Name | Type | Description |
| -------- | ------- | ---------------------------- |
| `Array1` | dynamic | The first array to compare. |
| `Array2` | dynamic | The second array to compare. |
### Returns
A dynamic array containing elements that exist in both `Array1` and `Array2`, in the order they appear in `Array1`, with duplicates removed.
## Example
Use `set_intersect` to return the intersection of two arrays.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend intersect = set_intersect(dynamic([1, 2, 3]), dynamic([2, 3, 4, 5]))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20intersect%20%3D%20set_intersect\(dynamic\(%5B1%2C%202%2C%203%5D\)%2C%20dynamic\(%5B2%2C%203%2C%204%2C%205%5D\)\)%22%7D)
**Output**
| \_time | together |
| ---------------- | -------- |
| May 22, 11:42:52 | \[2, 3] |
## List of related functions
* [set\_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions.
* [set\_has\_element](/apl/scalar-functions/mathematical-functions/set-has-element): Tests whether a set contains a specific value. Prefer it when you only need a Boolean result.
* [set\_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set.
# set_union
Source: https://axiom.co/docs/apl/scalar-functions/mathematical-functions/set-union
This page explains how to use the set_union function in APL.
Use the `set_union` function in APL to combine two dynamic arrays into one, returning a new array that includes all distinct elements from both. The order of elements in the result isn’t guaranteed and may differ from the original input arrays.
You can use `set_union` when you need to merge two arrays and eliminate duplicates. It’s especially useful in scenarios where you need to perform set-based logic, such as comparing user activity across multiple sources, correlating IPs from different datasets, or combining traces or log attributes from different events.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
APL’s `set_union` works similarly to using `mvappend` followed by `mvdedup` in SPL. While SPL stores multivalue fields and uses field-based manipulation, APL focuses on dynamic arrays. You need to explicitly apply set logic in APL using functions like `set_union`.
```sql Splunk example theme={null}
| eval result=mvappend(array1, array2)
| eval result=mvdedup(result)
```
```kusto APL equivalent theme={null}
extend result = set_union(array1, array2)
```
Standard SQL doesn’t support arrays as first-class types or set functions like `set_union`. However, conceptually, `set_union` behaves like applying `UNION` between two subqueries that return one column each, followed by a `DISTINCT`.
```sql SQL example theme={null}
SELECT value FROM (
SELECT value FROM table1
UNION
SELECT value FROM table2
)
```
```kusto APL equivalent theme={null}
extend result = set_union(array1, array2)
```
## Usage
### Syntax
```kusto theme={null}
set_union(Array1, Array2)
```
### Parameters
| Name | Type | Description |
| ------ | ------- | -------------------------- |
| Array1 | dynamic | The first array to merge. |
| Array2 | dynamic | The second array to merge. |
### Returns
A dynamic array that contains the distinct elements of both input arrays.
## Example
Use `set_union` to return the union of two arrays.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend together = set_union(dynamic([1, 2, 3]), dynamic([2, 3, 4, 5]))
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20together%20%3D%20set_union\(dynamic\(%5B1%2C%202%2C%203%5D\)%2C%20dynamic\(%5B2%2C%203%2C%204%2C%205%5D\)\)%22%7D)
**Output**
| \_time | together |
| ---------------- | ----------------- |
| May 22, 11:42:52 | \[1, 2, 3, 4, 5 ] |
## List of related functions
* [set\_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions.
* [set\_has\_element](/apl/scalar-functions/mathematical-functions/set-has-element): Tests whether a set contains a specific value. Prefer it when you only need a Boolean result.
* [set\_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set.
# column_ifexists
Source: https://axiom.co/docs/apl/scalar-functions/metadata-functions/column-ifexists
This page explains how to use the column_ifexists function in APL.
Use `column_ifexists()` to make your queries resilient to schema changes. The function checks if a field with a given name exists in the dataset. If it does, the function returns it. If not, it returns a fallback field or expression that you provide.
This is especially useful when working with datasets that evolve over time or come from multiple sources with different schemas. Instead of failing when a field is missing, your query continues running by using a default. Use this function to safely handle queries where the presence of a field isn’t guaranteed.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk, field selection is strict—missing fields typically return `null` in results, but conditional logic for fallback fields requires using `eval` or `coalesce`. In APL, `column_ifexists()` directly substitutes the fallback field at query-time based on schema.
```sql Splunk example theme={null}
... | eval field=if(isnull(Capital), State, Capital)
```
```kusto APL equivalent theme={null}
StormEvents | project column_ifexists('Capital', State)
```
In SQL, you need to check for the existence of a field using system views or error handling. `column_ifexists()` in APL simplifies this by allowing fallback behavior inline without needing procedural code.
```sql SQL example theme={null}
SELECT CASE
WHEN EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'StormEvents' AND COLUMN_NAME = 'Capital')
THEN Capital ELSE State END AS Result
FROM StormEvents
```
```kusto APL equivalent theme={null}
StormEvents | project column_ifexists('Capital', State)
```
## Usage
### Syntax
```kusto theme={null}
column_ifexists(FieldName, DefaultValue)
```
### Parameters
* `FieldName`: The name of the field to return as a string.
* `DefaultValue`: The fallback value to return if `FieldName` doesn’t exist. This can be another field or a literal.
### Returns
Returns the field specified by `FieldName` if it exists in the table schema. Otherwise, returns the result of `DefaultValue`.
## Use case examples
You want to examine HTTP logs, and your schema might have a `geo.region` field in some environments and not in others. You fall back to `geo.country` when `geo.region` is missing.
**Query**
```kusto theme={null}
['sample-http-logs']
| project _time, location = column_ifexists('geo.region', ['geo.country'])
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20project%20_time%2C%20location%20%3D%20column_ifexists\('geo.region'%2C%20%5B'geo.country'%5D\)%22%7D)
**Output**
| \_time | location |
| -------------------- | -------------- |
| 2025-04-28T12:04:10Z | United States |
| 2025-04-28T12:04:12Z | Canada |
| 2025-04-28T12:04:15Z | United Kingdom |
The query returns `geo.region` if it exists; otherwise, it falls back to `geo.country`.
You analyze OpenTelemetry traces and you’re not sure if your data contains `status_code` and `status` fields. You fall back to `100` when it’s missing.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend status_code_field = column_ifexists('status_code', '100')
| extend status_field = column_ifexists('status', 100)
| project _time, trace_id, span_id, status_code_field, status_field
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20status_code_field%20%3D%20column_ifexists\('status_code'%2C%20'100'\)%20%7C%20extend%20status_field%20%3D%20column_ifexists\('status'%2C%20100\)%20%7C%20project%20_time%2C%20trace_id%2C%20span_id%2C%20status_code_field%2C%20status_field%22%7D)
**Output**
| \_time | trace\_id | span\_id | status\_code\_field | status\_field |
| -------------------- | --------- | -------- | ------------------- | ------------- |
| 2025-04-28T10:30:12Z | abc123 | span567 | nil | 100 |
| 2025-04-28T10:30:15Z | def456 | span890 | 200 | 100 |
The query returns the `status_code` field if it exists. Otherwise, it falls back to `100`.
You inspect logs for suspicious activity. In some datasets, a `threat_level` field exists, but not in all. You use the `status` field as a fallback.
**Query**
```kusto theme={null}
['sample-http-logs']
| project _time, id, threat = column_ifexists('threat_level', status)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20project%20_time%2C%20id%2C%20threat%20%3D%20column_ifexists\('threat_level'%2C%20status\)%22%7D)
**Output**
| \_time | id | threat |
| -------------------- | ---- | ------ |
| 2025-04-28T13:22:11Z | u123 | 200 |
| 2025-04-28T13:22:13Z | u456 | 403 |
The function avoids breaking the query if `threat_level` doesn’t exist by defaulting to `status`.
## List of related functions
* [coalesce](/apl/scalar-functions/string-functions#coalesce): Returns the first non-null value from a list of expressions. Use when you want to handle null values, not missing fields.
* [iff](/apl/scalar-functions/conditional-function#iff): Performs conditional logic based on a boolean expression. Use when you want explicit control over evaluation.
* [isnull](/apl/scalar-functions/string-functions#isnull): Checks if a value is null. Useful when combined with other functions for fine-grained control.
* [case](/apl/scalar-functions/conditional-function#case): Allows multiple conditional branches. Use when fallback logic depends on multiple conditions.
* [project](/apl/tabular-operators/project-operator): Selects and transforms fields. Use with `column_ifexists()` to build resilient field projections.
# cursor_current
Source: https://axiom.co/docs/apl/scalar-functions/metadata-functions/cursor-current
This page explains how to use the cursor_current function in APL.
Use the `cursor_current` function in APL to retrieve a cursor string that represents the current point in the query execution. A cursor is a unique identifier that marks a specific position in your data stream, allowing you to resume queries from that exact point in subsequent executions.
You use `cursor_current` when implementing incremental data processing, change data capture (CDC) patterns, or any scenario where you need to track the last processed position in your data. This is particularly useful for building efficient data synchronization pipelines, continuous monitoring systems, and incremental analytics workflows.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use time-based bookmarks or the `_indextime` field to track processing positions. APL's `cursor_current` provides a more robust cursor mechanism that works across query executions and handles distributed data more reliably.
```sql Splunk example theme={null}
| stats max(_indextime) as last_processed
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend cursor = cursor_current()
| summarize max(_time), last_cursor = make_list(cursor)[0]
```
In ANSI SQL, you typically use `MAX(timestamp)` or row IDs to track the last processed record. APL's `cursor_current` provides a system-level cursor that captures the exact query execution position, which is more reliable for distributed systems.
```sql SQL example theme={null}
SELECT MAX(timestamp) as last_processed_time
FROM logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend cursor = cursor_current()
| summarize max(_time), processing_cursor = make_list(cursor)[0]
```
## Usage
### Syntax
```kusto theme={null}
cursor_current()
```
### Parameters
This function takes no parameters.
### Returns
A `string` representing a cursor that marks the current position in the query execution. This cursor can be stored and used in subsequent queries to resume processing from the same point.
## Use case examples
Use `cursor_current` to track the last processed position when implementing incremental log processing pipelines.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend processing_cursor = cursor_current()
| where status != '200'
| summarize error_count = count(), last_cursor = make_list(processing_cursor)[0] by bin(_time, 5m)
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20processing_cursor%20%3D%20cursor_current\(\)%20%7C%20where%20status%20!%3D%20'200'%20%7C%20summarize%20error_count%20%3D%20count\(\)%2C%20last_cursor%20%3D%20make_list\(processing_cursor\)\[0]%20by%20bin\(_time%2C%205m\)%20%7C%20take%205%22%7D)
**Output**
| error\_count | last\_cursor |
| ------------ | --------------------------------------- |
| 343,769 | 0ddnv2035n4lc-082f23975a003f6b-00006d10 |
This query captures cursor positions alongside error counts, allowing you to resume processing from the last successful position in case of failures or for incremental updates.
Use `cursor_current` to implement incremental trace processing for building derived metrics or alerts based on span data.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_cursor = cursor_current()
| where kind == 'server'
| summarize span_count = count(), latest_cursor = make_list(trace_cursor)[0] by ['service.name'], bin(_time, 5m)
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20trace_cursor%20%3D%20cursor_current\(\)%20%7C%20where%20kind%20%3D%3D%20'server'%20%7C%20summarize%20span_count%20%3D%20count\(\)%2C%20latest_cursor%20%3D%20make_list\(trace_cursor\)\[0]%20by%20%5B'service.name'%5D%2C%20bin\(_time%2C%205m\)%20%7C%20take%205%22%7D)
**Output**
| \['service.name'] | span\_count | latest\_cursor |
| ----------------- | ----------- | ----------------- |
| frontend | 234 | cur\_xyz456abc789 |
| cart | 187 | cur\_mno123pqr456 |
This query tracks the processing position for each service, enabling resumable trace analysis and incremental metric computation.
## List of related functions
* [ingestion\_time](/apl/scalar-functions/metadata-functions/ingestion-time): Use `ingestion_time` to get the time when data was ingested. Use `cursor_current` for resumable query execution tracking.
* [now](/apl/scalar-functions/datetime-functions#now): Use `now` to get the current query execution time. Use `cursor_current` for position tracking in data streams.
* [bin](/apl/scalar-functions/rounding-functions#bin): Use `bin` to group data into time buckets. Use `cursor_current` alongside `bin` for checkpointed time-series processing.
# ingestion_time
Source: https://axiom.co/docs/apl/scalar-functions/metadata-functions/ingestion-time
This page explains how to use the ingestion_time function in APL.
Use the `ingestion_time` function to retrieve the timestamp of when each record was ingested into Axiom. This function helps you distinguish between the original event time (as captured in the `_time` field) and the time the data was actually received by Axiom.
You can use `ingestion_time` to:
* Detect delays or lags in data ingestion.
* Filter events based on their ingestion window.
* Audit data pipelines by comparing event time with ingestion time.
This function is especially useful when working with streaming or event-based data sources where ingestion delays are common and might affect alerting, dashboarding, or correlation accuracy.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk provides the `_indextime` field, which represents when an event was indexed. In APL, the equivalent concept is accessed using the `ingestion_time` function, which must be called explicitly.
```sql Splunk example theme={null}
... | eval ingest_time=_indextime
```
```kusto APL equivalent theme={null}
...
| extend ingest_time = ingestion_time()
```
ANSI SQL doesn’t have a standard equivalent to `ingestion_time`, since SQL databases typically don’t distinguish ingestion time from event time. APL provides `ingestion_time` for observability-specific workflows where the arrival time of data is important.
```sql SQL example theme={null}
SELECT event_time, CURRENT_TIMESTAMP AS ingest_time FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend ingest_time = ingestion_time()
```
## Usage
### Syntax
```kusto theme={null}
ingestion_time()
```
### Parameters
This function doesn’t take any parameters.
### Returns
A `datetime` value that represents when each record was ingested into Axiom.
## Use case examples
Use `ingestion_time` to identify delays between when an HTTP request occurred and when it was ingested into Axiom.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ingest_time = ingestion_time()
| extend delay = datetime_diff('second', ingest_time, _time)
| where delay > 1
| project _time, ingest_time, delay, method, uri, status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20ingest_time%20%3D%20ingestion_time\(\)%20%7C%20extend%20delay%20%3D%20datetime_diff\('second'%2C%20ingest_time%2C%20_time\)%20%7C%20where%20delay%20%3E%201%20%7C%20project%20_time%2C%20ingest_time%2C%20delay%2C%20method%2C%20uri%2C%20status%22%7D)
**Output**
| \_time | ingest\_time | delay | method | uri | status |
| -------------------- | -------------------- | ----- | ------ | ------------- | ------ |
| 2025-06-10T12:00:00Z | 2025-06-10T12:01:30Z | 90 | GET | /api/products | 200 |
| 2025-06-10T12:05:00Z | 2025-06-10T12:06:10Z | 70 | POST | /api/cart/add | 201 |
This query calculates the difference between the ingestion time and event time, highlighting entries with more than 60 seconds delay.
Use `ingestion_time` to monitor ingestion lags for spans generated by services, helping identify pipeline slowdowns or delivery issues.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend ingest_time = ingestion_time()
| extend delay = datetime_diff('second', ingest_time, _time)
| summarize avg_delay = avg(delay) by ['service.name'], kind
| order by avg_delay desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20ingest_time%20%3D%20ingestion_time\(\)%20%7C%20extend%20delay%20%3D%20datetime_diff\('second'%2C%20ingest_time%2C%20_time\)%20%7C%20summarize%20avg_delay%20%3D%20avg\(delay\)%20by%20%5B'service.name'%5D%2C%20kind%20%7C%20order%20by%20avg_delay%20desc%22%7D)
**Output**
| service.name | kind | avg\_delay |
| --------------- | -------- | ---------- |
| checkoutservice | server | 45 |
| cartservice | client | 30 |
| frontend | internal | 12 |
This query calculates the average ingestion delay per service and kind to identify services affected by delayed ingestion.
Use `ingestion_time` to identify recently ingested suspicious activity, even if the event occurred earlier.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ingest_time = ingestion_time()
| where status == '401' and ingest_time > ago(1h)
| project _time, ingest_time, id, method, uri, ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20ingest_time%20%3D%20ingestion_time\(\)%20%7C%20where%20status%20%3D%3D%20'401'%20and%20ingest_time%20%3E%20ago\(1h\)%20%7C%20project%20_time%2C%20ingest_time%2C%20id%2C%20method%2C%20uri%2C%20%5B'geo.country'%5D%22%7D)
**Output**
| \_time | ingest\_time | id | method | uri | geo.country |
| -------------------- | -------------------- | ------- | ------ | ------------------ | ----------- |
| 2025-06-11T09:15:00Z | 2025-06-11T10:45:00Z | user123 | GET | /admin/login | US |
| 2025-06-11T08:50:00Z | 2025-06-11T10:30:00Z | user456 | POST | /api/session/start | DE |
This query surfaces failed login attempts that were ingested in the last hour, regardless of when the request actually occurred.
# Pair functions
Source: https://axiom.co/docs/apl/scalar-functions/pair-functions
Learn how to use and combine different pair functions in APL
## Pair functions
| **Function Name** | **Description** |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| [find\_pair](/apl/scalar-functions/pair-functions/find-pair) | Searches an array of key-value pairs and finds the first pair that matches the specified key and value patterns. |
| [pair](#pair) | Creates a pair from a key and value. |
| [parse\_pair](#parse-pair) | Parses a string to form a pair. |
Each argument has a **required** section which is denoted with `required` or `optional`
* If it’s denoted by `required` it means the argument must be passed into that function before it’ll work.
* if it’s denoted by `optional` it means the function can work without passing the argument value.
## pair
Creates a pair from a key and value.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| --------- | -------- | ------------------------ | ----------------------------------------------- |
| Key | string | Required | String for the key in the pair |
| Value | string | Required | String for the value in the pair |
| Separator | string | Optional (Default: ":") | Separator between the key and value in the pair |
### Returns
Returns a pair with the key **Key** and the value **Value** with the separator **Seperator**.
### Examples
```kusto theme={null}
pair("key", "value", ".")
```
```kusto theme={null}
['logs']
| where tags contains pair("host", "mymachine")
```
## parse\_pair
Creates a pair from a key and value.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| --------- | -------- | ------------------------ | ----------------------------------------------- |
| Pair | string | Required | String that has a pair of key value to pull out |
| Separator | string | Optional (Default: ":") | Separator between the key and value in the pair |
### Returns
Returns a pair with the key and value separated by the separator **Seperator** in **Pair**. If
none is found a pair with the value of **Pair** and an empty key is returned.
### Examples
```kusto theme={null}
parse_pair("key.value", ".")
```
```kusto theme={null}
['logs']
| where parse_pair(tags[0]).key == "host"
```
# find_pair
Source: https://axiom.co/docs/apl/scalar-functions/pair-functions/find-pair
This page explains how to use the find_pair function in APL.
Use the `find_pair` function in APL to search an array of key-value pairs and find the first pair that matches specified key and value patterns. This function combines pattern matching with pair extraction, making it easy to locate specific pairs in collections of metadata or tags.
You use `find_pair` when working with arrays of pairs (such as tags, labels, or metadata) where you need to find a specific pair based on pattern matching. This is particularly useful in log analysis, OpenTelemetry traces with custom attributes, and any scenario where data is stored as key-value pair arrays.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically iterate through multi-value fields using `mvfind` or use `spath` for JSON data. APL's `find_pair` provides a specialized function for finding key-value pairs with pattern matching.
```sql Splunk example theme={null}
| eval found_tag=mvfind(tags, 'host=server.*')
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend tags = dynamic(['host:server1', 'env:prod', 'region:us-west'])
| extend found = find_pair(tags, 'host', 'server*')
```
In ANSI SQL, you typically use `JSON_EXTRACT` or array functions with `LIKE` patterns to search arrays. APL's `find_pair` provides a more direct approach for pair-based searches.
```sql SQL example theme={null}
SELECT *
FROM logs
WHERE JSON_EXTRACT(tags, '$[*].key') LIKE 'host%'
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend tags = dynamic(['host:server1', 'env:prod'])
| extend found = find_pair(tags, 'host*', '*')
```
## Usage
### Syntax
```kusto theme={null}
find_pair(array, key_pattern, value_pattern)
find_pair(array, key_pattern, value_pattern, separator)
```
### Parameters
| Name | Type | Description |
| --------------- | --------- | ------------------------------------------------------------------------------- |
| `array` | `dynamic` | An array of strings representing key-value pairs to search. |
| `key_pattern` | `string` | A wildcard pattern to match against pair keys. Use `*` for wildcard matching. |
| `value_pattern` | `string` | A wildcard pattern to match against pair values. Use `*` for wildcard matching. |
| `separator` | `string` | (Optional) The separator between keys and values in the pairs. Defaults to `:`. |
### Returns
A `dynamic` object representing the first matched pair, with `key`, `value` and `separator` properties. Returns `null` if no matching pair is found.
## Example
Use `find_pair` to extract specific metadata from HTTP logs stored as tag arrays.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend tags = dynamic(['server:web01', 'env:production', 'region:us-west'])
| extend server_tag = find_pair(tags, 'server', '*')
| project _time, uri, tags, server_tag
| take 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20tags%20%3D%20dynamic\(%5B'server%3Aweb01'%2C%20'env%3Aproduction'%2C%20'region%3Aus-west'%5D\)%20%7C%20extend%20server_tag%20%3D%20find_pair\(tags%2C%20'server'%2C%20'*'\)%20%7C%20project%20_time%2C%20uri%2C%20tags%2C%20server_tag%20%7C%20take%205%22%7D)
**Output**
| \_time | uri | tags | server\_tag |
| ------------------- | --------- | ----------------------------------------------------- | ------------------------------------------------------ |
| 2025-05-26 08:15:30 | /api/user | \['server:web01', 'env:production', 'region:us-west'] | \{"separator": ":", "value": "web01", "key": "server"} |
| 2025-05-26 08:16:45 | /api/data | \['server:web01', 'env:production', 'region:us-west'] | \{"separator": ":", "value": "web01", "key": "server"} |
This query searches tag arrays for server information and extracts the matching pair, making it easy to filter or group by server tags.
## List of related functions
* [parse\_pair](/apl/scalar-functions/pair-functions#parse-pair): Use `parse_pair` to parse a single pair string into key and value. Use `find_pair` to search an array of pairs.
* [pair](/apl/scalar-functions/pair-functions#pair): Use `pair` to create a pair string from a key and value. Use `find_pair` to locate existing pairs in arrays.
* [array\_index\_of](/apl/scalar-functions/array-functions/array-index-of): Use `array_index_of` for exact match searches in arrays. Use `find_pair` for pattern-based pair matching.
* [extract](/apl/scalar-functions/string-functions#extract): Use `extract` for regex-based extraction from single strings. Use `find_pair` for structured pair searching in arrays.
# Rounding functions
Source: https://axiom.co/docs/apl/scalar-functions/rounding-functions
Learn how to use and combine different rounding functions in APL
## Rounding functions
| **Function Name** | **Description** |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| [ceiling()](#ceiling) | Calculates the smallest integer greater than, or equal to, the specified numeric expression. |
| [bin()](#bin) | Rounds values down to an integer multiple of a given bin size. |
| [bin\_auto()](#bin-auto) | Rounds values down to a fixed-size bin, with control over the bin size and starting point provided by a query property. |
| [floor()](#floor) | Calculates the largest integer less than, or equal to, the specified numeric expression. |
## ceiling()
Calculates the smallest integer greater than, or equal to, the specified numeric expression.
### Arguments
* x: A real number.
### Returns
* The smallest integer greater than, or equal to, the specified numeric expression.
### Examples
```kusto theme={null}
ceiling(x)
```
```kusto theme={null}
ceiling(25.43) == 26
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20project%20smallest_integer%20%3D%20ceiling%2825.43%29%22%7D)
## bin()
Rounds values down to an integer multiple of a given bin size.
The `bin()` function is used with [summarize operator](/apl/tabular-operators/summarize-operator). If your set of values are disorderly, they will be grouped into fractions.
### Arguments
* value: A date, number, or [timespan](/apl/data-types/scalar-data-types#timespan-literals)
* roundTo: The "bin size", a number or timespan that divides value.
### Returns
The nearest multiple of roundTo below value.
### Examples
```kusto theme={null}
bin(value,roundTo)
```
```kusto theme={null}
bin(25.73, 4) == 24
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20project%20round_value%20%3D%20bin%2825.73%2C%204%29%22%7D)
## bin\_auto()
Rounds values down to a fixed-size "bin", the `bin_auto()` function can only be used with the [summarize operator](/apl/tabular-operators/summarize-operator) by statement with the `_time` column.
### Arguments
* Expression: A scalar expression of a numeric type indicating the value to round.
### Returns
The nearest multiple of `query_bin_auto_at` below Expression, shifted so that `query_bin_auto_at` will be translated into itself.
### Example
```kusto theme={null}
summarize count() by bin_auto(_time)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20summarize%20count%28%29%20by%20bin_auto%28_time%29%22%7D)
## floor()
Calculates the largest integer less than, or equal to, the specified numeric expression.
### Arguments
* number: A real number.
### Returns
* The largest integer greater than, or equal to, the specified numeric expression.
### Examples
```kusto theme={null}
floor(number)
```
```kusto theme={null}
floor(25.73) == 25
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%5Cn%7C%20project%20largest_integer_number%20%3D%20floor%2825.73%29%22%7D)
# SQL functions
Source: https://axiom.co/docs/apl/scalar-functions/sql-functions
Learn how to use SQL functions in APL
## SQL functions
| **Function Name** | **Description** |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [parse\_sql()](#parse-sql) | Interprets and analyzes SQL queries, making it easier to extract and understand SQL statements within datasets. |
| [format\_sql()](#format-sql) | Converts the data model produced by `parse_sql()` back into a SQL statement for validation or formatting purposes. |
## parse\_sql()
Analyzes an SQL statement and constructs a data model, enabling insights into the SQL content within a dataset.
### Limitations
* It’s mainly used for simple SQL queries. SQL statements like stored procedures, Windows functions, common table expressions (CTEs), recursive queries, advanced statistical functions, and special joins aren’t supported.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| -------------- | -------- | ------------------------ | ----------------------------- |
| sql\_statement | string | Required | The SQL statement to analyze. |
### Returns
A dictionary representing the structured data model of the provided SQL statement. This model includes maps or slices that detail the various components of the SQL statement, such as tables, fields, conditions, etc.
### Examples
### Basic data retrieval
The SQL statement **`SELECT * FROM db`** retrieves all columns and rows from the table named **`db`**.
```kusto theme={null}
hn
| project parse_sql("select * from db")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\('select%20*%20from%20db'\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### WHERE Clause
This example parses a **`SELECT`** statement with a **`WHERE`** clause, filtering **`customers`** by **`subscription_status`**.
```kusto theme={null}
hn
| project parse_sql("SELECT id, email FROM customers WHERE subscription_status = 'active'")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20id%2C%20email%20FROM%20customers%20WHERE%20subscription_status%20%3D%20'active'%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### JOIN operation
This example shows parsing an SQL statement that performs a **`JOIN`** operation between **`orders`** and **`customers`** tables to match orders with customer names.
```kusto theme={null}
hn
| project parse_sql("SELECT orders.id, customers.name FROM orders JOIN customers ON orders.customer_id = customers.id")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20orders.id%2C%20customers.name%20FROM%20orders%20JOIN%20customers%20ON%20orders.customer_id%20%3D%20customers.id%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### GROUP BY Clause
In this example, the **`parse_sql()`** function is used to parse an SQL statement that aggregates order counts by **`product_id`** using the **`GROUP BY`** clause.
```kusto theme={null}
hn
| project parse_sql("SELECT product_id, COUNT(*) as order_count FROM orders GROUP BY product_id")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20product_id%2C%20COUNT\(*\)%20as%20order_count%20FROM%20orders%20GROUP%20BY%20product_id%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Nested Queries
This example demonstrates parsing a nested SQL query, where the inner query selects **`user_id`** from **`orders`** based on **`purchase_date`**, and the outer query selects names from **`users`** based on those IDs.
```kusto theme={null}
hn
| project parse_sql("SELECT name FROM users WHERE id IN (SELECT user_id FROM orders WHERE purchase_date > '2022-01-01')")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20name%20FROM%20users%20WHERE%20id%20IN%20\(SELECT%20user_id%20FROM%20orders%20WHERE%20purchase_date%20%3E%20'2022-01-01'\)%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### ORDER BY Clause
Here, the example shows how to parse an SQL statement that orders **`users`** by **`registration_date`** in descending order.
```kusto theme={null}
hn
| project parse_sql("SELECT name, registration_date FROM users ORDER BY registration_date DESC")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20name%2C%20registration_date%20FROM%20users%20ORDER%20BY%20registration_date%20DESC%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Sorting users by registration data
This example demonstrates parsing an SQL statement that retrieves the **`name`** and **`registration_date`** of users from the **`users`** table, and orders the results by **`registration_date`** in descending order, showing how to sort data based on a specific column.
```kusto theme={null}
hn | extend parse_sql("SELECT name, registration_date FROM users ORDER BY registration_date DESC")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parse_sql\(%5C%22SELECT%20name%2C%20registration_date%20FROM%20users%20ORDER%20BY%20registration_date%20DESC%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Querying with index hints to use a specific index
This query hints at MySQL to use a specific index named **`index_name`** when executing the SELECT statement on the **`users`** table.
```kusto theme={null}
hn
| project parse_sql("SELECT * FROM users USE INDEX (index_name) WHERE user_id = 101")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20*%20FROM%20users%20USE%20INDEX%20\(index_name\)%20WHERE%20user_id%20%3D%20101%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Inserting data with ON DUPLICATE KEY UPDATE
This example showcases MySQL’s ability to handle duplicate key entries elegantly by updating the existing record if the insert operation encounters a duplicate key.
```kusto theme={null}
hn
| project parse_sql("INSERT INTO settings (user_id, setting, value) VALUES (1, 'theme', 'dark') ON DUPLICATE KEY UPDATE value='dark'")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22INSERT%20INTO%20settings%20\(user_id%2C%20setting%2C%20value\)%20VALUES%20\(1%2C%20'theme'%2C%20'dark'\)%20ON%20DUPLICATE%20KEY%20UPDATE%20value%3D'dark'%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Using JSON functions
This query demonstrates MySQL’s support for JSON data types and functions, extracting the age from a JSON object stored in the **`user_info`** column.
```kusto theme={null}
hn
| project parse_sql("SELECT JSON_EXTRACT(user_info, '$.age') AS age FROM users WHERE user_id = 101")
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20project%20parse_sql\(%5C%22SELECT%20JSON_EXTRACT\(user_info%2C%20%27%24.age%27\)%20AS%20age%20FROM%20users%20WHERE%20user_id%20%3D%20101%5C%22\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
## format\_sql()
Transforms the data model output by `parse_sql()` back into a SQL statement. Useful for testing and ensuring that the parsing accurately retains the original structure and intent of the SQL statement.
### Arguments
| **Name** | **Type** | **Required or Optional** | **Description** |
| ------------------ | ---------- | ------------------------ | -------------------------------------------------- |
| parsed\_sql\_model | dictionary | Required | The structured data model output by `parse_sql()`. |
### Returns
A string that represents the SQL statement reconstructed from the provided data model.
### Examples
### Reformatting a basic SELECT Query
After parsing a SQL statement, you can reformat it back to its original or a standard SQL format.
```kusto theme={null}
hn
| extend parsed = parse_sql("SELECT * FROM db")
| project formatted_sql = format_sql(parsed)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parsed%20%3D%20parse_sql\(%5C%22SELECT%20*%20FROM%20db%5C%22\)%20%7C%20project%20formatted_sql%20%3D%20format_sql\(parsed\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Formatting SQL Queries
This example first parses a SQL statement to analyze its structure and then formats the parsed structure back into a SQL string using `format_sql`.
```kusto theme={null}
hn
| extend parsed = parse_sql("SELECT name, registration_date FROM users ORDER BY registration_date DESC")
| project format_sql(parsed)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parsed%20%3D%20parse_sql\(%5C%22SELECT%20name%2C%20registration_date%20FROM%20users%20ORDER%20BY%20registration_date%20DESC%5C%22\)%20%7C%20project%20formatted_sql%20%3D%20format_sql\(parsed\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Formatting a simple SELECT Statement
This example demonstrates parsing a straightforward `SELECT` statement that retrieves user IDs and usernames from an `user_accounts` table where the `active` status is `1`. After parsing, it uses `format_sql` to convert the parsed data back into a SQL string.
```kusto theme={null}
hn
| extend parsed = parse_sql("SELECT user_id, username FROM user_accounts WHERE active = 1")
| project formatted_sql = format_sql(parsed)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parsed%20%3D%20parse_sql\(%5C%22SELECT%20user_id%2C%20username%20FROM%20user_accounts%20WHERE%20active%20%3D%201%5C%22\)%20%7C%20project%20formatted_sql%20%3D%20format_sql\(parsed\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Reformatting a complex query with JOINS
In this example, a more complex SQL statement involving an `INNER JOIN` between `orders` and `customers` tables is parsed. The query selects orders and customer names for orders placed after January 1, 2023. `format_sql` is then used to reformat the parsed structure into a SQL string.
```kusto theme={null}
hn
| extend parsed = parse_sql("SELECT orders.order_id, customers.name FROM orders INNER JOIN customers ON orders.customer_id = customers.id WHERE orders.order_date > '2023-01-01'")
| project formatted_sql = format_sql(parsed)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parsed%20%3D%20parse_sql\(%5C%22SELECT%20orders.order_id%2C%20customers.name%20FROM%20orders%20INNER%20JOIN%20customers%20ON%20orders.customer_id%20%3D%20customers.id%20WHERE%20orders.order_date%20%3E%20'2023-01-01'%5C%22\)%20%7C%20project%20formatted_sql%20%3D%20format_sql\(parsed\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
### Using format\_sql with aggregation functions
This example focuses on parsing an SQL statement that performs aggregation. It selects product IDs and counts of total sales from a `sales` table, grouping by `product_id` and having a condition on the count. After parsing, `format_sql` reformats the output into an SQL string.
```kusto theme={null}
hn
| extend parsed = parse_sql("SELECT product_id, COUNT(*) as total_sales FROM sales GROUP BY product_id HAVING COUNT(*) > 100")
| project formatted_sql = format_sql(parsed)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22hn%20%7C%20extend%20parsed%20%3D%20parse_sql\(%5C%22SELECT%20product_id%2C%20COUNT\(*\)%20as%20total_sales%20FROM%20sales%20GROUP%20BY%20product_id%20HAVING%20COUNT\(*\)%20%3E%20100%5C%22\)%20%7C%20project%20formatted_sql%20%3D%20format_sql\(parsed\)%22%2C%22queryOptions%22%3A%7B%22quickRange%22%3A%2290d%22%7D%7D)
# String functions
Source: https://axiom.co/docs/apl/scalar-functions/string-functions
Learn how to use and combine different string functions in APL
The table summarizes the string functions available in APL.
| Name | Description |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [base64\_decode\_toarray](/apl/scalar-functions/string-functions/base64-decode-toarray) | Decodes a Base64-encoded string into an array of bytes. |
| [base64\_decode\_tostring](/apl/scalar-functions/string-functions/base64-decode-tostring) | Decodes a base64 string to a UTF-8 string. |
| [base64\_encode\_fromarray](/apl/scalar-functions/string-functions/base64-encode-fromarray) | Converts a sequence of bytes into a Base64-encoded string. |
| [base64\_encode\_tostring](/apl/scalar-functions/string-functions/base64-encode-tostring) | Encodes a string as base64 string. |
| [coalesce](/apl/scalar-functions/string-functions/coalesce) | Evaluates a list of expressions and returns the first non-null (or non-empty for string) expression. |
| [countof\_regex](/apl/scalar-functions/string-functions/countof-regex) | Counts occurrences of a substring in a string. Regex matches don’t. |
| [countof](/apl/scalar-functions/string-functions/countof) | Counts occurrences of a substring in a string. |
| [extract\_all](/apl/scalar-functions/string-functions/extract-all) | Gets all matches for a regular expression from a text string. |
| [extract](/apl/scalar-functions/string-functions/extract) | Gets a match for a regular expression from a text string. |
| [format\_bytes](/apl/scalar-functions/string-functions/format-bytes) | Formats a number of bytes as a string including bytes units |
| [format\_url](/apl/scalar-functions/string-functions/format-url) | Formats an input string into a valid URL by adding the necessary protocol if it’s escaping illegal URL characters. |
| [gettype](/apl/scalar-functions/string-functions/gettype) | Returns the runtime type of its single argument. |
| [indexof](/apl/scalar-functions/string-functions/indexof) | Function reports the zero-based index of the first occurrence of a specified string within input string. |
| [isascii](/apl/scalar-functions/string-functions/isascii) | Checks whether all characters in an input string are ASCII characters. |
| [isempty](/apl/scalar-functions/string-functions/isempty) | Returns true if the argument is an empty string or is null. |
| [isnotempty](/apl/scalar-functions/string-functions/isnotempty) | Returns true if the argument isn’t an empty string or a null. |
| [isnotnull](/apl/scalar-functions/string-functions/isnotnull) | Returns true if the argument isn’t null. |
| [isnull](/apl/scalar-functions/string-functions/isnull) | Evaluates its sole argument and returns a bool value indicating if the argument evaluates to a null value. |
| [parse\_bytes](/apl/scalar-functions/string-functions/parse-bytes) | Parses a string including byte size units and returns the number of bytes |
| [parse\_csv](/apl/scalar-functions/string-functions/parse-csv) | Splits a given string representing a single record of comma-separated values and returns a string array with these values. |
| [parse\_json](/apl/scalar-functions/string-functions/parse-json) | Interprets a string as a JSON value and returns the value as dynamic. |
| [parse\_url](/apl/scalar-functions/string-functions/parse-url) | Parses an absolute URL string and returns a dynamic object contains all parts of the URL. |
| [parse\_urlquery](/apl/scalar-functions/string-functions/parse-urlquery) | Parses a URL query string and returns a dynamic object contains the Query parameters. |
| [quote](/apl/scalar-functions/string-functions/quote) | Returns a string representing the input enclosed in double quotes, with internal quotes and escape sequences handled appropriately. |
| [replace\_regex](/apl/scalar-functions/string-functions/replace-regex) | Replaces all regex matches with another string. |
| [replace\_string](/apl/scalar-functions/string-functions/replace-string) | Replaces all string matches with another string. |
| [replace](/apl/scalar-functions/string-functions/replace) | Replace all regex matches with another string. |
| [reverse](/apl/scalar-functions/string-functions/reverse) | Function makes reverse of input string. |
| [split](/apl/scalar-functions/string-functions/split) | Splits a given string according to a given delimiter and returns a string array with the contained substrings. |
| [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim) | Concatenates between 2 and 64 arguments, with delimiter, provided as first argument. |
| [strcat](/apl/scalar-functions/string-functions/strcat) | Concatenates between 1 and 64 arguments. |
| [strcmp](/apl/scalar-functions/string-functions/strcmp) | Compares two strings. |
| [string-size](/apl/scalar-functions/string-functions/string-size) | Returns the length, in characters, of the input string. |
| [strlen](/apl/scalar-functions/string-functions/strlen) | Returns the length, in characters, of the input string. |
| [strrep](/apl/scalar-functions/string-functions/strrep) | Repeats given string provided number of times (default = 1). |
| [substring](/apl/scalar-functions/string-functions/substring) | Extracts a substring from a source string. |
| [tolower](/apl/scalar-functions/string-functions/tolower) | Converts a string to lower case. |
| [totitle](/apl/scalar-functions/string-functions/totitle) | Converts a string to title case. |
| [toupper](/apl/scalar-functions/string-functions/toupper) | Converts a string to upper case. |
| [translate](/apl/scalar-functions/string-functions/translate) | Substitutes characters in a string, one by one, based on their position in two input lists. |
| [trim\_end\_regex](/apl/scalar-functions/string-functions/trim-end-regex) | Removes trailing match of the specified regular expression. |
| [trim\_end](/apl/scalar-functions/string-functions/trim-end) | Removes trailing match of the specified cutset. |
| [trim\_regex](/apl/scalar-functions/string-functions/trim-regex) | Removes all leading and trailing matches of the specified regular expression. |
| [trim\_space](/apl/scalar-functions/string-functions/trim-space) | Removes all leading and trailing whitespace from a string. |
| [trim\_start\_regex](/apl/scalar-functions/string-functions/trim-start-regex) | Removes leading match of the specified regular expression. |
| [trim\_start](/apl/scalar-functions/string-functions/trim-start) | Removes leading match of the specified cutset. |
| [trim](/apl/scalar-functions/string-functions/trim) | Removes all leading and trailing matches of the specified cutset. |
| [unicode\_codepoints\_from\_string](/apl/scalar-functions/string-functions/unicode-codepoints-from-string) | Converts a UTF-8 string into an array of Unicode code points. |
| [unicode\_codepoints\_to\_string](/apl/scalar-functions/string-functions/unicode-codepoints-to-string) | Converts an array of Unicode code points into a UTF-8 encoded string. |
| [url\_decode](/apl/scalar-functions/string-functions/url-decode) | Converts encoded URL into a regular URL representation. |
| [url\_encode](/apl/scalar-functions/string-functions/url-encode) | Converts characters of the input URL into a format that can be transmitted over the Internet. |
# base64_decode_toarray
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/base64-decode-toarray
This page explains how to use the base64_decode_toarray function in APL.
Use the `base64_decode_toarray` function to decode a Base64-encoded string into an array of bytes. This is especially useful when you need to extract raw binary data from encoded inputs, such as network payloads, authentication tokens, or structured log fields. You can then transform or analyze the resulting byte array using additional APL functions like `array_slice`, `array_length`, or `array_index`.
This function is useful in scenarios where logs or telemetry data include fields that store binary data encoded as Base64, which is common for compact transmission or obfuscation. By decoding these values into byte arrays, you gain visibility into the underlying structure of the data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, decoding Base64 requires using `eval` with the `base64decode` function, which returns a string. If you need a byte array representation, you must manually transform it. In APL, `base64_decode_toarray` directly produces an array of bytes, allowing you to work with binary data more precisely.
```sql Splunk example theme={null}
| eval decoded=base64decode(encodedField)
```
```kusto APL equivalent theme={null}
['my-dataset']
| extend decoded = base64_decode_toarray(encodedField)
```
Standard ANSI SQL doesn’t include a native function to decode Base64 into byte arrays. You typically need to rely on a UDF or cast the result into `VARBINARY` if the engine supports it. APL provides a built-in function that directly yields an array of integers representing bytes.
```sql SQL example theme={null}
SELECT CAST(FROM_BASE64(encodedField) AS BINARY) FROM my_table;
```
```kusto APL equivalent theme={null}
['my-dataset']
| extend decoded = base64_decode_toarray(encodedField)
```
## Usage
### Syntax
```kusto theme={null}
base64_decode_toarray(base64_input)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | ------------------------ |
| base64\_input | string | ✓ | A Base64-encoded string. |
The input string must be standard Base64 with padding, as defined by RFC 4648. For more information, see the [RFC Series documentation](https://www.rfc-editor.org/rfc/rfc4648).
### Returns
An array of integers representing the decoded byte values. If the input string is not valid Base64, the function returns an empty array.
## Use case examples
You want to decode a Base64-encoded field in logs to inspect raw payloads for debugging or transformation.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend raw = base64_decode_toarray('aGVsbG8gd29ybGQ=')
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20raw%20%3D%20base64_decode_toarray\('aGVsbG8gd29ybGQ%3D'\)%22%7D)
**Output**
| raw |
| ------------------------------------------------------- |
| \[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100] |
This query decodes the Base64 string `'aGVsbG8gd29ybGQ='`, which represents the ASCII string `"hello world"`, into an array of byte values.
You receive Base64-encoded trace IDs from an external system and want to decode them for low-level correlation.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_bytes = base64_decode_toarray(trace_id)
| project trace_id, trace_bytes
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20trace_bytes%20%3D%20base64_decode_toarray\(trace_id\)%20%7C%20project%20trace_id%2C%20trace_bytes%22%7D)
**Output**
| trace\_id | trace\_bytes |
| -------------------- | -------------------------------------------------------------- |
| dHJhY2UtaWQtZGVtbw== | \[116, 114, 97, 99, 101, 45, 105, 100, 45, 100, 101, 109, 111] |
This query decodes the trace ID from Base64 into its byte-level representation for internal processing or fingerprinting.
## List of related functions
* [array\_length](/apl/scalar-functions/array-functions/array-length): Returns the number of elements in an array. Use after decoding to validate payload length.
* [array\_slice](/apl/scalar-functions/array-functions/array-slice): Extracts a subrange from an array. Use to focus on specific byte segments after decoding.
* [base64\_encode\_fromarray](/apl/scalar-functions/string-functions/base64-encode-fromarray): Converts a sequence of bytes into a Base64-encoded string.
# base64_decode_tostring
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/base64-decode-tostring
This page explains how to use the base64_decode_tostring function in APL.
The `base64_decode_tostring` function decodes a Base64-encoded string back to its original UTF-8 text format. Use this function when you need to decode Base64-encoded data received from APIs, stored in configurations, or logged in encoded format.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might not have a built-in Base64 decoding function and would typically rely on external scripts. In APL, `base64_decode_tostring` provides native Base64 decoding directly in your queries.
```sql Splunk example theme={null}
| eval decoded=base64decode(field_name)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend decoded = base64_decode_tostring(field_name)
```
In ANSI SQL, Base64 decoding typically requires database-specific functions like `FROM_BASE64()` in MySQL or custom functions. APL provides `base64_decode_tostring` as a standard function.
```sql SQL example theme={null}
SELECT FROM_BASE64(field_name) AS decoded FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend decoded = base64_decode_tostring(field_name)
```
## Usage
### Syntax
```kusto theme={null}
base64_decode_tostring(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ------------------------------------------------- |
| value | string | Yes | The Base64-encoded string to be decoded to UTF-8. |
### Returns
Returns the decoded UTF-8 string from the Base64-encoded input.
## Use case examples
Decode Base64-encoded messages or tokens in HTTP logs to analyze their content.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend decoded_message = base64_decode_tostring('VGhpcyBpcyBhIHRlc3QgbWVzc2FnZQ==')
| project _time, decoded_message, status, uri
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20decoded_message%20%3D%20base64_decode_tostring\(%27VGhpcyBpcyBhIHRlc3QgbWVzc2FnZQ%3D%3D%27\)%20%7C%20project%20_time%2C%20decoded_message%2C%20status%2C%20uri%20%7C%20limit%2010%22%7D)
**Output**
| \_time | decoded\_message | status | uri |
| -------------------- | ---------------------- | ------ | ---------- |
| 2024-11-06T10:00:00Z | This is a test message | 200 | /api/data |
| 2024-11-06T10:01:00Z | This is a test message | 200 | /api/users |
This query decodes a Base64-encoded message, which is useful when analyzing encoded payloads or authentication tokens in HTTP requests.
Decode Base64-encoded span attributes or metadata in distributed traces.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend decoded_attr = base64_decode_tostring('Y2hlY2tvdXQ=')
| project _time, ['service.name'], decoded_attr, trace_id
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20decoded_attr%20%3D%20base64_decode_tostring\(%27Y2hlY2tvdXQ%3D%27\)%20%7C%20project%20_time%2C%20%5B%27service.name%27%5D%2C%20decoded_attr%2C%20trace_id%20%7C%20limit%2010%22%7D)
**Output**
| \_time | service.name | decoded\_attr | trace\_id |
| -------------------- | ------------ | ------------- | --------- |
| 2024-11-06T10:00:00Z | frontend | checkout | abc123 |
| 2024-11-06T10:01:00Z | cart | checkout | def456 |
This query decodes Base64-encoded attributes in traces, which can be useful when trace metadata is transmitted in encoded format.
Decode Base64-encoded authentication tokens or credentials in security logs for investigation.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend decoded_token = base64_decode_tostring('YWRtaW46cGFzc3dvcmQ=')
| project _time, decoded_token, status, uri, id
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20decoded_token%20%3D%20base64_decode_tostring\('YWRtaW46cGFzc3dvcmQ%3D'\)%20%7C%20project%20_time%2C%20decoded_token%2C%20status%2C%20uri%2C%20id%20%7C%20limit%2010%22%7D)
**Output**
| \_time | decoded\_token | status | uri | id |
| -------------------- | -------------- | ------ | ---------- | ------- |
| 2024-11-06T10:00:00Z | admin:password | 401 | /api/login | user123 |
| 2024-11-06T10:01:00Z | admin:password | 403 | /admin | user456 |
This query decodes Base64-encoded credentials from failed authentication attempts, which is useful for security investigations and identifying brute-force attack patterns.
## List of related functions
* [base64\_encode\_tostring](/apl/scalar-functions/string-functions/base64-encode-tostring): Encodes a UTF-8 string into Base64 format. Use this when you need to encode data for transmission or storage.
* [base64\_decode\_toarray](/apl/scalar-functions/string-functions/base64-decode-toarray): Decodes a Base64 string into an array of bytes. Use this when you need to work with the raw binary representation.
* [base64\_encode\_fromarray](/apl/scalar-functions/string-functions/base64-encode-fromarray): Encodes an array of bytes into a Base64 string. Use this when working with binary data rather than text strings.
* [url\_decode](/apl/scalar-functions/string-functions/url-decode): Decodes a URL-encoded string. Use this when working with URL encoding rather than Base64 encoding.
# base64_encode_fromarray
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/base64-encode-fromarray
This page explains how to use the base64_encode_fromarray function in APL.
Use the `base64_encode_fromarray` function to convert a sequence of bytes into a Base64-encoded string. This function is useful when you need to transform binary data into a textual representation for safe storage, logging, or transmission—especially over protocols that require plain-text formats.
You can apply this function when working with IP addresses, file contents, or any byte array that needs to be encoded for use in logs or APIs. It accepts a byte array and returns the Base64-encoded string representation of that array.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk doesn’t provide a native function to directly encode an array of bytes into Base64 in SPL. You would typically write a custom script using an external command or use `eval` with a helper function in an app context.
```sql Splunk example theme={null}
| eval encoded=custom_base64_encode(byte_array_field)
```
```kusto APL equivalent theme={null}
datatable(bytes: dynamic)
[
dynamic([192, 168, 1, 1])
]
| extend encoded = base64_encode_fromarray(bytes)
```
ANSI SQL doesn’t define a built-in standard for Base64 encoding from an array of bytes. This is usually handled via vendor-specific functions (e.g., `TO_BASE64()` in MySQL, or `encode()` in PostgreSQL).
```sql SQL example theme={null}
SELECT TO_BASE64(BINARY 'data') AS encoded;
```
```kusto APL equivalent theme={null}
datatable(bytes: dynamic)
[
dynamic([192, 168, 1, 1])
]
| extend encoded = base64_encode_fromarray(bytes)
```
## Usage
### Syntax
```kusto theme={null}
base64_encode_fromarray(array)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------- | -------- | ----------------------------------------------------------------------- |
| array | dynamic | ✓ | A dynamic array of integers between 0 and 255 representing byte values. |
### Returns
If successful, returns a string representing the Base64-encoded version of the input byte array. If the input is not a valid array of bytes, the result is an empty string.
## Example
Use this function to encode request metadata for logging or comparison with external systems that require Base64-encoded fields.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ip_bytes = dynamic([192, 168, 0, 1])
| extend encoded_ip = base64_encode_fromarray(ip_bytes)
| project _time, id, method, uri, encoded_ip
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20ip_bytes%20%3D%20dynamic\(%5B192%2C%20168%2C%200%2C%201%5D\)%20%7C%20extend%20encoded_ip%20%3D%20base64_encode_fromarray\(ip_bytes\)%20%7C%20project%20_time%2C%20id%2C%20method%2C%20uri%2C%20encoded_ip%22%7D)
**Output**
| \_time | id | method | uri | encoded\_ip |
| -------------------- | ------- | ------ | --------- | ----------- |
| 2025-06-25T08:00:00Z | user123 | GET | /api/data | wKgAAQ== |
Encodes a hardcoded byte representation of an IP address into Base64 for easy string-based comparison or logging.
## List of related functions
* [array\_concat](/apl/scalar-functions/array-functions/array-concat): Concatenates arrays of bytes or values. Use this when building byte arrays for Base64 encoding.
* [base64\_decode\_toarray](/apl/scalar-functions/string-functions/base64-decode-toarray): Decode a Base64-encoded string into an array of bytes. Use this when decoding data received from external sources.
* [format\_ipv4\_mask](/apl/scalar-functions/ip-functions/format-ipv4-mask): Formats a raw IPv4 address with an optional prefix into CIDR notation. Use when dealing with IP-to-string transformations.
* [parse\_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4): Parses a string representation of an IP address into its numeric form. Use this before encoding or masking IP addresses.
# base64_encode_tostring
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/base64-encode-tostring
This page explains how to use the base64_encode_tostring function in APL.
The `base64_encode_tostring` function encodes a string into Base64 format. Use this function when you need to encode data for transmission or storage in systems that only support text-based formats, such as APIs, configuration files, or log analysis pipelines.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might not have a built-in Base64 encoding function and would typically rely on external scripts or commands. In APL, `base64_encode_tostring` provides native Base64 encoding directly in your queries.
```sql Splunk example theme={null}
| eval encoded=base64encode(field_name)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend encoded = base64_encode_tostring(field_name)
```
In ANSI SQL, Base64 encoding typically requires database-specific functions like `TO_BASE64()` in MySQL or custom functions. APL provides `base64_encode_tostring` as a standard function.
```sql SQL example theme={null}
SELECT TO_BASE64(field_name) AS encoded FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend encoded = base64_encode_tostring(field_name)
```
## Usage
### Syntax
```kusto theme={null}
base64_encode_tostring(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ----------------------------------------- |
| value | string | Yes | The input string to be encoded as Base64. |
### Returns
Returns the input string encoded as a Base64 string.
## Use case examples
Encode HTTP content types for secure transmission or storage in Base64 format.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend encoded_content_type = base64_encode_tostring(content_type)
| project _time, content_type, encoded_content_type
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20encoded_content_type%20%3D%20base64_encode_tostring\(content_type\)%20%7C%20project%20_time%2C%20content_type%2C%20encoded_content_type%20%7C%20limit%2010%22%7D)
**Output**
| \_time | content\_type | encoded\_content\_type |
| -------------------- | ---------------- | ------------------------ |
| 2024-11-06T10:00:00Z | application/json | YXBwbGljYXRpb24vanNvbg== |
| 2024-11-06T10:01:00Z | text/html | dGV4dC9odG1s |
This query encodes the content type of each HTTP request into Base64 format, which is useful when you need to pass content types through systems that have character restrictions.
Encode service names in traces for compatibility with external systems.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend encoded_service = base64_encode_tostring(['service.name'])
| project _time, ['service.name'], encoded_service, trace_id
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20encoded_service%20%3D%20base64_encode_tostring\(%5B%27service.name%27%5D\)%20%7C%20project%20_time%2C%20%5B%27service.name%27%5D%2C%20encoded_service%2C%20trace_id%20%7C%20limit%2010%22%7D)
**Output**
| \_time | service.name | encoded\_service | trace\_id |
| -------------------- | ------------ | ---------------- | --------- |
| 2024-11-06T10:00:00Z | frontend | ZnJvbnRlbmQ= | abc123 |
| 2024-11-06T10:01:00Z | checkout | Y2hlY2tvdXQ= | def456 |
This query encodes service names into Base64 format, which can be useful when transmitting trace metadata to systems with specific encoding requirements.
Encode user IDs or sensitive identifiers in security logs for obfuscation or transmission.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend encoded_id = base64_encode_tostring(id)
| project _time, id, encoded_id, status, uri
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20encoded_id%20%3D%20base64_encode_tostring\(id\)%20%7C%20project%20_time%2C%20id%2C%20encoded_id%2C%20status%2C%20uri%20%7C%20limit%2010%22%7D)
**Output**
| \_time | id | encoded\_id | status | uri |
| -------------------- | ------- | ------------ | ------ | ---------- |
| 2024-11-06T10:00:00Z | user123 | dXNlcjEyMw== | 401 | /api/login |
| 2024-11-06T10:01:00Z | user456 | dXNlcjQ1Ng== | 403 | /admin |
This query encodes user IDs from failed authentication attempts into Base64 format, which can be useful for secure log transmission or when integrating with systems that require encoded identifiers.
## List of related functions
* [base64\_decode\_tostring](/apl/scalar-functions/string-functions/base64-decode-tostring): Decodes a Base64-encoded string back to its original UTF-8 format. Use this when you need to reverse the encoding operation.
* [base64\_encode\_fromarray](/apl/scalar-functions/string-functions/base64-encode-fromarray): Encodes an array of bytes into a Base64 string. Use this when working with binary data rather than text strings.
* [base64\_decode\_toarray](/apl/scalar-functions/string-functions/base64-decode-toarray): Decodes a Base64 string into an array of bytes. Use this when you need to work with the raw binary representation.
* [url\_encode](/apl/scalar-functions/string-functions/url-encode): Encodes a URL string for safe transmission. Use this when working with URLs rather than general text encoding.
# coalesce
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/coalesce
This page explains how to use the coalesce function in APL.
The `coalesce` function evaluates a list of expressions and returns the first non-null (or non-empty for strings) value. Use this function to handle missing data, provide default values, or select the first available field from multiple options in your queries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use the `coalesce` command similarly to APL, but the syntax is slightly different. APL's `coalesce` works identically to Splunk's version.
```sql Splunk example theme={null}
| eval result=coalesce(field1, field2, field3)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend result = coalesce(field1, field2, field3)
```
In ANSI SQL, `COALESCE` is a standard function with the same behavior. APL's `coalesce` function works identically to SQL's `COALESCE`.
```sql SQL example theme={null}
SELECT COALESCE(field1, field2, field3) AS result FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend result = coalesce(field1, field2, field3)
```
## Usage
### Syntax
```kusto theme={null}
coalesce(expr1, expr2, ..., exprN)
```
### Parameters
| Name | Type | Required | Description |
| ------------------------ | ------ | -------- | ----------------------------------------------------------------------- |
| expr1, expr2, ..., exprN | scalar | Yes | A list of expressions to evaluate. At least one expression is required. |
### Returns
Returns the value of the first expression that is not null. For string expressions, returns the first non-empty string.
## Use case examples
Provide fallback values when analyzing HTTP logs where certain fields might be missing or empty.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend location = coalesce(['geo.city'], ['geo.country'], 'Unknown')
| summarize request_count = count() by location
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20location%20%3D%20coalesce\(%5B%27geo.city%27%5D%2C%20%5B%27geo.country%27%5D%2C%20%27Unknown%27\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20location%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| location | request\_count |
| ------------- | -------------- |
| New York | 1523 |
| London | 987 |
| United States | 654 |
| Unknown | 234 |
This query uses `coalesce` to select the city if available, fall back to country if city is missing, and finally use 'Unknown' if both are missing, ensuring comprehensive location tracking.
Handle missing or null span attributes in distributed traces by providing default values.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend span_kind = coalesce(kind, 'unknown')
| summarize span_count = count() by span_kind, ['service.name']
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20span_kind%20%3D%20coalesce\(kind%2C%20%27unknown%27\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20span_kind%2C%20%5B%27service.name%27%5D%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| span\_kind | service.name | span\_count |
| ---------- | --------------- | ----------- |
| server | frontend | 2345 |
| client | checkout | 1876 |
| internal | cart | 1234 |
| unknown | product-catalog | 567 |
This query uses `coalesce` to provide a default value for span kinds, ensuring that traces with missing kind information are still included in the analysis.
Ensure user identification in security logs by selecting from multiple possible identifier fields.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend user_identifier = coalesce(id, uri, 'anonymous')
| summarize failed_attempts = count() by user_identifier, status
| sort by failed_attempts desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20user_identifier%20%3D%20coalesce\(id%2C%20uri%2C%20'anonymous'\)%20%7C%20summarize%20failed_attempts%20%3D%20count\(\)%20by%20user_identifier%2C%20status%20%7C%20sort%20by%20failed_attempts%20desc%20%7C%20limit%2010%22%7D)
**Output**
| user\_identifier | status | failed\_attempts |
| ---------------- | ------ | ---------------- |
| user123 | 401 | 45 |
| user456 | 403 | 32 |
| /admin | 401 | 28 |
| anonymous | 401 | 15 |
This query uses `coalesce` to identify users from failed authentication attempts, trying the user ID first, then falling back to the URI, and finally marking truly anonymous attempts.
## List of related functions
* [isnotnull](/apl/scalar-functions/string-functions/isnotnull): Checks if a value is not null. Use this to explicitly test for null values before using coalesce.
* [isnull](/apl/scalar-functions/string-functions/isnull): Checks if a value is null. Use this to identify which values would be skipped by coalesce.
* [isempty](/apl/scalar-functions/string-functions/isempty): Checks if a string is empty or null. Use this with coalesce when working specifically with string data.
* [isnotempty](/apl/scalar-functions/string-functions/isnotempty): Checks if a string is not empty and not null. Use this to validate strings before coalescing them.
# countof
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/countof
This page explains how to use the countof function in APL.
The `countof` function counts the occurrences of a plain substring within a string. Use this function when you need to find how many times a specific text pattern appears in log messages, user input, or any string field without using regular expressions.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use a combination of `rex` and counting operations. APL's `countof` provides a simpler approach for counting plain string occurrences.
```sql Splunk example theme={null}
| rex field=message max_match=0 "error"
| eval error_count=mvcount(error)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend error_count = countof('GET', method)
```
In ANSI SQL, you typically calculate string occurrences using length differences. APL's `countof` provides a more direct approach.
```sql SQL example theme={null}
SELECT (LENGTH(field) - LENGTH(REPLACE(field, 'search', ''))) / LENGTH('search') AS count FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend count = countof('search', field)
```
## Usage
### Syntax
```kusto theme={null}
countof(search, text)
```
### Parameters
| Name | Type | Required | Description |
| ------ | ------ | -------- | -------------------------------------------------- |
| search | string | Yes | The plain substring to search for within the text. |
| text | string | Yes | The source string where occurrences are counted. |
### Returns
Returns the number of times the search string appears in the text.
## Use case examples
Count how many times specific HTTP methods appear in URIs to identify API usage patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend api_segments = countof('/', uri)
| summarize avg_depth = avg(api_segments), request_count = count() by method
| sort by request_count desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20api_segments%20%3D%20countof\(%27%2F%27%2C%20uri\)%20%7C%20summarize%20avg_depth%20%3D%20avg\(api_segments\)%2C%20request_count%20%3D%20count\(\)%20by%20method%20%7C%20sort%20by%20request_count%20desc%22%7D)
**Output**
| method | avg\_depth | request\_count |
| ------ | ---------- | -------------- |
| GET | 3.2 | 5432 |
| POST | 2.8 | 2341 |
| PUT | 2.5 | 876 |
| DELETE | 2.1 | 234 |
This query counts the number of forward slashes in URIs to determine the average API endpoint depth by HTTP method, helping identify API structure complexity.
Count occurrences of specific terms in span names to analyze service operation patterns.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend has_http = countof('frontend', ['service.name'])
| summarize services_with_frontend = sum(has_http), total_spans = count()
| extend percentage = round(100.0 * services_with_frontend / total_spans, 2)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20has_http%20%3D%20countof\(%27frontend%27%2C%20%5B%27service.name%27%5D\)%20%7C%20summarize%20services_with_frontend%20%3D%20sum\(has_http\)%2C%20total_spans%20%3D%20count\(\)%20%7C%20extend%20percentage%20%3D%20round\(100.0%20*%20services_with_frontend%20%2F%20total_spans%2C%202\)%22%7D)
**Output**
| services\_with\_frontend | total\_spans | percentage |
| ------------------------ | ------------ | ---------- |
| 1234 | 8765 | 14.08 |
This query counts how many spans contain 'frontend' in their service name to understand the proportion of frontend-related operations in your traces.
Count slashes in URIs to analyze URL structure and detect unusual patterns that might indicate security threats.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend slash_count = countof('/', uri)
| where slash_count > 5
| project _time, uri, slash_count, id, status, ['geo.country']
| sort by slash_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20slash_count%20%3D%20countof\(%27%2F%27%2C%20uri\)%20%7C%20where%20slash_count%20%3E%205%20%7C%20project%20_time%2C%20uri%2C%20slash_count%2C%20id%2C%20status%2C%20%5B%27geo.country%27%5D%20%7C%20sort%20by%20slash_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | slash\_count | id | status | geo.country |
| -------------------- | ---------------------------------------- | ------------ | ------- | ------ | ----------- |
| 2024-11-06T10:00:00Z | /api/v1/users/12345/posts/67890/comments | 6 | user123 | 200 | US |
| 2024-11-06T10:01:00Z | /admin/config/settings/advanced/security | 5 | user456 | 200 | UK |
This query identifies URIs with unusually high slash counts, which can help detect complex or potentially suspicious URL patterns that might warrant further investigation.
## List of related functions
* [countof\_regex](/apl/scalar-functions/string-functions/countof-regex): Counts substring occurrences using regular expressions. Use this when you need pattern matching instead of exact string matching.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string. Use this when you need the total character count rather than occurrence counting.
* [indexof](/apl/scalar-functions/string-functions/indexof): Finds the position of the first occurrence of a substring. Use this when you need to know where a substring appears, not how many times.
* [extract](/apl/scalar-functions/string-functions/extract): Extracts substrings using regular expressions. Use this when you need to capture matched text rather than count occurrences.
# countof_regex
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/countof-regex
This page explains how to use the countof_regex function in APL.
The `countof_regex` function counts occurrences of a regular expression pattern within a string. Use this function when you need to count complex patterns or character classes in log messages, requiring more flexibility than simple substring matching.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` with `max_match` to count regex matches. APL's `countof_regex` provides a more straightforward approach.
```sql Splunk example theme={null}
| rex field=message max_match=0 "error|warning"
| eval pattern_count=mvcount(rex)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend pattern_count = countof_regex('error|warning', uri)
```
In ANSI SQL, counting regex matches typically requires database-specific functions. APL's `countof_regex` provides a standard approach.
```sql SQL example theme={null}
SELECT REGEXP_COUNT(field, 'pattern') AS count FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend count = countof_regex('pattern', field)
```
## Usage
### Syntax
```kusto theme={null}
countof_regex(regex, text)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ------------------------------------------------------------- |
| regex | string | Yes | The regular expression pattern to search for within the text. |
| text | string | Yes | The source string where pattern occurrences are counted. |
### Returns
Returns the number of times the regex pattern matches in the text.
## Use case examples
Count numeric patterns in URIs to identify parameterized endpoint usage.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend numeric_params = countof_regex('[0-9]+', uri)
| where numeric_params > 0
| summarize avg_params = avg(numeric_params), request_count = count() by method
| sort by request_count desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20numeric_params%20%3D%20countof_regex\(%27%5B0-9%5D%2B%27%2C%20uri\)%20%7C%20where%20numeric_params%20%3E%200%20%7C%20summarize%20avg_params%20%3D%20avg\(numeric_params\)%2C%20request_count%20%3D%20count\(\)%20by%20method%20%7C%20sort%20by%20request_count%20desc%22%7D)
**Output**
| method | avg\_params | request\_count |
| ------ | ----------- | -------------- |
| GET | 1.8 | 3421 |
| POST | 1.2 | 1876 |
| PUT | 2.1 | 654 |
| DELETE | 1.5 | 234 |
This query counts numeric parameters in request URIs using regex, helping identify how frequently parameterized endpoints are accessed by different HTTP methods.
Count specific character patterns in trace IDs to analyze ID generation patterns.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend hex_chars = countof_regex('[a-f]', trace_id)
| summarize avg_hex_chars = avg(hex_chars), trace_count = count() by ['service.name']
| sort by trace_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20hex_chars%20%3D%20countof_regex\(%27%5Ba-f%5D%27%2C%20trace_id\)%20%7C%20summarize%20avg_hex_chars%20%3D%20avg\(hex_chars\)%2C%20trace_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%20%7C%20sort%20by%20trace_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | avg\_hex\_chars | trace\_count |
| --------------- | --------------- | ------------ |
| frontend | 8.3 | 2345 |
| checkout | 8.1 | 1987 |
| cart | 8.5 | 1654 |
| product-catalog | 7.9 | 1234 |
This query counts hexadecimal characters (a-f) in trace IDs to analyze the distribution of characters, which can help identify issues with trace ID generation.
Identify requests with multiple special characters that might indicate injection attacks.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend special_chars = countof_regex('[<>%;()&+]', uri)
| where special_chars >= 3
| project _time, uri, special_chars, id, status, method
| sort by special_chars desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20special_chars%20%3D%20countof_regex\('%5B%3C%3E%25%3B\(\)%26%2B%5D'%2C%20uri\)%20%7C%20where%20special_chars%20%3E%3D%203%20%7C%20project%20_time%2C%20uri%2C%20special_chars%2C%20id%2C%20status%2C%20method%20%7C%20sort%20by%20special_chars%20desc%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | special\_chars | id | status | method |
| -------------------- | --------------------------------------- | -------------- | ------- | ------ | ------ |
| 2024-11-06T10:00:00Z | /search?q= | 8 | user123 | 403 | GET |
| 2024-11-06T10:01:00Z | /api?param='OR'1'='1 | 6 | user456 | 403 | POST |
This query counts special characters commonly used in injection attacks, helping identify potentially malicious requests that warrant further investigation.
## List of related functions
* [countof](/apl/scalar-functions/string-functions/countof): Counts plain substring occurrences. Use this when you need exact string matching without regex complexity.
* [extract](/apl/scalar-functions/string-functions/extract): Extracts the first substring matching a regex. Use this when you need to capture the matched text, not just count occurrences.
* [extract\_all](/apl/scalar-functions/string-functions/extract-all): Extracts all substrings matching a regex. Use this when you need both the count and the actual matched values.
* [replace\_regex](/apl/scalar-functions/string-functions/replace-regex): Replaces all regex matches with another string. Use this when you need to modify matched patterns rather than count them.
# extract
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/extract
This page explains how to use the extract function in APL.
The `extract` function retrieves the first substring that matches a regular expression from a source string. Use this function when you need to pull out specific patterns from log messages, URLs, or any text field using regex capture groups.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` with named or numbered groups. APL's `extract` is similar but uses a numbered capture group parameter.
```sql Splunk example theme={null}
| rex field=message "user=(?\w+)"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend username = extract('user=([A-Za-z0-9_]+)', 1, uri)
```
In ANSI SQL, regex extraction varies by database. APL's `extract` provides a consistent approach across all data.
```sql SQL example theme={null}
SELECT REGEXP_SUBSTR(field, 'pattern', 1, 1, NULL, 1) AS extracted FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend extracted = extract('pattern', 1, field)
```
## Usage
### Syntax
```kusto theme={null}
extract(regex, captureGroup, text)
```
### Parameters
| Name | Type | Required | Description |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------- |
| regex | string | Yes | A regular expression pattern with optional capture groups. |
| captureGroup | int | Yes | The capture group to extract. Use 0 for the entire match, 1 for the first group, 2 for the second, etc. |
| text | string | Yes | The source string to search. |
### Returns
Returns the substring matched by the specified capture group, or null if no match is found.
## Use case examples
Extract user IDs from HTTP request URIs to identify which users are accessing specific endpoints.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend user_id = extract('/users/([0-9]+)', 1, uri)
| where isnotempty(user_id)
| summarize request_count = count() by user_id, method
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20user_id%20%3D%20extract\(%27%2Fusers%2F\(%5B0-9%5D%2B\)%27%2C%201%2C%20uri\)%20%7C%20where%20isnotempty\(user_id\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20user_id%2C%20method%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| user\_id | method | request\_count |
| -------- | ------ | -------------- |
| 12345 | GET | 234 |
| 67890 | POST | 187 |
| 11111 | GET | 156 |
| 22222 | PUT | 98 |
This query extracts numeric user IDs from URIs like '/users/12345' using a regex capture group, helping analyze per-user API usage patterns.
Extract version numbers from service names to track which service versions are running.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend version = extract('v([0-9]+[.][0-9]+)', 1, ['service.name'])
| where isnotempty(version)
| summarize span_count = count() by ['service.name'], version
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20version%20%3D%20extract\(%27v\(%5B0-9%5D%2B%5B.%5D%5B0-9%5D%2B\)%27%2C%201%2C%20%5B%27service.name%27%5D\)%20%7C%20where%20isnotempty\(version\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%2C%20version%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | version | span\_count |
| ------------- | ------- | ----------- |
| frontend-v2.1 | 2.1 | 3456 |
| checkout-v1.5 | 1.5 | 2341 |
| cart-v3.0 | 3.0 | 1987 |
This query extracts version numbers from service names, helping track which versions of services are generating traces.
Extract IP addresses from URIs or request headers to identify the source of suspicious requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend ip_address = extract('([0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3})', 1, uri)
| where status == '403' or status == '401'
| where isnotempty(ip_address)
| summarize failed_attempts = count() by ip_address, status
| sort by failed_attempts desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20ip_address%20%3D%20extract\(%27\(%5B0-9%5D%7B1%2C3%7D%5B.%5D%5B0-9%5D%7B1%2C3%7D%5B.%5D%5B0-9%5D%7B1%2C3%7D%5B.%5D%5B0-9%5D%7B1%2C3%7D\)%27%2C%201%2C%20uri\)%20%7C%20where%20status%20%3D%3D%20%27403%27%20or%20status%20%3D%3D%20%27401%27%20%7C%20where%20isnotempty\(ip_address\)%20%7C%20summarize%20failed_attempts%20%3D%20count\(\)%20by%20ip_address%2C%20status%20%7C%20sort%20by%20failed_attempts%20desc%20%7C%20limit%2010%22%7D)
**Output**
| ip\_address | status | failed\_attempts |
| ------------- | ------ | ---------------- |
| 192.168.1.100 | 401 | 45 |
| 10.0.0.25 | 403 | 32 |
| 172.16.0.50 | 401 | 28 |
This query extracts IP addresses embedded in URIs from failed authentication requests, helping identify potential attackers or misconfigured systems.
## List of related functions
* [extract\_all](/apl/scalar-functions/string-functions/extract-all): Extracts all matches of a regex pattern. Use this when you need multiple matches instead of just the first one.
* [parse\_json](/apl/scalar-functions/string-functions/parse-json): Parses JSON strings into dynamic objects. Use this when working with structured JSON data rather than regex patterns.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by a delimiter. Use this for simpler tokenization without regex complexity.
* [replace\_regex](/apl/scalar-functions/string-functions/replace-regex): Replaces regex matches with new text. Use this when you need to modify matched patterns rather than extract them.
# extract_all
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/extract-all
This page explains how to use the extract_all function in APL.
The `extract_all` function retrieves all substrings that match a regular expression from a source string. Use this function when you need to capture multiple matches of a pattern, such as extracting all email addresses, URLs, or repeated patterns from log entries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` with `max_match=0` to extract all matches. APL's `extract_all` provides a more direct approach.
```sql Splunk example theme={null}
| rex field=message max_match=0 "error_(?\d+)"
| mvexpand code
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend codes = extract_all('error_(\\d+)', dynamic([1]), uri)
```
In ANSI SQL, extracting all regex matches typically requires recursive queries or database-specific functions. APL's `extract_all` simplifies this operation.
```sql SQL example theme={null}
SELECT REGEXP_EXTRACT_ALL(field, 'pattern') AS matches FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend matches = extract_all('pattern', dynamic([1]), field)
```
## Usage
### Syntax
```kusto theme={null}
extract_all(regex, captureGroups, text)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------------- | -------- | ---------------------------------------------------------------------------------------- |
| regex | string | Yes | A regular expression with one or more capture groups. |
| captureGroups | dynamic array | Yes | An array of capture group numbers to extract (e.g., `dynamic([1])` or `dynamic([1,2])`). |
| text | string | Yes | The source string to search. |
### Returns
Returns a dynamic array containing all matches. For single capture groups, returns a one-dimensional array. For multiple capture groups, returns a two-dimensional array.
## Use case examples
Extract all numeric values from URIs to analyze parameter patterns in API requests.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend numbers = extract_all('([0-9]+)', dynamic([1]), uri)
| where array_length(numbers) > 0
| project _time, uri, numbers, method
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20numbers%20%3D%20extract_all\(%27\(%5B0-9%5D%2B\)%27%2C%20dynamic\(%5B1%5D\)%2C%20uri\)%20%7C%20where%20array_length\(numbers\)%20%3E%200%20%7C%20project%20_time%2C%20uri%2C%20numbers%2C%20method%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | numbers | method |
| -------------------- | --------------------------------- | ---------------------- | ------ |
| 2024-11-06T10:00:00Z | /api/users/123/posts/456 | \["123", "456"] | GET |
| 2024-11-06T10:01:00Z | /products/789 | \["789"] | GET |
| 2024-11-06T10:02:00Z | /orders/111/items/222/details/333 | \["111", "222", "333"] | POST |
This query extracts all numeric values from URIs, helping analyze how many IDs are typically passed in API requests and their patterns.
Extract all service names mentioned in span attributes to understand service dependencies.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_mentions = extract_all('(frontend|checkout|cart|product-catalog)', dynamic([1]), ['service.name'])
| where array_length(service_mentions) > 0
| summarize mention_count = count() by service_mention = tostring(service_mentions)
| sort by mention_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20service_mentions%20%3D%20extract_all\(%27\(frontend%7Ccheckout%7Ccart%7Cproduct-catalog\)%27%2C%20dynamic\(%5B1%5D\)%2C%20%5B%27service.name%27%5D\)%20%7C%20where%20array_length\(service_mentions\)%20%3E%200%20%7C%20summarize%20mention_count%20%3D%20count\(\)%20by%20service_mention%20%3D%20tostring\(service_mentions\)%20%7C%20sort%20by%20mention_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service\_mention | mention\_count |
| -------------------- | -------------- |
| \["frontend"] | 4532 |
| \["checkout"] | 3421 |
| \["cart"] | 2987 |
| \["product-catalog"] | 2341 |
This query extracts all service name patterns from span data, helping understand which services are most frequently referenced in traces.
Extract all suspicious keywords from URIs to detect potential SQL injection or XSS attempts.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend threats = extract_all('(union|select|script|alert|drop|insert|delete)', dynamic([1]), uri)
| where array_length(threats) > 0
| project _time, uri, threats, id, status, ['geo.country']
| sort by array_length(threats) desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20threats%20%3D%20extract_all\(%27\(union%7Cselect%7Cscript%7Calert%7Cdrop%7Cinsert%7Cdelete\)%27%2C%20dynamic\(%5B1%5D\)%2C%20uri\)%20%7C%20where%20array_length\(threats\)%20%3E%200%20%7C%20project%20_time%2C%20uri%2C%20threats%2C%20id%2C%20status%2C%20%5B%27geo.country%27%5D%20%7C%20sort%20by%20array_length\(threats\)%20desc%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | threats | id | status | geo.country |
| -------------------- | --------------------------------------- | -------------------- | ------- | ------ | ----------- |
| 2024-11-06T10:00:00Z | /search?q= | \["script", "alert"] | user123 | 403 | Unknown |
| 2024-11-06T10:01:00Z | /api?id=1'union select \* | \["union", "select"] | user456 | 403 | Russia |
This query extracts all SQL and XSS-related keywords from URIs, helping identify potential injection attacks by counting how many threat indicators appear in each request.
## List of related functions
* [extract](/apl/scalar-functions/string-functions/extract): Extracts only the first match of a regex pattern. Use this when you only need the first occurrence rather than all matches.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by a delimiter into an array. Use this for simpler tokenization without regex complexity.
* [parse\_json](/apl/scalar-functions/string-functions/parse-json): Parses JSON strings into dynamic objects. Use this when working with structured JSON data rather than regex patterns.
* [countof\_regex](/apl/scalar-functions/string-functions/countof-regex): Counts regex pattern occurrences. Use this when you only need the count of matches, not the actual matched text.
# format_bytes
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/format-bytes
This page explains how to use the format_bytes function in APL.
The `format_bytes` function formats a numeric value as a human-readable string representing data size in bytes with appropriate units (KB, MB, GB, etc.). Use this function to make byte values more readable in reports, dashboards, and log analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically need custom eval expressions or lookup tables to format bytes. APL's `format_bytes` provides this functionality natively.
```sql Splunk example theme={null}
| eval size_str=if(resp_header_size_bytes<1024, resp_header_size_bytes." B", if(resp_header_size_bytes<1048576, round(resp_header_size_bytes/1024,2)." KB", round(resp_header_size_bytes/1048576,2)." MB"))
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend size_str = format_bytes(resp_header_size_bytes)
```
In ANSI SQL, formatting bytes requires complex CASE statements. APL's `format_bytes` simplifies this operation.
```sql SQL example theme={null}
SELECT CASE
WHEN resp_header_size_bytes < 1024 THEN CONCAT(resp_header_size_bytes, ' B')
WHEN resp_header_size_bytes < 1048576 THEN CONCAT(ROUND(resp_header_size_bytes/1024, 2), ' KB')
ELSE CONCAT(ROUND(resp_header_size_bytes/1048576, 2), ' MB')
END AS size_str FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend size_str = format_bytes(resp_header_size_bytes)
```
## Usage
### Syntax
```kusto theme={null}
format_bytes(value, precision, units, base)
```
### Parameters
| Name | Type | Required | Description |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| value | number | Yes | The numeric value representing bytes to format. |
| precision | number | No | Number of decimal places (default: 0). |
| units | string | No | Target units. If omitted, units are auto-selected. Base 2 suffixes: `Bytes`, `KiB`, `KB`, `MiB`, `MB`, `GiB`, `GB`, `TiB`, `TB`, `PiB`, `EiB`, `ZiB`, `YiB`. Base 10 suffixes: `kB`, `MB`, `GB`, `TB`, `PB`, `EB`, `ZB`, `YB`. |
| base | number | No | Either 2 (default, 1024-based) or 10 (1000-based) for unit calculations. |
### Returns
Returns a formatted string representing the byte value with appropriate units.
## Use case examples
Format response header sizes as human-readable values for better analysis of payload patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend formatted_size = format_bytes(resp_header_size_bytes, 2)
| summarize avg_size = avg(resp_header_size_bytes), formatted_avg = format_bytes(toint(avg(resp_header_size_bytes)), 2) by status
| sort by avg_size desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20formatted_size%20%3D%20format_bytes\(resp_header_size_bytes%2C%202\)%20%7C%20summarize%20avg_size%20%3D%20avg\(resp_header_size_bytes\)%2C%20formatted_avg%20%3D%20format_bytes\(toint\(avg\(resp_header_size_bytes\)\)%2C%202\)%20by%20status%20%7C%20sort%20by%20avg_size%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | avg\_size | formatted\_avg |
| ------ | --------- | -------------- |
| 500 | 8765432 | 8.36 MB |
| 200 | 3456789 | 3.30 MB |
| 404 | 1234567 | 1.18 MB |
| 301 | 456789 | 446.08 KB |
This query formats average response header sizes by HTTP status code, making it easier to identify which status codes are associated with larger data transfers.
Format response header sizes for failed authentication attempts to identify potential data exfiltration or unusual payload patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '403' or status == '401'
| extend formatted_size = format_bytes(resp_header_size_bytes, 1)
| summarize failed_attempts = count(), avg_size = format_bytes(toint(avg(resp_header_size_bytes)), 1) by status
| sort by failed_attempts desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20status%20%3D%3D%20%27403%27%20or%20status%20%3D%3D%20%27401%27%20%7C%20extend%20formatted_size%20%3D%20format_bytes\(resp_header_size_bytes%2C%201\)%20%7C%20summarize%20failed_attempts%20%3D%20count\(\)%2C%20avg_size%20%3D%20format_bytes\(toint\(avg\(resp_header_size_bytes\)\)%2C%201\)%20by%20status%20%7C%20sort%20by%20failed_attempts%20desc%22%7D)
**Output**
| status | failed\_attempts | avg\_size |
| ------ | ---------------- | --------- |
| 401 | 1234 | 850.0 KB |
| 403 | 987 | 720.0 KB |
This query formats average response header sizes, helping identify unusual payload patterns that might indicate security issues.
## List of related functions
* [parse\_bytes](/apl/scalar-functions/string-functions/parse-bytes): Parses a formatted byte string back to a numeric value. Use this to reverse the formatting operation.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string in characters. Use this when you need character count rather than byte formatting.
# format_url
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/format-url
This page explains how to use the format_url function in APL.
The `format_url` function constructs a properly formatted URL from a dynamic object containing URL components (scheme, host, path, port, etc.). Use this function when you need to build URLs programmatically from parsed components or when reconstructing URLs from log data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically concatenate URL parts manually with `eval`. APL's `format_url` provides a structured approach.
```sql Splunk example theme={null}
| eval full_url=scheme."://".host.":".port.path
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend full_url = format_url(dynamic({'scheme': 'https', 'host': host, 'port': 443, 'path': path}))
```
In ANSI SQL, URL formatting requires string concatenation with null handling. APL's `format_url` simplifies this operation.
```sql SQL example theme={null}
SELECT CONCAT(scheme, '://', host, COALESCE(CONCAT(':', port), ''), path) AS url FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend url = format_url(dynamic({'scheme': scheme, 'host': host, 'port': port, 'path': path}))
```
## Usage
### Syntax
```kusto theme={null}
format_url(url_parts)
```
### Parameters
| Name | Type | Required | Description |
| ---------- | ------- | -------- | ------------------------------------------------------------------------------------------------------ |
| url\_parts | dynamic | Yes | A dynamic object containing URL components: scheme, host, path, port, fragment, user, password, query. |
### Returns
Returns a properly formatted URL string constructed from the provided components.
## Use case examples
Reconstruct full URLs from parsed components to analyze complete request patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend full_url = format_url(dynamic({'scheme': 'https', 'host': 'api.example.com', 'path': uri}))
| project _time, method, status, full_url
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20full_url%20%3D%20format_url\(dynamic\(%7B%27scheme%27%3A%20%27https%27%2C%20%27host%27%3A%20%27api.example.com%27%2C%20%27path%27%3A%20uri%7D\)\)%20%7C%20project%20_time%2C%20method%2C%20status%2C%20full_url%20%7C%20limit%2010%22%7D)
**Output**
| \_time | method | status | full\_url |
| -------------------- | ------ | ------ | -------------------------------------- |
| 2024-11-06T10:00:00Z | GET | 200 | `https://api.example.com/api/users` |
| 2024-11-06T10:01:00Z | POST | 201 | `https://api.example.com/api/orders` |
| 2024-11-06T10:02:00Z | GET | 200 | `https://api.example.com/api/products` |
This query reconstructs full URLs from URI paths by adding the scheme and host, useful for generating clickable links in reports.
Build URLs from trace attributes to identify the full endpoints being called.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_url = format_url(dynamic({'scheme': 'http', 'host': 'localhost', 'port': 8080, 'path': strcat('/', ['service.name'])}))
| project _time, ['service.name'], service_url, trace_id
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20service_url%20%3D%20format_url\(dynamic\(%7B%27scheme%27%3A%20%27http%27%2C%20%27host%27%3A%20%27localhost%27%2C%20%27port%27%3A%208080%2C%20%27path%27%3A%20strcat\(%27%2F%27%2C%20%5B%27service.name%27%5D\)%7D\)\)%20%7C%20project%20_time%2C%20%5B%27service.name%27%5D%2C%20service_url%2C%20trace_id%20%7C%20limit%2010%22%7D)
**Output**
| \_time | service.name | service\_url | trace\_id |
| -------------------- | ------------ | -------------------------------- | --------- |
| 2024-11-06T10:00:00Z | frontend | `http://localhost:8080/frontend` | abc123 |
| 2024-11-06T10:01:00Z | checkout | `http://localhost:8080/checkout` | def456 |
| 2024-11-06T10:02:00Z | cart | `http://localhost:8080/cart` | ghi789 |
This query constructs service URLs from trace data, helping visualize the actual endpoints in a distributed system.
Construct URLs with authentication parameters to audit access attempts.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend access_url = format_url(dynamic({'scheme': 'https', 'host': 'secure.example.com', 'path': uri, 'user': id}))
| project _time, access_url, status, ['geo.country']
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20access_url%20%3D%20format_url\(dynamic\(%7B'scheme'%3A%20'https'%2C%20'host'%3A%20'secure.example.com'%2C%20'path'%3A%20uri%2C%20'user'%3A%20id%7D\)\)%20%7C%20project%20_time%2C%20access_url%2C%20status%2C%20%5B'geo.country'%5D%20%7C%20limit%2010%22%7D)
**Output**
| \_time | access\_url | status | geo.country |
| -------------------- | ---------------------------------------------------------------------------------------------- | ------ | ------------- |
| 2024-11-06T10:00:00Z | [https://user123@secure.example.com/admin](https://user123@secure.example.com/admin) | 403 | United States |
| 2024-11-06T10:01:00Z | [https://user456@secure.example.com/api/secret](https://user456@secure.example.com/api/secret) | 401 | Unknown |
This query constructs complete URLs including user information for failed authentication attempts, helping security teams understand the full context of access attempts.
## List of related functions
* [parse\_url](/apl/scalar-functions/string-functions/parse-url): Parses a URL string into its components. Use this to reverse the formatting operation and extract URL parts.
* [parse\_urlquery](/apl/scalar-functions/string-functions/parse-urlquery): Parses URL query parameters. Use this when you need to work with query string parameters specifically.
* [url\_encode](/apl/scalar-functions/string-functions/url-encode): Encodes a string for safe use in URLs. Use this to encode individual URL components before formatting.
* [strcat](/apl/scalar-functions/string-functions/strcat): Concatenates strings. Use this for simple URL construction without the structure of format\_url.
# gettype
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/gettype
This page explains how to use the gettype function in APL.
The `gettype` function returns the runtime type of its argument as a string. Use this function when you need to determine the data type of fields, validate data structures, or debug type-related issues in your queries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `typeof` to check types. APL's `gettype` provides similar functionality with consistent type names.
```sql Splunk example theme={null}
| eval field_type=typeof(field_name)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend field_type = gettype(field_name)
```
In ANSI SQL, type checking varies by database. APL's `gettype` provides a standardized approach to runtime type detection.
```sql SQL example theme={null}
SELECT TYPEOF(field_name) AS field_type FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend field_type = gettype(field_name)
```
## Usage
### Syntax
```kusto theme={null}
gettype(expression)
```
### Parameters
| Name | Type | Required | Description |
| ---------- | ---- | -------- | ------------------------------------------------ |
| expression | any | Yes | The expression whose type you want to determine. |
### Returns
Returns a string representing the runtime type: `string`, `int`, `long`, `real`, `bool`, `datetime`, `timespan`, `dynamic`, `array`, `dictionary`, or `null`.
## Use case examples
Identify the data types of fields to ensure proper query operations and data validation.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend status_type = gettype(status),
duration_type = gettype(req_duration_ms),
time_type = gettype(_time)
| project status, status_type, req_duration_ms, duration_type, _time, time_type
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20status_type%20%3D%20gettype\(status\)%2C%20duration_type%20%3D%20gettype\(req_duration_ms\)%2C%20time_type%20%3D%20gettype\(_time\)%20%7C%20project%20status%2C%20status_type%2C%20req_duration_ms%2C%20duration_type%2C%20_time%2C%20time_type%20%7C%20limit%2010%22%7D)
**Output**
| status | status\_type | req\_duration\_ms | duration\_type | \_time | time\_type |
| ------ | ------------ | ----------------- | -------------- | -------------------- | ---------- |
| 200 | string | 145 | long | 2024-11-06T10:00:00Z | datetime |
| 404 | string | 89 | long | 2024-11-06T10:01:00Z | datetime |
| 500 | string | 234 | long | 2024-11-06T10:02:00Z | datetime |
This query identifies the data types of key fields in HTTP logs, helping ensure that data is in the expected format for analysis and troubleshooting type-related query issues.
Validate trace field types to ensure proper data ingestion and processing.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_type = gettype(['service.name']),
duration_type = gettype(duration),
kind_type = gettype(kind)
| summarize type_counts = count() by service_type, duration_type, kind_type
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20service_type%20%3D%20gettype\(%5B%27service.name%27%5D\)%2C%20duration_type%20%3D%20gettype\(duration\)%2C%20kind_type%20%3D%20gettype\(kind\)%20%7C%20summarize%20type_counts%20%3D%20count\(\)%20by%20service_type%2C%20duration_type%2C%20kind_type%22%7D)
**Output**
| service\_type | duration\_type | kind\_type | type\_counts |
| ------------- | -------------- | ---------- | ------------ |
| string | timespan | string | 8765 |
This query validates the types of trace fields, helping identify data quality issues where fields might have unexpected types due to ingestion problems.
Detect type inconsistencies in security logs that might indicate data manipulation or logging errors.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend id_type = gettype(id),
status_type = gettype(status),
uri_type = gettype(uri)
| summarize failed_attempts = count() by id_type, status_type, uri_type
| sort by failed_attempts desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20id_type%20%3D%20gettype\(id\)%2C%20status_type%20%3D%20gettype\(status\)%2C%20uri_type%20%3D%20gettype\(uri\)%20%7C%20summarize%20failed_attempts%20%3D%20count\(\)%20by%20id_type%2C%20status_type%2C%20uri_type%20%7C%20sort%20by%20failed_attempts%20desc%22%7D)
**Output**
| id\_type | status\_type | uri\_type | failed\_attempts |
| -------- | ------------ | --------- | ---------------- |
| string | string | string | 2341 |
This query validates field types in failed authentication logs, helping detect anomalies where expected string fields might have different types due to injection attempts or data corruption.
## List of related functions
* [isnull](/apl/scalar-functions/string-functions/isnull): Checks if a value is null. Use this to specifically test for null values rather than getting the type.
* [isnotnull](/apl/scalar-functions/string-functions/isnotnull): Checks if a value is not null. Use this in filters when you need to exclude null values.
* [parse\_json](/apl/scalar-functions/string-functions/parse-json): Parses JSON strings into dynamic types. Use this before gettype when working with JSON data.
# indexof
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/indexof
This page explains how to use the indexof function in APL.
The `indexof` function reports the zero-based index of the first occurrence of a specified string within an input string. Use this function to find the position of substrings, validate string formats, or extract parts of strings based on delimiter positions.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you might use `searchmatch` or string manipulation. APL's `indexof` provides a direct way to find substring positions.
```sql Splunk example theme={null}
| eval pos=if(match(field, "search"), strpos(field, "search"), -1)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend pos = indexof(field, 'search')
```
In ANSI SQL, you use `POSITION()` or `INSTR()` to find substring positions. APL's `indexof` provides similar functionality with additional parameters.
```sql SQL example theme={null}
SELECT POSITION('search' IN field) - 1 AS pos FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend pos = indexof(field, 'search')
```
## Usage
### Syntax
```kusto theme={null}
indexof(source, lookup, start_index, length, occurrence)
```
### Parameters
| Name | Type | Required | Description |
| ------------ | ------ | -------- | ----------------------------------------------------------------------------- |
| source | string | Yes | The input string to search within. |
| lookup | string | Yes | The string to search for. |
| start\_index | int | No | The position to start searching from (default: 0). |
| length | int | No | Number of character positions to examine. Use -1 for unlimited (default: -1). |
| occurrence | int | No | The occurrence number to find (default: 1 for first occurrence). |
### Returns
Returns the zero-based index position of the first occurrence of the lookup string, or -1 if not found.
## Use case examples
Find the position of API version indicators in URIs to categorize and analyze API usage patterns.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend api_pos = indexof(uri, '/api/')
| where api_pos >= 0
| extend has_version = indexof(uri, '/v', api_pos)
| project _time, uri, api_pos, has_version, method, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20api_pos%20%3D%20indexof\(uri%2C%20%27%2Fapi%2F%27\)%20%7C%20where%20api_pos%20%3E%3D%200%20%7C%20extend%20has_version%20%3D%20indexof\(uri%2C%20%27%2Fv%27%2C%20api_pos\)%20%7C%20project%20_time%2C%20uri%2C%20api_pos%2C%20has_version%2C%20method%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | api\_pos | has\_version | method | status |
| -------------------- | -------------- | -------- | ------------ | ------ | ------ |
| 2024-11-06T10:00:00Z | /api/v2/users | 0 | 4 | GET | 200 |
| 2024-11-06T10:01:00Z | /api/products | 0 | -1 | GET | 200 |
| 2024-11-06T10:02:00Z | /api/v1/orders | 0 | 4 | POST | 201 |
This query finds the position of API indicators in URIs, helping identify versioned versus unversioned API endpoints.
Locate service name delimiters to extract service identifiers from composite names.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend dash_pos = indexof(['service.name'], '-')
| where dash_pos >= 0
| extend service_prefix = substring(['service.name'], 0, dash_pos)
| summarize span_count = count() by service_prefix
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20dash_pos%20%3D%20indexof\(%5B%27service.name%27%5D%2C%20%27-%27\)%20%7C%20where%20dash_pos%20%3E%3D%200%20%7C%20extend%20service_prefix%20%3D%20substring\(%5B%27service.name%27%5D%2C%200%2C%20dash_pos\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20service_prefix%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service\_prefix | span\_count |
| --------------- | ----------- |
| otel | 8765 |
| service | 4321 |
| app | 2345 |
This query uses `indexof` to find delimiter positions in service names, enabling extraction of service prefixes for grouping and analysis.
Detect SQL injection attempts by finding the position of SQL keywords in URIs.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend union_pos = indexof(tolower(uri), 'union'),
select_pos = indexof(tolower(uri), 'select'),
drop_pos = indexof(tolower(uri), 'drop')
| where union_pos >= 0 or select_pos >= 0 or drop_pos >= 0
| project _time, uri, union_pos, select_pos, drop_pos, id, status, ['geo.country']
| sort by _time desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20union_pos%20%3D%20indexof\(tolower\(uri\)%2C%20%27union%27\)%2C%20select_pos%20%3D%20indexof\(tolower\(uri\)%2C%20%27select%27\)%2C%20drop_pos%20%3D%20indexof\(tolower\(uri\)%2C%20%27drop%27\)%20%7C%20where%20union_pos%20%3E%3D%200%20or%20select_pos%20%3E%3D%200%20or%20drop_pos%20%3E%3D%200%20%7C%20project%20_time%2C%20uri%2C%20union_pos%2C%20select_pos%2C%20drop_pos%2C%20id%2C%20status%2C%20%5B%27geo.country%27%5D%20%7C%20sort%20by%20_time%20desc%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | union\_pos | select\_pos | drop\_pos | id | status | geo.country |
| -------------------- | ---------------------- | ---------- | ----------- | --------- | ------- | ------ | ----------- |
| 2024-11-06T10:00:00Z | /api?id=1'union select | -1 | 11 | -1 | user123 | 403 | Unknown |
| 2024-11-06T10:01:00Z | /search?q=drop table | -1 | -1 | 10 | user456 | 403 | Russia |
This query identifies potential SQL injection attempts by finding the position of SQL keywords in URIs, helping security teams detect and respond to attacks.
## List of related functions
* [substring](/apl/scalar-functions/string-functions/substring): Extracts a substring from a source string. Use this together with indexof to extract parts of strings based on found positions.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string. Use this with indexof to calculate positions relative to string length.
* [extract](/apl/scalar-functions/string-functions/extract): Extracts substrings using regular expressions. Use this when you need pattern matching instead of simple substring positions.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by delimiters. Use this when you want to tokenize rather than find positions.
# indexof_regex
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/indexof-regex
This page explains how to use the indexof_regex function in APL.
Use the `indexof_regex` function to find the position of the first match of a regular expression in a string. The function is helpful when you want to locate a pattern within a larger text field and take action based on its position. For example, you can use `indexof_regex` to extract fields from semi-structured logs, validate string formats, or trigger alerts when specific patterns appear in log data.
The function returns the zero-based index of the first match. If no match is found, it returns `-1`. Use `indexof_regex` when you need more flexibility than simple substring search (`indexof`), especially when working with dynamic or non-fixed patterns.
All regex functions of APL use the [RE2 regex syntax](https://github.com/google/re2/wiki/Syntax).
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Use `match()` in Splunk SPL to perform regular expression matching. However, `match()` returns a Boolean, not the match position. APL’s `indexof_regex` is similar to combining `match()` with additional logic to extract position, which isn’t natively supported in SPL.
```sql Splunk example theme={null}
... | eval match_index=if(match(field, "pattern"), 0, -1)
```
```kusto APL equivalent theme={null}
['dataset']
| extend match_index = indexof_regex(field, 'pattern')
```
ANSI SQL doesn’t have a built-in function to return the index of a regex match. You typically use `REGEXP_LIKE` for Boolean evaluation. `indexof_regex` provides a more direct and powerful way to find the exact match position in APL.
```sql SQL example theme={null}
SELECT CASE WHEN REGEXP_LIKE(field, 'pattern') THEN 0 ELSE -1 END FROM table;
```
```kusto APL equivalent theme={null}
['dataset']
| extend match_index = indexof_regex(field, 'pattern')
```
## Usage
### Syntax
```kusto theme={null}
indexof_regex(string, match [, start [, occurrence [, length]]])
```
### Parameters
| Name | Type | Required | Description |
| ---------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| string | string | Yes | The input text to inspect. |
| match | string | Yes | The regular expression pattern to search for. |
| start | int | | The index in the string where to begin the search. If negative, the function starts that many characters from the end. |
| occurrence | int | | Which instance of the pattern to match. Defaults to `1` if not specified. |
| length | int | | The number of characters to search through. Use `-1` to search to the end of the string. |
### Returns
The function returns the position (starting at zero) where the pattern first matches within the string. If the pattern isn’t found, the result is `-1`.
The function returns `null` in the following cases:
* The `start` value is negative.
* The `occurrence` value is less than 1.
* The `length` is set to a value below `-1`.
## Use case examples
Use `indexof_regex` to detect whether the URI in a log entry contains an encoded user ID by checking for patterns like `user-[0-9]+`.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend user_id_pos = indexof_regex(uri, 'user-[0-9]+')
| where user_id_pos != -1
| project _time, id, uri, user_id_pos
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20user_id_pos%20%3D%20indexof_regex\(uri%2C%20'user-%5B0-9%5D%2B'\)%20%7C%20where%20user_id_pos%20!%3D%20-1%20%7C%20project%20_time%2C%20id%2C%20uri%2C%20user_id_pos%22%7D)
**Output**
| \_time | id | uri | user\_id\_pos |
| -------------------- | ------ | ------------------------ | ------------- |
| 2025-06-10T12:34:56Z | user42 | /api/user-12345/settings | 5 |
| 2025-06-10T12:35:07Z | user91 | /v2/user-6789/dashboard | 4 |
The query finds log entries where the URI contains a user ID pattern and shows the position of the match in the URI string.
Use `indexof_regex` to detect trace IDs that include a specific structure, such as four groups of hex digits.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend match_index = indexof_regex(trace_id, '^[0-9a-f]{8}-[0-9a-f]{4}')
| where match_index == 0
| project _time, trace_id, match_index
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20match_index%20%3D%20indexof_regex\(trace_id%2C%20'%5E%5B0-9a-f%5D%7B8%7D-%5B0-9a-f%5D%7B4%7D'\)%20%7C%20where%20match_index%20%3D%3D%200%20%7C%20project%20_time%2C%20trace_id%2C%20match_index%22%7D)
**Output**
| \_time | trace\_id | match\_index |
| -------------------- | ------------------------------------ | ------------ |
| 2025-06-10T08:23:12Z | ab12cd34-1234-5678-9abc-def123456789 | 0 |
| 2025-06-10T08:24:55Z | fe98ba76-4321-abcd-8765-fedcba987654 | 0 |
This query finds spans where the trace ID begins with a specific regex pattern, helping validate span ID formatting.
Use `indexof_regex` to locate suspicious request patterns such as attempts to access system files (`/etc/passwd`).
**Query**
```kusto theme={null}
['sample-http-logs']
| extend passwd_index = indexof_regex(uri, '/etc/passwd')
| where passwd_index != -1
| project _time, id, uri, passwd_index
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20passwd_index%20%3D%20indexof_regex\(uri%2C%20'%2Fetc%2Fpasswd'\)%20%7C%20where%20passwd_index%20!%3D%20-1%20%7C%20project%20_time%2C%20id%2C%20uri%2C%20passwd_index%22%7D)
**Output**
| \_time | id | uri | passwd\_index |
| -------------------- | ------ | ------------------------------ | ------------- |
| 2025-06-10T10:15:45Z | user88 | /cgi-bin/view?path=/etc/passwd | 20 |
This query detects HTTP requests attempting to access sensitive file paths, a common indicator of intrusion attempts.
# isascii
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/isascii
This page explains how to use the isascii function in APL.
Use the `isascii` function to check whether a string contains only ASCII characters. It returns `true` if every character in the input string belongs to the ASCII character set (for example, character codes 0–127) and `false` otherwise.
The function is useful in scenarios where you want to detect non-ASCII text in logs, validate inputs for encoding compliance, or identify potential anomalies introduced by copy-pasted foreign characters or malformed input in user-submitted data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
Splunk SPL doesn’t have a direct equivalent to `isascii`. To achieve similar functionality, you typically need to use `match()` or custom regular expressions. In contrast, APL provides `isascii` as a simple built-in function, making the check much more concise and performant.
```sql Splunk example theme={null}
... | eval is_ascii=if(match(field, "^[\x00-\x7F]+$"), "true", "false")
```
```kusto APL equivalent theme={null}
datatable(input:string)
[
'hello',
'こんにちは'
]
| extend is_ascii = isascii(input)
```
ANSI SQL doesn’t provide a built-in `isascii` function. You often need to simulate it using regular expressions or character code checks. APL simplifies this with the dedicated `isascii` function.
```sql SQL example theme={null}
SELECT input,
CASE WHEN input ~ '^[\x00-\x7F]+$' THEN true ELSE false END AS is_ascii
FROM my_table;
```
```kusto APL equivalent theme={null}
datatable(input:string)
[
'hello',
'こんにちは'
]
| extend is_ascii = isascii(input)
```
## Usage
### Syntax
```kusto theme={null}
isascii(value)
```
### Parameters
| Name | Type | Description |
| ----- | ------ | ------------------------------------------- |
| value | string | The input string to check for ASCII content |
### Returns
A `bool` value:
* `true` if all characters in `value` are ASCII characters.
* `false` if any character is outside the ASCII range.
## Use case examples
Identify non-ASCII characters in request URIs to detect unusual or malformed traffic.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend is_ascii_uri = isascii(uri)
| summarize count() by is_ascii_uri
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20is_ascii_uri%20%3D%20isascii\(uri\)%20%7C%20summarize%20count\(\)%20by%20is_ascii_uri%22%7D)
**Output**
| is\_ascii\_uri | count\_ |
| -------------- | ------- |
| true | 14250 |
| false | 130 |
This query flags requests with non-ASCII characters in the `uri` field. These entries can indicate abnormal requests or encoding issues in log data.
Detect non-ASCII span IDs, which could indicate instrumentation bugs or encoding anomalies.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend is_ascii_span_id = isascii(span_id)
| summarize count() by is_ascii_span_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20is_ascii_span_id%20%3D%20isascii\(span_id\)%20%7C%20summarize%20count\(\)%20by%20is_ascii_span_id%22%7D)
**Output**
| is\_ascii\_span\_id | count\_ |
| ------------------- | ------- |
| true | 28700 |
| false | 2 |
This query validates that span IDs in your telemetry traces contain only ASCII characters. Non-ASCII values may hint at bugs or corrupted trace headers.
Identify requests where user IDs contain non-ASCII characters, which can help detect suspicious or malformed entries.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend is_ascii_id = isascii(id)
| summarize count() by is_ascii_id
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20is_ascii_id%20%3D%20isascii\(id\)%20%7C%20summarize%20count\(\)%20by%20is_ascii_id%22%7D)
**Output**
| is\_ascii\_id | count |
| ------------- | ----- |
| true | 11900 |
| false | 350 |
This query detects potentially malicious or malformed user IDs by filtering for non-ASCII values in the `id` field.
# isempty
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/isempty
This page explains how to use the isempty function in APL.
The `isempty` function returns true if the argument is an empty string or null. Use this function to filter out records with missing or empty string values, validate data completeness, or identify fields that need default values.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you check for empty values using conditions like `field=""` or `isnull(field)`. APL's `isempty` combines both checks.
```sql Splunk example theme={null}
| where field="" OR isnull(field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isempty(field)
```
In ANSI SQL, you check for empty or null values using separate conditions. APL's `isempty` provides a more concise approach.
```sql SQL example theme={null}
SELECT * FROM logs WHERE field IS NULL OR field = '';
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isempty(field)
```
## Usage
### Syntax
```kusto theme={null}
isempty(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ----------------------------------------- |
| value | scalar | Yes | The value to check for emptiness or null. |
### Returns
Returns `true` if the value is an empty string or null, otherwise returns `false`.
## Use case examples
Identify HTTP requests with missing or empty geographic information for data quality monitoring.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_empty_city = isempty(['geo.city']),
has_empty_country = isempty(['geo.country'])
| where has_empty_city or has_empty_country
| summarize incomplete_records = count() by has_empty_city, has_empty_country, status
| sort by incomplete_records desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20has_empty_city%20%3D%20isempty\(%5B%27geo.city%27%5D\)%2C%20has_empty_country%20%3D%20isempty\(%5B%27geo.country%27%5D\)%20%7C%20where%20has_empty_city%20or%20has_empty_country%20%7C%20summarize%20incomplete_records%20%3D%20count\(\)%20by%20has_empty_city%2C%20has_empty_country%2C%20status%20%7C%20sort%20by%20incomplete_records%20desc%22%7D)
**Output**
| has\_empty\_city | has\_empty\_country | status | incomplete\_records |
| ---------------- | ------------------- | ------ | ------------------- |
| true | false | 200 | 1234 |
| true | true | 404 | 567 |
| false | true | 500 | 234 |
This query identifies requests with incomplete geographic data, helping assess data quality and identify potential issues with geo-IP lookups.
Find traces with missing service information to identify instrumentation gaps.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend empty_service = isempty(['service.name']),
empty_kind = isempty(kind)
| where empty_service or empty_kind
| summarize problematic_spans = count() by empty_service, empty_kind
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20empty_service%20%3D%20isempty\(%5B%27service.name%27%5D\)%2C%20empty_kind%20%3D%20isempty\(kind\)%20%7C%20where%20empty_service%20or%20empty_kind%20%7C%20summarize%20problematic_spans%20%3D%20count\(\)%20by%20empty_service%2C%20empty_kind%22%7D)
**Output**
| empty\_service | empty\_kind | problematic\_spans |
| -------------- | ----------- | ------------------ |
| false | true | 234 |
| true | false | 89 |
This query identifies spans with missing required fields, helping improve observability instrumentation by highlighting gaps in trace data.
Detect authentication attempts with missing user identifiers that might indicate anonymized or suspicious activity.
**Query**
```kusto theme={null}
['sample-http-logs']
| where status == '401' or status == '403'
| extend empty_id = isempty(id)
| summarize failed_attempts = count(), empty_id_attempts = countif(empty_id) by status
| extend anonymous_percentage = round(100.0 * empty_id_attempts / failed_attempts, 2)
| sort by failed_attempts desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20status%20%3D%3D%20%27401%27%20or%20status%20%3D%3D%20%27403%27%20%7C%20extend%20empty_id%20%3D%20isempty\(id\)%20%7C%20summarize%20failed_attempts%20%3D%20count\(\)%2C%20empty_id_attempts%20%3D%20countif\(empty_id\)%20by%20status%20%7C%20extend%20anonymous_percentage%20%3D%20round\(100.0%20*%20empty_id_attempts%20%2F%20failed_attempts%2C%202\)%20%7C%20sort%20by%20failed_attempts%20desc%22%7D)
**Output**
| status | failed\_attempts | empty\_id\_attempts | anonymous\_percentage |
| ------ | ---------------- | ------------------- | --------------------- |
| 401 | 1234 | 345 | 27.96 |
| 403 | 987 | 123 | 12.46 |
This query analyzes the percentage of failed authentication attempts without user IDs, helping security teams identify potential anonymous attack patterns.
## List of related functions
* [isnotempty](/apl/scalar-functions/string-functions/isnotempty): Returns true if a value is not empty and not null. Use this for the inverse check of isempty.
* [isnull](/apl/scalar-functions/string-functions/isnull): Checks only if a value is null. Use this when you specifically need to test for null without checking for empty strings.
* [coalesce](/apl/scalar-functions/string-functions/coalesce): Returns the first non-null or non-empty value. Use this to provide default values for empty fields.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string. Use this when you need to check if a string has content beyond just emptiness.
# isnotempty
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/isnotempty
This page explains how to use the isnotempty function in APL.
The `isnotempty` function returns true if the argument isn’t an empty string and isn’t null. Use this function to filter for records with valid, non-empty values, ensure data quality, or validate that required fields contain actual content.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you check for non-empty values using conditions like `field!=""` and `isnotnull(field)`. APL's `isnotempty` combines both checks.
```sql Splunk example theme={null}
| where field!="" AND isnotnull(field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnotempty(field)
```
In ANSI SQL, you check for non-empty and non-null values using separate conditions. APL's `isnotempty` provides a more concise approach.
```sql SQL example theme={null}
SELECT * FROM logs WHERE field IS NOT NULL AND field <> '';
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnotempty(field)
```
## Usage
### Syntax
```kusto theme={null}
isnotempty(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | -------------------------------------------------- |
| value | scalar | Yes | The value to check for non-emptiness and non-null. |
### Returns
Returns `true` if the value is not an empty string and not null, otherwise returns `false`.
## Use case examples
Filter HTTP logs to only include requests with valid geographic information for accurate location-based analytics.
**Query**
```kusto theme={null}
['sample-http-logs']
| where isnotempty(['geo.city']) and isnotempty(['geo.country'])
| summarize request_count = count() by ['geo.city'], ['geo.country']
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20isnotempty\(%5B%27geo.city%27%5D\)%20and%20isnotempty\(%5B%27geo.country%27%5D\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20%5B%27geo.city%27%5D%2C%20%5B%27geo.country%27%5D%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| geo.city | geo.country | request\_count |
| -------- | -------------- | -------------- |
| New York | United States | 2341 |
| London | United Kingdom | 1987 |
| Tokyo | Japan | 1654 |
| Paris | France | 1432 |
This query filters requests to only include those with complete geographic information, ensuring accurate location-based analysis without null or empty values.
Analyze only traces with complete service information to ensure accurate service performance metrics.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where isnotempty(['service.name']) and isnotempty(kind)
| summarize avg_duration = avg(duration), span_count = count() by ['service.name'], kind
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20where%20isnotempty\(%5B%27service.name%27%5D\)%20and%20isnotempty\(kind\)%20%7C%20summarize%20avg_duration%20%3D%20avg\(duration\)%2C%20span_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%2C%20kind%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | kind | avg\_duration | span\_count |
| ------------ | -------- | ------------- | ----------- |
| frontend | server | 125ms | 4532 |
| checkout | client | 89ms | 3421 |
| cart | internal | 56ms | 2987 |
This query filters traces to only include spans with complete service and kind information, ensuring reliable performance analysis without incomplete data.
Identify authenticated users by filtering out requests without valid user identifiers.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend authenticated = isnotempty(id)
| summarize total_attempts = count(), authenticated_attempts = countif(authenticated) by status
| extend authenticated_percentage = round(100.0 * authenticated_attempts / total_attempts, 2)
| sort by total_attempts desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20authenticated%20%3D%20isnotempty\(id\)%20%7C%20summarize%20total_attempts%20%3D%20count\(\)%2C%20authenticated_attempts%20%3D%20countif\(authenticated\)%20by%20status%20%7C%20extend%20authenticated_percentage%20%3D%20round\(100.0%20*%20authenticated_attempts%20%2F%20total_attempts%2C%202\)%20%7C%20sort%20by%20total_attempts%20desc%22%7D)
**Output**
| status | total\_attempts | authenticated\_attempts | authenticated\_percentage |
| ------ | --------------- | ----------------------- | ------------------------- |
| 401 | 1234 | 889 | 72.04 |
| 403 | 987 | 864 | 87.53 |
This query distinguishes between authenticated and anonymous failed access attempts by checking if user IDs are present, helping security teams understand attack patterns.
## List of related functions
* [isempty](/apl/scalar-functions/string-functions/isempty): Returns true if a value is empty or null. Use this for the inverse check of isnotempty.
* [isnotnull](/apl/scalar-functions/string-functions/isnotnull): Checks only if a value is not null. Use this when you specifically need to test for null without checking for empty strings.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string. Use this when you need to ensure strings have minimum content length beyond just being non-empty.
* [coalesce](/apl/scalar-functions/string-functions/coalesce): Returns the first non-null or non-empty value. Use this to select from multiple fields or provide defaults.
# isnotnull
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/isnotnull
This page explains how to use the isnotnull function in APL.
The `isnotnull` function returns true if the argument isn’t null. Use this function to filter for records with defined values, validate data presence, or distinguish between null and other values including empty strings.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you check for non-null values using `isnotnull()` function. APL's `isnotnull` works the same way.
```sql Splunk example theme={null}
| where isnotnull(field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnotnull(field)
```
In ANSI SQL, you check for non-null values using `IS NOT NULL`. APL's `isnotnull` provides the same functionality with function syntax.
```sql SQL example theme={null}
SELECT * FROM logs WHERE field IS NOT NULL;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnotnull(field)
```
## Usage
### Syntax
```kusto theme={null}
isnotnull(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | -------------------------------- |
| value | scalar | Yes | The value to check for non-null. |
### Returns
Returns `true` if the value is not null, otherwise returns `false`. Note that empty strings return `true` because they are not null.
## Use case examples
Filter HTTP logs to only include requests where duration information is available for performance analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| where isnotnull(req_duration_ms)
| summarize avg_duration = avg(req_duration_ms),
max_duration = max(req_duration_ms),
request_count = count() by status
| sort by avg_duration desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20where%20isnotnull\(req_duration_ms\)%20%7C%20summarize%20avg_duration%20%3D%20avg\(req_duration_ms\)%2C%20max_duration%20%3D%20max\(req_duration_ms\)%2C%20request_count%20%3D%20count\(\)%20by%20status%20%7C%20sort%20by%20avg_duration%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | avg\_duration | max\_duration | request\_count |
| ------ | ------------- | ------------- | -------------- |
| 500 | 987.5 | 5432 | 234 |
| 200 | 145.3 | 3421 | 8765 |
| 404 | 89.7 | 987 | 1234 |
This query filters to only include requests with duration data, ensuring accurate performance metrics without skewing calculations with null values.
Analyze traces with recorded durations to calculate accurate service performance metrics.
**Query**
```kusto theme={null}
['otel-demo-traces']
| where isnotnull(duration)
| summarize p50_duration = percentile(duration, 50),
p95_duration = percentile(duration, 95),
trace_count = count() by ['service.name']
| sort by p95_duration desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20where%20isnotnull\(duration\)%20%7C%20summarize%20p50_duration%20%3D%20percentile\(duration%2C%2050\)%2C%20p95_duration%20%3D%20percentile\(duration%2C%2095\)%2C%20trace_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%20%7C%20sort%20by%20p95_duration%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | p50\_duration | p95\_duration | trace\_count |
| ------------ | ------------- | ------------- | ------------ |
| checkout | 234ms | 987ms | 3421 |
| frontend | 145ms | 654ms | 4532 |
| cart | 89ms | 456ms | 2987 |
This query ensures duration calculations are based only on spans with recorded timing data, preventing null values from affecting percentile calculations.
Track requests with identified users to analyze authenticated access patterns versus anonymous attempts.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend has_user_id = isnotnull(id)
| summarize requests_by_type = count() by has_user_id, status, ['geo.country']
| sort by requests_by_type desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20has_user_id%20%3D%20isnotnull\(id\)%20%7C%20summarize%20requests_by_type%20%3D%20count\(\)%20by%20has_user_id%2C%20status%2C%20%5B'geo.country'%5D%20%7C%20sort%20by%20requests_by_type%20desc%20%7C%20limit%2010%22%7D)
**Output**
| has\_user\_id | status | geo.country | requests\_by\_type |
| ------------- | ------ | ------------- | ------------------ |
| true | 401 | United States | 456 |
| true | 403 | Unknown | 345 |
| false | 401 | Russia | 234 |
| false | 403 | China | 123 |
This query distinguishes between authenticated and truly anonymous access attempts by checking for user ID presence, helping identify different attack patterns.
## List of related functions
* [isnull](/apl/scalar-functions/string-functions/isnull): Returns true if a value is null. Use this for the inverse check of isnotnull.
* [isnotempty](/apl/scalar-functions/string-functions/isnotempty): Checks if a value is not empty and not null. Use this when you need to ensure both conditions.
* [coalesce](/apl/scalar-functions/string-functions/coalesce): Returns the first non-null value from a list. Use this to provide default values for null fields.
* [gettype](/apl/scalar-functions/string-functions/gettype): Returns the type of a value. Use this to distinguish between null and other types.
# isnull
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/isnull
This page explains how to use the isnull function in APL.
The `isnull` function evaluates its argument and returns true if the argument is null. Use this function to identify missing data, filter out incomplete records, or validate that optional fields are absent.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you check for null values using `isnull()` function. APL's `isnull` works the same way.
```sql Splunk example theme={null}
| where isnull(field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnull(field)
```
In ANSI SQL, you check for null values using `IS NULL`. APL's `isnull` provides the same functionality with function syntax.
```sql SQL example theme={null}
SELECT * FROM logs WHERE field IS NULL;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| where isnull(field)
```
## Usage
### Syntax
```kusto theme={null}
isnull(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ---------------------------- |
| value | scalar | Yes | The value to check for null. |
### Returns
Returns `true` if the value is null, otherwise returns `false`. Note that empty strings return `false` because they are not null.
## Use case examples
Identify HTTP requests with missing duration information to assess data quality and completeness.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend missing_duration = isnull(req_duration_ms)
| summarize total_requests = count(),
missing_duration_count = countif(missing_duration),
missing_percentage = round(100.0 * countif(missing_duration) / count(), 2) by status
| sort by missing_duration_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20missing_duration%20%3D%20isnull\(req_duration_ms\)%20%7C%20summarize%20total_requests%20%3D%20count\(\)%2C%20missing_duration_count%20%3D%20countif\(missing_duration\)%2C%20missing_percentage%20%3D%20round\(100.0%20*%20countif\(missing_duration\)%20%2F%20count\(\)%2C%202\)%20by%20status%20%7C%20sort%20by%20missing_duration_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | total\_requests | missing\_duration\_count | missing\_percentage |
| ------ | --------------- | ------------------------ | ------------------- |
| 500 | 1234 | 123 | 9.97 |
| 200 | 8765 | 87 | 0.99 |
| 404 | 2341 | 23 | 0.98 |
This query identifies the percentage of requests missing duration data by status code, helping assess logging infrastructure reliability and identify potential issues.
Find traces with missing duration information to identify instrumentation problems.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend null_duration = isnull(duration)
| where null_duration
| summarize incomplete_spans = count() by ['service.name'], kind
| sort by incomplete_spans desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20null_duration%20%3D%20isnull\(duration\)%20%7C%20where%20null_duration%20%7C%20summarize%20incomplete_spans%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%2C%20kind%20%7C%20sort%20by%20incomplete_spans%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | kind | incomplete\_spans |
| --------------- | -------- | ----------------- |
| product-catalog | server | 234 |
| cart | internal | 123 |
| checkout | client | 89 |
This query identifies services with incomplete trace data, helping pinpoint instrumentation issues where duration information is not being captured properly.
Identify anonymous access attempts by finding requests without user identification.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend anonymous = isnull(id)
| summarize total_failures = count(),
anonymous_failures = countif(anonymous) by status, ['geo.country']
| extend anonymous_rate = round(100.0 * anonymous_failures / total_failures, 2)
| where anonymous_failures > 10
| sort by anonymous_failures desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20anonymous%20%3D%20isnull\(id\)%20%7C%20summarize%20total_failures%20%3D%20count\(\)%2C%20anonymous_failures%20%3D%20countif\(anonymous\)%20by%20status%2C%20%5B'geo.country'%5D%20%7C%20extend%20anonymous_rate%20%3D%20round\(100.0%20*%20anonymous_failures%20%2F%20total_failures%2C%202\)%20%7C%20where%20anonymous_failures%20%3E%2010%20%7C%20sort%20by%20anonymous_failures%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | geo.country | total\_failures | anonymous\_failures | anonymous\_rate |
| ------ | ----------- | --------------- | ------------------- | --------------- |
| 401 | Unknown | 567 | 345 | 60.85 |
| 403 | Russia | 234 | 189 | 80.77 |
| 401 | China | 198 | 156 | 78.79 |
This query identifies patterns of anonymous failed access attempts by country, helping security teams detect automated attacks or scanning activity.
## List of related functions
* [isnotnull](/apl/scalar-functions/string-functions/isnotnull): Returns true if a value is not null. Use this for the inverse check of isnull.
* [isempty](/apl/scalar-functions/string-functions/isempty): Checks if a value is empty or null. Use this when you need to check for both null and empty strings.
* [coalesce](/apl/scalar-functions/string-functions/coalesce): Returns the first non-null value from a list. Use this to provide default values for null fields.
* [gettype](/apl/scalar-functions/string-functions/gettype): Returns the type of a value. Use this to distinguish between null and other types.
# parse_bytes
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-bytes
This page explains how to use the parse_bytes function in APL.
The `parse_bytes` function parses a string representation of data size (like `1 KB`, `500 MB`) and returns the numeric value in bytes. Use this function to convert human-readable byte strings from logs or configuration files into numeric values for calculations and comparisons.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically need custom eval expressions to parse byte strings. APL's `parse_bytes` provides this functionality natively.
```sql Splunk example theme={null}
| eval bytes=case(
match(size_str, "KB"), tonumber(replace(size_str, " KB", "")) * 1024,
match(size_str, "MB"), tonumber(replace(size_str, " MB", "")) * 1048576,
true(), 0)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend bytes = parse_bytes(size_str)
```
In ANSI SQL, parsing byte strings requires complex CASE statements. APL's `parse_bytes` simplifies this operation.
```sql SQL example theme={null}
SELECT CASE
WHEN size_str LIKE '%KB%' THEN CAST(REPLACE(size_str, ' KB', '') AS FLOAT) * 1024
WHEN size_str LIKE '%MB%' THEN CAST(REPLACE(size_str, ' MB', '') AS FLOAT) * 1048576
ELSE 0
END AS bytes FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend bytes = parse_bytes(size_str)
```
## Usage
### Syntax
```kusto theme={null}
parse_bytes(bytes_string, base)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | ------------------------------------------------------------------------------------- |
| bytes\_string | string | Yes | A string representing a data size with units (for example, `1 KB`, `500 MB`, `2 GB`). |
| base | int | No | Either 2 (default, 1024-based) or 10 (1000-based) for unit calculations. |
### Returns
Returns the numeric value in bytes, or 0 if the string cannot be parsed.
## Use case examples
Parse human-readable size strings to analyze and aggregate data transfer volumes.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend size_bytes = parse_bytes('512 KB')
| where req_duration_ms > 3
| summarize total_bytes = sum(size_bytes), request_count = count() by status
| sort by total_bytes desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20size_bytes%20%3D%20parse_bytes\(%27512%20KB%27\)%20%7C%20where%20req_duration_ms%20%3E%203%20%7C%20summarize%20total_bytes%20%3D%20sum\(size_bytes\)%2C%20request_count%20%3D%20count\(\)%20by%20status%20%7C%20sort%20by%20total_bytes%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | total\_bytes | request\_count |
| ------ | ------------ | -------------- |
| 200 | 4587520 | 8765 |
| 500 | 1048576 | 2341 |
| 404 | 524288 | 1234 |
This query parses size strings to calculate total data transfer by HTTP status code, enabling volume-based analysis of API usage.
Convert size strings from span attributes to numeric bytes for threshold-based analysis.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend payload_size = parse_bytes('256 KB', 2)
| where duration > 100ms
| summarize avg_size = avg(payload_size), span_count = count() by ['service.name']
| sort by avg_size desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20payload_size%20%3D%20parse_bytes\(%27256%20KB%27%2C%202\)%20%7C%20where%20duration%20%3E%20100ms%20%7C%20summarize%20avg_size%20%3D%20avg\(payload_size\)%2C%20span_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%20%7C%20sort%20by%20avg_size%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | avg\_size | span\_count |
| ------------ | --------- | ----------- |
| checkout | 262144 | 3421 |
| frontend | 262144 | 4532 |
| cart | 262144 | 2987 |
This query converts size strings to bytes for numeric analysis of payload sizes across different services.
## List of related functions
* [format\_bytes](/apl/scalar-functions/string-functions/format-bytes): Formats numeric bytes as human-readable strings. Use this to reverse the parsing operation.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns the length of a string. Use this when you need string length rather than byte parsing.
# parse_csv
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-csv
This page explains how to use the parse_csv function in APL.
The `parse_csv` function splits a comma-separated values (CSV) string into an array of strings. Use this function to parse CSV-formatted log entries, configuration values, or any comma-delimited data into individual values for analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` or the `split` function to parse CSV. APL's `parse_csv` provides proper CSV parsing with quote handling.
```sql Splunk example theme={null}
| makemv delim="," field_name
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend values = parse_csv(field_name)
```
In ANSI SQL, parsing CSV requires string splitting functions that vary by database. APL's `parse_csv` provides standardized CSV parsing.
```sql SQL example theme={null}
SELECT STRING_TO_ARRAY(field_name, ',') AS values FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend values = parse_csv(field_name)
```
## Usage
### Syntax
```kusto theme={null}
parse_csv(csv_text)
```
### Parameters
| Name | Type | Required | Description |
| --------- | ------ | -------- | ---------------------------------------------------- |
| csv\_text | string | Yes | A string containing comma-separated values to parse. |
### Returns
Returns a string array containing the individual values from the CSV string. Properly handles quoted values and escaped characters.
## Use case examples
Parse comma-separated status codes or error types from log messages.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend status_list = parse_csv('200,201,204,304')
| extend is_success = status in (status_list)
| summarize request_count = count() by is_success, status
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20status_list%20%3D%20parse_csv\(%27200%2C201%2C204%2C304%27\)%20%7C%20extend%20is_success%20%3D%20status%20in%20\(status_list\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20is_success%2C%20status%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| is\_success | status | request\_count |
| ----------- | ------ | -------------- |
| true | 200 | 8765 |
| false | 404 | 2341 |
| false | 500 | 1234 |
| true | 304 | 987 |
This query parses a CSV list of success status codes and categorizes requests accordingly.
Parse comma-separated service lists from trace attributes or configuration.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_list = parse_csv('frontend,checkout,cart')
| extend is_monitored = ['service.name'] in (service_list)
| summarize span_count = count() by ['service.name'], is_monitored
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20service_list%20%3D%20parse_csv\(%27frontend%2Ccheckout%2Ccart%27\)%20%7C%20extend%20is_monitored%20%3D%20%5B%27service.name%27%5D%20in%20\(service_list\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%2C%20is_monitored%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | is\_monitored | span\_count |
| --------------- | ------------- | ----------- |
| frontend | true | 4532 |
| checkout | true | 3421 |
| cart | true | 2987 |
| product-catalog | false | 2341 |
This query parses a CSV list of monitored services and identifies which services are included in the monitoring scope.
Parse comma-separated allowlists or blocklists for security rule evaluation.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend blocked_ips = parse_csv('192.168.1.100,10.0.0.25,172.16.0.50')
| extend simulated_ip = '192.168.1.100'
| extend is_blocked = simulated_ip in (blocked_ips)
| where is_blocked
| summarize blocked_attempts = count() by status, ['geo.country']
| sort by blocked_attempts desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20blocked_ips%20%3D%20parse_csv\(%27192.168.1.100%2C10.0.0.25%2C172.16.0.50%27\)%20%7C%20extend%20simulated_ip%20%3D%20%27192.168.1.100%27%20%7C%20extend%20is_blocked%20%3D%20simulated_ip%20in%20\(blocked_ips\)%20%7C%20where%20is_blocked%20%7C%20summarize%20blocked_attempts%20%3D%20count\(\)%20by%20status%2C%20%5B%27geo.country%27%5D%20%7C%20sort%20by%20blocked_attempts%20desc%20%7C%20limit%2010%22%7D)
**Output**
| status | geo.country | blocked\_attempts |
| ------ | ----------- | ----------------- |
| 403 | Unknown | 234 |
| 401 | Russia | 123 |
This query parses a CSV blocklist and identifies requests from blocked IP addresses for security monitoring.
## List of related functions
* [split](/apl/scalar-functions/string-functions/split): Splits strings by any delimiter. Use this when working with non-CSV delimiters or when quote handling is not needed.
* [parse\_json](/apl/scalar-functions/string-functions/parse-json): Parses JSON strings into dynamic objects. Use this when working with JSON arrays rather than CSV.
* [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim): Concatenates strings with delimiters. Use this to create CSV strings from individual values.
* [extract\_all](/apl/scalar-functions/string-functions/extract-all): Extracts multiple regex matches. Use this for more complex parsing patterns beyond CSV.
# parse_json
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-json
This page explains how to use the parse_json function in APL.
The `parse_json` function interprets a string as JSON and returns the value as a dynamic object. Use this function to extract structured data from JSON-formatted log entries, API responses, or configuration values stored as JSON strings.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `spath` to parse JSON. APL's `parse_json` provides similar functionality with dynamic object support.
```sql Splunk example theme={null}
| spath input=json_field
| rename "field.name" as extracted_value
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parsed = parse_json(json_field)
| extend extracted_value = parsed['field']['name']
```
In ANSI SQL, JSON parsing varies by database with different functions. APL's `parse_json` provides standardized JSON parsing.
```sql SQL example theme={null}
SELECT JSON_EXTRACT(json_field, '$.field.name') AS extracted_value FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parsed = parse_json(json_field)
| extend extracted_value = parsed.field.name
```
## Usage
### Syntax
```kusto theme={null}
parse_json(json_string)
```
### Parameters
| Name | Type | Required | Description |
| ------------ | ------ | -------- | ---------------------------------------- |
| json\_string | string | Yes | A string containing valid JSON to parse. |
### Returns
Returns a dynamic object representing the parsed JSON. If the JSON is invalid, returns the original string.
## Use case examples
Parse JSON-formatted log messages to extract specific fields for analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend json_data = parse_json('{"response_time": 145, "cache_hit": true, "endpoint": "/api/users"}')
| extend response_time = toint(json_data.response_time)
| extend cache_hit = tobool(json_data.cache_hit)
| extend endpoint = tostring(json_data.endpoint)
| project _time, response_time, cache_hit, endpoint, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20json_data%20%3D%20parse_json\(%27%7B%5C%22response_time%5C%22%3A%20145%2C%20%5C%22cache_hit%5C%22%3A%20true%2C%20%5C%22endpoint%5C%22%3A%20%5C%22%2Fapi%2Fusers%5C%22%7D%27\)%20%7C%20extend%20response_time%20%3D%20toint\(json_data.response_time\)%20%7C%20extend%20cache_hit%20%3D%20tobool\(json_data.cache_hit\)%20%7C%20extend%20endpoint%20%3D%20tostring\(json_data.endpoint\)%20%7C%20project%20_time%2C%20response_time%2C%20cache_hit%2C%20endpoint%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | response\_time | cache\_hit | endpoint | status |
| -------------------- | -------------- | ---------- | ---------- | ------ |
| 2024-11-06T10:00:00Z | 145 | true | /api/users | 200 |
| 2024-11-06T10:01:00Z | 145 | true | /api/users | 200 |
This query parses JSON-formatted metadata from logs to extract performance metrics like response time and cache hit status.
Extract structured attributes from JSON-formatted span data.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend attrs = parse_json('{"http.method": "GET", "http.status_code": 200, "user.id": "12345"}')
| extend http_method = tostring(attrs['http.method'])
| extend http_status = toint(attrs['http.status_code'])
| extend user_id = tostring(attrs['user.id'])
| summarize span_count = count() by http_method, http_status
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20attrs%20%3D%20parse_json\(%27%7B%5C%22http.method%5C%22%3A%20%5C%22GET%5C%22%2C%20%5C%22http.status_code%5C%22%3A%20200%2C%20%5C%22user.id%5C%22%3A%20%5C%2212345%5C%22%7D%27\)%20%7C%20extend%20http_method%20%3D%20tostring\(attrs%5B%27http.method%27%5D\)%20%7C%20extend%20http_status%20%3D%20toint\(attrs%5B%27http.status_code%27%5D\)%20%7C%20extend%20user_id%20%3D%20tostring\(attrs%5B%27user.id%27%5D\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20http_method%2C%20http_status%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| http\_method | http\_status | span\_count |
| ------------ | ------------ | ----------- |
| GET | 200 | 8765 |
This query parses JSON attributes from OpenTelemetry spans to analyze HTTP request patterns.
Parse JSON-formatted security events to extract threat indicators.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend security_data = parse_json('{"threat_level": "high", "attack_type": "sql_injection", "blocked": true}')
| extend threat_level = tostring(security_data.threat_level)
| extend attack_type = tostring(security_data.attack_type)
| extend blocked = tobool(security_data.blocked)
| project _time, uri, threat_level, attack_type, blocked, id, ['geo.country']
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20security_data%20%3D%20parse_json\(%27%7B%5C%22threat_level%5C%22%3A%20%5C%22high%5C%22%2C%20%5C%22attack_type%5C%22%3A%20%5C%22sql_injection%5C%22%2C%20%5C%22blocked%5C%22%3A%20true%7D%27\)%20%7C%20extend%20threat_level%20%3D%20tostring\(security_data.threat_level\)%20%7C%20extend%20attack_type%20%3D%20tostring\(security_data.attack_type\)%20%7C%20extend%20blocked%20%3D%20tobool\(security_data.blocked\)%20%7C%20project%20_time%2C%20uri%2C%20threat_level%2C%20attack_type%2C%20blocked%2C%20id%2C%20%5B%27geo.country%27%5D%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | threat\_level | attack\_type | blocked | id | geo.country |
| -------------------- | ---------- | ------------- | -------------- | ------- | ------- | ----------- |
| 2024-11-06T10:00:00Z | /api/users | high | sql\_injection | true | user123 | Unknown |
| 2024-11-06T10:01:00Z | /admin | high | sql\_injection | true | user456 | Russia |
This query parses JSON-formatted security events to extract and analyze threat information from failed access attempts.
## Best practices
When working with JSON data in Axiom, consider the following best practices:
* **Prefer structured ingestion over runtime parsing:** If possible, structure your JSON data as separate fields during ingestion rather than storing it as a stringified JSON object. This provides better query performance and enables indexing on nested fields.
* **Use map fields for nested data:** For nested or unpredictable JSON structures, consider using [map fields](/apl/data-types/map-fields) instead of stringified JSON. Map fields allow you to query nested properties directly without using `parse_json` at query time.
* **Avoid mixed types:** When logging JSON data, ensure consistent field types across events. Mixed types (for example, sometimes a string, sometimes a number) can cause query issues. Use type conversion functions like `toint` or `tostring` when necessary.
* **Performance considerations:** Using `parse_json` at query time adds CPU overhead. For frequently queried JSON data, consider parsing during ingestion or using map fields for better performance.
## List of related functions
* [parse\_url](/apl/scalar-functions/string-functions/parse-url): Parses URLs into components. Use this specifically for URL parsing rather than general JSON.
* [parse\_csv](/apl/scalar-functions/string-functions/parse-csv): Parses CSV strings. Use this for comma-separated values rather than JSON.
* [todynamic](/apl/scalar-functions/conversion-functions/todynamic): Alias for parse\_json. Use either name based on your preference.
* [gettype](/apl/scalar-functions/string-functions/gettype): Returns the type of a value. Use this to check the types of parsed JSON fields.
# parse_path
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-path
This page explains how to use the parse_path function in APL.
Use the `parse_path` function to extract structured components from file paths, URIs, or URLs in your log and trace data. This function is useful when you want to decompose a full path into individual segments such as the directory, filename, extension, or query parameters for easier filtering, aggregation, or analysis.
You typically use `parse_path` in log analysis, OpenTelemetry traces, and security investigations to understand which resources are being accessed, identify routing patterns, or isolate endpoints with high error rates. It simplifies complex string parsing tasks and helps you normalize paths for comparisons and reporting.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, path or URL parsing often involves a combination of `spath`, `rex`, and `spath` field access logic. You typically write regular expressions or JSONPath selectors manually.
In APL, `parse_path` handles common URL and path structures for you automatically. It returns a dynamic object with fields like `directory`, `basename`, `extension`, `query`, and others.
```sql Splunk example theme={null}
... | rex field=uri "(?\/api\/v1\/[^\?]+)"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend path_parts = parse_path(uri)
| extend endpoint = path_parts.directory
```
ANSI SQL doesn’t have a built-in function for parsing structured paths. You often use a combination of `SUBSTRING`, `CHARINDEX`, or user-defined functions.
APL simplifies this task with `parse_path`, which returns a structured object from a URI or file path, removing the need for manual string manipulation.
```sql SQL example theme={null}
SELECT SUBSTRING(uri, 1, CHARINDEX('/', uri)) AS directory FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend directory = parse_path(uri).directory
```
## Usage
### Syntax
```kusto theme={null}
parse_path(source)
```
### Parameters
| Name | Type | Description |
| ------ | ------ | ---------------------------------------------------- |
| source | string | A string representing a path, file URI, or full URL. |
### Returns
Returns a dynamic object with the following fields:
* Scheme
* RootPath
* DirectoryPath
* DirectoryName
* Filename
* Extension
* AlternateDataStreamName
## Use case example
Extract endpoint directories and file extensions from HTTP request URIs.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend path_parts = parse_path(uri)
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20path_parts%20%3D%20parse_path\(uri\)%20%7C%20project%20_time%2C%20path_parts%22%7D)
**Output**
| \_time | path\_parts |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Jun 11, 10:39:16 | \{ "Filename": "users", "RootPath": "", "Scheme": "", "AlternateDataStream": "", "DirectoryName": "messages", "DirectoryPath": "/api/v1/messages", "Extension": "" } |
| Jun 11, 10:39:16 | \{ "Scheme": "", "AlternateDataStream": "", "DirectoryName": "background", "DirectoryPath": "/api/v1/textdata/background", "Extension": "", "Filename": "change", "RootPath": "" } |
| Jun 11, 10:39:16 | \{ "Filename": "users", "RootPath": "", "Scheme": "", "AlternateDataStream": "", "DirectoryName": "textdata", "DirectoryPath": "/api/v1/textdata", "Extension": "" } |
This query helps you identify which directories and file types receive the most traffic.
# parse_url
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-url
This page explains how to use the parse_url function in APL.
The `parse_url` function parses an absolute URL string into a dynamic object containing all URL components (scheme, host, port, path, query parameters, etc.). Use this function to extract and analyze specific parts of URLs from logs, web traffic data, or API requests.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` or URL-specific extractions. APL's `parse_url` provides structured URL parsing in one function.
```sql Splunk example theme={null}
| rex field=url "(?https?)://(?[^/]+)(?.*)"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parsed = parse_url(url)
| extend scheme = parsed.scheme, host = parsed.host, path = parsed.path
```
In ANSI SQL, URL parsing requires complex string manipulation. APL's `parse_url` provides structured parsing natively.
```sql SQL example theme={null}
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(url, '://', -1), '/', 1) AS host FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parsed = parse_url(url)
| extend host = parsed.host
```
## Usage
### Syntax
```kusto theme={null}
parse_url(url)
```
### Parameters
| Name | Type | Required | Description |
| ---- | ------ | -------- | -------------------------------- |
| url | string | Yes | An absolute URL string to parse. |
### Returns
Returns a dynamic object containing URL components: `scheme`, `host`, `port`, `path`, `username`, `password`, `query`, `fragment`.
## Use case examples
Parse URLs from HTTP logs to analyze traffic patterns by host and path.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend full_url = strcat('https://api.example.com', uri)
| extend parsed = parse_url(full_url)
| extend host = tostring(parsed.host)
| extend path = tostring(parsed.path)
| summarize request_count = count() by host, path
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20full_url%20%3D%20strcat\(%27https%3A%2F%2Fapi.example.com%27%2C%20uri\)%20%7C%20extend%20parsed%20%3D%20parse_url\(full_url\)%20%7C%20extend%20host%20%3D%20tostring\(parsed.host\)%20%7C%20extend%20path%20%3D%20tostring\(parsed.path\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20host%2C%20path%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| host | path | request\_count |
| --------------- | ------------- | -------------- |
| api.example.com | /api/users | 2341 |
| api.example.com | /api/orders | 1987 |
| api.example.com | /api/products | 1654 |
This query parses complete URLs to extract host and path information for traffic analysis and API endpoint usage patterns.
Extract URL components from span attributes to analyze service communication patterns.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend url = strcat('http://', ['service.name'], ':8080/api/endpoint')
| extend parsed = parse_url(url)
| extend host = tostring(parsed.host)
| extend port = toint(parsed.port)
| summarize span_count = count() by host, port
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20url%20%3D%20strcat\(%27http%3A%2F%2F%27%2C%20%5B%27service.name%27%5D%2C%20%27%3A8080%2Fapi%2Fendpoint%27\)%20%7C%20extend%20parsed%20%3D%20parse_url\(url\)%20%7C%20extend%20host%20%3D%20tostring\(parsed.host\)%20%7C%20extend%20port%20%3D%20toint\(parsed.port\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20host%2C%20port%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| host | port | span\_count |
| -------- | ---- | ----------- |
| frontend | 8080 | 4532 |
| checkout | 8080 | 3421 |
| cart | 8080 | 2987 |
This query parses service URLs from traces to understand port usage and service endpoints in a distributed system.
Parse URLs from security logs to identify suspicious patterns in schemes, hosts, or paths.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend full_url = strcat('http://example.com', uri)
| extend parsed = parse_url(full_url)
| extend scheme = tostring(parsed.scheme)
| extend path = tostring(parsed.path)
| extend has_traversal = indexof(path, '..') >= 0
| project _time, full_url, scheme, path, has_traversal, id, status
| where has_traversal
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20full_url%20%3D%20strcat\(%27http%3A%2F%2Fexample.com%27%2C%20uri\)%20%7C%20extend%20parsed%20%3D%20parse_url\(full_url\)%20%7C%20extend%20scheme%20%3D%20tostring\(parsed.scheme\)%20%7C%20extend%20path%20%3D%20tostring\(parsed.path\)%20%7C%20extend%20has_traversal%20%3D%20indexof\(path%2C%20%27..%27\)%20%3E%3D%200%20%7C%20project%20_time%2C%20full_url%2C%20scheme%2C%20path%2C%20has_traversal%2C%20id%2C%20status%20%7C%20where%20has_traversal%20%7C%20limit%2010%22%7D)
**Output**
| \_time | full\_url | scheme | path | has\_traversal | id | status |
| -------------------- | ------------------------------------- | ------ | ------------------- | -------------- | ------- | ------ |
| 2024-11-06T10:00:00Z | `http://example.com/../../etc/passwd` | http | `/../../etc/passwd` | true | user123 | 403 |
This query parses URLs from failed access attempts and checks for path traversal patterns, helping identify potential security threats.
## List of related functions
* [parse\_urlquery](/apl/scalar-functions/string-functions/parse-urlquery): Parses only URL query parameters. Use this when you only need query string parsing.
* [format\_url](/apl/scalar-functions/string-functions/format-url): Constructs URLs from components. Use this to reverse the parsing operation and build URLs.
* [url\_decode](/apl/scalar-functions/string-functions/url-decode): Decodes URL-encoded strings. Use this to decode individual URL components.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by delimiters. Use this for simpler URL tokenization without full parsing.
# parse_urlquery
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/parse-urlquery
This page explains how to use the parse_urlquery function in APL.
The `parse_urlquery` function parses a URL query string and returns a dynamic object containing the query parameters as key-value pairs. Use this function to extract and analyze query parameters from URLs in logs, API requests, or web traffic data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` or URL parsing to extract query parameters. APL's `parse_urlquery` provides structured query string parsing.
```sql Splunk example theme={null}
| rex field=url "\\?(?.*)"
| eval params=split(query_string, "&")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend params = parse_urlquery(uri)
```
In ANSI SQL, query string parsing requires complex string manipulation. APL's `parse_urlquery` provides native parsing.
```sql SQL example theme={null}
SELECT SUBSTRING(url, POSITION('?' IN url) + 1) AS query_string FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend params = parse_urlquery(uri)
```
## Usage
### Syntax
```kusto theme={null}
parse_urlquery(query_string)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | -------------------------------------------------------------- |
| query\_string | string | Yes | A URL query string (with or without the leading '?') to parse. |
### Returns
Returns a dynamic object containing the query parameters as key-value pairs.
## Use case examples
Extract and analyze query parameters from API requests to understand search patterns and filter usage.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend parameters = parse_urlquery('page=1&limitation=50&sort=date')
| extend page = toint(parameters.page)
| extend limitation = toint(parameters.limitation)
| extend sort = tostring(parameters.sort)
| summarize request_count = count() by page, limitation, sort
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20parameters%20%3D%20parse_urlquery\('page%3D1%26limitation%3D50%26sort%3Ddate'\)%20%7C%20extend%20page%20%3D%20toint\(parameters.page\)%20%7C%20extend%20limitation%20%3D%20toint\(parameters.limitation\)%20%7C%20extend%20sort%20%3D%20tostring\(parameters.sort\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20page%2C%20limitation%2C%20sort%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| page | limit | sort | request\_count |
| ---- | ----- | ---- | -------------- |
| 1 | 50 | date | 8765 |
This query parses query parameters from API requests to analyze pagination and sorting preferences.
Extract query parameters from HTTP spans to analyze API query patterns.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend query_string = '?user_id=12345&action=checkout¤cy=USD'
| extend params = parse_urlquery(query_string)
| extend user_id = tostring(params.user_id)
| extend action = tostring(params.action)
| extend currency = tostring(params.currency)
| summarize span_count = count() by action, currency
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20query_string%20%3D%20%27%3Fuser_id%3D12345%26action%3Dcheckout%26currency%3DUSD%27%20%7C%20extend%20params%20%3D%20parse_urlquery\(query_string\)%20%7C%20extend%20user_id%20%3D%20tostring\(params.user_id\)%20%7C%20extend%20action%20%3D%20tostring\(params.action\)%20%7C%20extend%20currency%20%3D%20tostring\(params.currency\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20action%2C%20currency%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| action | currency | span\_count |
| -------- | -------- | ----------- |
| checkout | USD | 8765 |
This query extracts query parameters from span data to analyze user actions and currency usage patterns in a distributed system.
Detect potential SQL injection or XSS attacks by analyzing suspicious query parameters.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend query_params = parse_urlquery('search= | /search?q=\[HTML\_REMOVED] | user123 | 403 | Unknown |
| 2024-11-06T10:01:00Z | /api?id=1 union select \* | /api?id=1 \[SQL\_REMOVED] \* | user456 | 403 | Russia |
This query sanitizes malicious HTML and SQL patterns, making them safe to display and analyze without risk of execution.
## List of related functions
* [replace\_regex](/apl/scalar-functions/string-functions/replace-regex): Alias for replace with regex support. Use either name based on preference.
* [replace\_string](/apl/scalar-functions/string-functions/replace-string): Replaces plain string matches without regex. Use this for simpler, faster replacements when regex is not needed.
* [extract](/apl/scalar-functions/string-functions/extract): Extracts regex matches without replacement. Use this when you need to capture text rather than modify it.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by delimiters. Use this when tokenizing rather than replacing.
# replace_regex
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/replace-regex
This page explains how to use the replace_regex function in APL.
The `replace_regex` function replaces all matches of a regular expression pattern with another string. This function is an alias for `replace` and provides the same functionality for regex-based text replacement.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `rex` with mode=sed for regex replacements. APL's `replace_regex` provides the same functionality with simpler syntax.
```sql Splunk example theme={null}
| rex field=message mode=sed "s/error_([0-9]+)/ERROR-\\1/g"
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend formatted = replace_regex('error_([0-9]+)', 'ERROR-$1', uri)
```
In ANSI SQL, you use `REGEXP_REPLACE` for regex replacements. APL's `replace_regex` provides similar functionality with consistent syntax.
```sql SQL example theme={null}
SELECT REGEXP_REPLACE(field, 'pattern', 'replacement', 'g') AS result FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend result = replace_regex('pattern', 'replacement', field)
```
## Usage
### Syntax
```kusto theme={null}
replace_regex(regex, rewrite, text)
```
### Parameters
| Name | Type | Required | Description |
| ------- | ------ | -------- | ----------------------------------------------------------------------------------------- |
| regex | string | Yes | The regular expression pattern to search for. Can include capture groups. |
| rewrite | string | Yes | The replacement string. Use $0 for the entire match, $1 for the first capture group, etc. |
| text | string | Yes | The source string to perform replacements on. |
### Returns
Returns the text with all regex matches replaced by the rewrite pattern. Non-overlapping matches.
## Use case examples
Standardize HTTP status codes by adding descriptive prefixes for better readability.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend formatted_status = replace_regex('^(2[0-9]{2})$', 'SUCCESS-$1', status)
| extend formatted_status = replace_regex('^(4[0-9]{2})$', 'CLIENT_ERROR-$1', formatted_status)
| extend formatted_status = replace_regex('^(5[0-9]{2})$', 'SERVER_ERROR-$1', formatted_status)
| summarize request_count = count() by formatted_status
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20formatted_status%20%3D%20replace_regex\(%27%5E\(2%5B0-9%5D%7B2%7D\)%24%27%2C%20%27SUCCESS-%241%27%2C%20status\)%20%7C%20extend%20formatted_status%20%3D%20replace_regex\(%27%5E\(4%5B0-9%5D%7B2%7D\)%24%27%2C%20%27CLIENT_ERROR-%241%27%2C%20formatted_status\)%20%7C%20extend%20formatted_status%20%3D%20replace_regex\(%27%5E\(5%5B0-9%5D%7B2%7D\)%24%27%2C%20%27SERVER_ERROR-%241%27%2C%20formatted_status\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20formatted_status%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| formatted\_status | request\_count |
| ----------------- | -------------- |
| SUCCESS-200 | 8765 |
| CLIENT\_ERROR-404 | 2341 |
| SERVER\_ERROR-500 | 1234 |
| CLIENT\_ERROR-403 | 987 |
This query adds descriptive prefixes to status codes using regex capture groups, making log analysis more intuitive.
Extract and reformat duration values in span attributes by normalizing units.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend duration_str = strcat(tostring(duration / 1ms), 'ms')
| extend normalized = replace_regex('([0-9]+)ms', '$1 milliseconds', duration_str)
| project _time, ['service.name'], duration, duration_str, normalized
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20duration_str%20%3D%20strcat\(tostring\(duration%20%2F%201ms\)%2C%20%27ms%27\)%20%7C%20extend%20normalized%20%3D%20replace_regex\(%27\(%5B0-9%5D%2B\)ms%27%2C%20%27%241%20milliseconds%27%2C%20duration_str\)%20%7C%20project%20_time%2C%20%5B%27service.name%27%5D%2C%20duration%2C%20duration_str%2C%20normalized%20%7C%20limit%2010%22%7D)
**Output**
| \_time | service.name | duration | duration\_str | normalized |
| -------------------- | ------------ | -------- | ------------- | ---------------- |
| 2024-11-06T10:00:00Z | frontend | 125ms | 125ms | 125 milliseconds |
| 2024-11-06T10:01:00Z | checkout | 234ms | 234ms | 234 milliseconds |
This query normalizes duration format using regex capture groups to ensure consistent unit representation across different services.
Mask sensitive data patterns like credit card numbers or SSNs using regex capture groups.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend masked_uri = replace_regex('([0-9]{4})[0-9]{8}([0-9]{4})', '$1********$2', uri)
| extend masked_uri = replace_regex('([0-9]{3})-[0-9]{2}-([0-9]{4})', '$1-XX-$2', masked_uri)
| project _time, uri, masked_uri, id, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20masked_uri%20%3D%20replace_regex\('\(%5B0-9%5D%7B4%7D\)%5B0-9%5D%7B8%7D\(%5B0-9%5D%7B4%7D\)'%2C%20'%241********%242'%2C%20uri\)%20%7C%20extend%20masked_uri%20%3D%20replace_regex\('\(%5B0-9%5D%7B3%7D\)-%5B0-9%5D%7B2%7D-\(%5B0-9%5D%7B4%7D\)'%2C%20'%241-XX-%242'%2C%20masked_uri\)%20%7C%20project%20_time%2C%20uri%2C%20masked_uri%2C%20id%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | masked\_uri | id | status |
| -------------------- | ------------------------ | -------------------------------- | ------- | ------ |
| 2024-11-06T10:00:00Z | /api?cc=1234567890123456 | /api?cc=1234\*\*\*\*\*\*\*\*3456 | user123 | 403 |
| 2024-11-06T10:01:00Z | /api?ssn=123-45-6789 | /api?ssn=123-XX-6789 | user456 | 401 |
This query masks sensitive personally identifiable information like credit card numbers and SSNs using regex capture groups to preserve format while hiding sensitive digits.
## List of related functions
* [replace](/apl/scalar-functions/string-functions/replace): Alias for replace\_regex. Use either name based on preference.
* [replace\_string](/apl/scalar-functions/string-functions/replace-string): Replaces plain string matches without regex. Use this for faster replacement when regex patterns are not needed.
* [extract](/apl/scalar-functions/string-functions/extract): Extracts the first regex match. Use this when you need to capture text rather than modify it.
* [extract\_all](/apl/scalar-functions/string-functions/extract-all): Extracts all regex matches. Use this when you need multiple captured values without replacement.
# replace_string
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/replace-string
This page explains how to use the replace_string function in APL.
The `replace_string` function replaces all occurrences of a plain string with another string. Use this function when you need exact string matching without regular expression patterns, which makes it faster and simpler than regex-based replacement.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use `replace` for simple string replacements. APL's `replace_string` provides the same functionality.
```sql Splunk example theme={null}
| eval cleaned=replace(field, "old_text", "new_text")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend cleaned = replace_string('old_text', 'new_text', field)
```
In ANSI SQL, you use `REPLACE` for string replacements. APL's `replace_string` provides similar functionality.
```sql SQL example theme={null}
SELECT REPLACE(field, 'old_text', 'new_text') AS cleaned FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend cleaned = replace_string('old_text', 'new_text', field)
```
## Usage
### Syntax
```kusto theme={null}
replace_string(lookup, rewrite, text)
```
### Parameters
| Name | Type | Required | Description |
| ------- | ------ | -------- | --------------------------------------------- |
| lookup | string | Yes | The plain string to search for and replace. |
| rewrite | string | Yes | The replacement string. |
| text | string | Yes | The source string to perform replacements on. |
### Returns
Returns the text with all occurrences of the lookup string replaced by the rewrite string. Matches do not overlap.
## Use case examples
Normalize HTTP methods by replacing abbreviations with full names for consistency.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend normalized_method = replace_string('GET', 'Retrieve', method)
| extend normalized_method = replace_string('POST', 'Create', normalized_method)
| extend normalized_method = replace_string('PUT', 'Update', normalized_method)
| extend normalized_method = replace_string('DELETE', 'Remove', normalized_method)
| summarize request_count = count() by normalized_method, status
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20normalized_method%20%3D%20replace_string\(%27GET%27%2C%20%27Retrieve%27%2C%20method\)%20%7C%20extend%20normalized_method%20%3D%20replace_string\(%27POST%27%2C%20%27Create%27%2C%20normalized_method\)%20%7C%20extend%20normalized_method%20%3D%20replace_string\(%27PUT%27%2C%20%27Update%27%2C%20normalized_method\)%20%7C%20extend%20normalized_method%20%3D%20replace_string\(%27DELETE%27%2C%20%27Remove%27%2C%20normalized_method\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20normalized_method%2C%20status%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| normalized\_method | status | request\_count |
| ------------------ | ------ | -------------- |
| Retrieve | 200 | 5432 |
| Create | 201 | 2341 |
| Retrieve | 404 | 1234 |
| Update | 200 | 987 |
This query replaces HTTP method abbreviations with descriptive action names, making logs more readable for non-technical audiences.
Standardize service names by replacing environment-specific prefixes.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend clean_service = replace_string('prod-', '', ['service.name'])
| extend clean_service = replace_string('staging-', '', clean_service)
| extend clean_service = replace_string('dev-', '', clean_service)
| summarize span_count = count() by clean_service, kind
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20clean_service%20%3D%20replace_string\(%27prod-%27%2C%20%27%27%2C%20%5B%27service.name%27%5D\)%20%7C%20extend%20clean_service%20%3D%20replace_string\(%27staging-%27%2C%20%27%27%2C%20clean_service\)%20%7C%20extend%20clean_service%20%3D%20replace_string\(%27dev-%27%2C%20%27%27%2C%20clean_service\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20clean_service%2C%20kind%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| clean\_service | kind | span\_count |
| -------------- | -------- | ----------- |
| frontend | server | 4532 |
| checkout | client | 3421 |
| cart | internal | 2987 |
This query removes environment prefixes from service names to enable cross-environment analysis and aggregation.
Anonymize IP addresses by replacing specific segments for privacy compliance.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend simulated_ip = '192.168.1.100'
| extend anonymized_ip = replace_string('.100', '.XXX', simulated_ip)
| extend anonymized_ip = replace_string('.1.', '.X.', anonymized_ip)
| project _time, uri, simulated_ip, anonymized_ip, status, ['geo.country']
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20simulated_ip%20%3D%20'192.168.1.100'%20%7C%20extend%20anonymized_ip%20%3D%20replace_string\('.100'%2C%20'.XXX'%2C%20simulated_ip\)%20%7C%20extend%20anonymized_ip%20%3D%20replace_string\('.1.'%2C%20'.X.'%2C%20anonymized_ip\)%20%7C%20project%20_time%2C%20uri%2C%20simulated_ip%2C%20anonymized_ip%2C%20status%2C%20%5B'geo.country'%5D%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | simulated\_ip | anonymized\_ip | status | geo.country |
| -------------------- | ----------- | ------------- | -------------- | ------ | ------------- |
| 2024-11-06T10:00:00Z | /admin | 192.168.1.100 | 192.168.X.XXX | 403 | United States |
| 2024-11-06T10:01:00Z | /api/secret | 192.168.1.100 | 192.168.X.XXX | 401 | Unknown |
This query anonymizes IP addresses by replacing specific octets with placeholders, enabling security analysis while maintaining privacy compliance.
## List of related functions
* [replace](/apl/scalar-functions/string-functions/replace): Replaces strings using regular expressions. Use this when you need pattern matching capabilities.
* [replace\_regex](/apl/scalar-functions/string-functions/replace-regex): Alias for replace with regex support. Use this for pattern-based replacements.
* [strcat](/apl/scalar-functions/string-functions/strcat): Concatenates strings. Use this when building new strings rather than replacing parts of existing ones.
* [substring](/apl/scalar-functions/string-functions/substring): Extracts parts of strings. Use this when you need to extract rather than replace text.
# reverse
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/reverse
This page explains how to use the reverse function in APL.
The `reverse` function reverses the order of characters in a string. Use this function to analyze strings from right to left, detect palindromes, or transform data for specific pattern matching requirements.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, reversing strings typically requires custom functions or scripts. APL's `reverse` provides this functionality natively.
```sql Splunk example theme={null}
| eval reversed=mvreverse(split(field, ""))| eval reversed=mvjoin(reversed, "")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend reversed = reverse(field)
```
In ANSI SQL, string reversal varies by database with different functions. APL's `reverse` provides standardized string reversal.
```sql SQL example theme={null}
SELECT REVERSE(field) AS reversed FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend reversed = reverse(field)
```
## Usage
### Syntax
```kusto theme={null}
reverse(value)
```
### Parameters
| Name | Type | Required | Description |
| ----- | ------ | -------- | ---------------------------- |
| value | string | Yes | The input string to reverse. |
### Returns
Returns the input string with its characters in reverse order.
## Use case examples
Detect palindromic patterns in URIs or identifiers for data validation.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend reversed_uri = reverse(uri)
| extend is_palindrome = uri == reversed_uri
| summarize palindrome_count = countif(is_palindrome), total_count = count() by method
| extend palindrome_percentage = round(100.0 * palindrome_count / total_count, 2)
| sort by palindrome_count desc
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20reversed_uri%20%3D%20reverse\(uri\)%20%7C%20extend%20is_palindrome%20%3D%20uri%20%3D%3D%20reversed_uri%20%7C%20summarize%20palindrome_count%20%3D%20countif\(is_palindrome\)%2C%20total_count%20%3D%20count\(\)%20by%20method%20%7C%20extend%20palindrome_percentage%20%3D%20round\(100.0%20*%20palindrome_count%20%2F%20total_count%2C%202\)%20%7C%20sort%20by%20palindrome_count%20desc%22%7D)
**Output**
| method | palindrome\_count | total\_count | palindrome\_percentage |
| ------ | ----------------- | ------------ | ---------------------- |
| GET | 12 | 8765 | 0.14 |
| POST | 5 | 2341 | 0.21 |
| PUT | 2 | 987 | 0.20 |
This query detects palindromic URIs by comparing them with their reversed versions, which can help identify unusual or test data patterns.
Analyze trace IDs by examining their reversed format for pattern detection.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend reversed_trace = reverse(trace_id)
| extend first_char = substring(trace_id, 0, 1)
| extend last_char = substring(reversed_trace, 0, 1)
| extend matches = first_char == last_char
| summarize match_count = countif(matches), total = count() by ['service.name']
| extend match_percentage = round(100.0 * match_count / total, 2)
| sort by match_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20reversed_trace%20%3D%20reverse\(trace_id\)%20%7C%20extend%20first_char%20%3D%20substring\(trace_id%2C%200%2C%201\)%20%7C%20extend%20last_char%20%3D%20substring\(reversed_trace%2C%200%2C%201\)%20%7C%20extend%20matches%20%3D%20first_char%20%3D%3D%20last_char%20%7C%20summarize%20match_count%20%3D%20countif\(matches\)%2C%20total%20%3D%20count\(\)%20by%20%5B%27service.name%27%5D%20%7C%20extend%20match_percentage%20%3D%20round\(100.0%20*%20match_count%20%2F%20total%2C%202\)%20%7C%20sort%20by%20match_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | match\_count | total | match\_percentage |
| ------------ | ------------ | ----- | ----------------- |
| frontend | 287 | 4532 | 6.33 |
| checkout | 216 | 3421 | 6.31 |
| cart | 189 | 2987 | 6.33 |
This query analyzes trace ID patterns by checking if the first and last characters match, which can help validate ID generation algorithms.
Detect reverse proxy attacks or unusual URI patterns by analyzing reversed strings.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend reversed_uri = reverse(uri)
| extend has_reversed_exploit = indexof(reversed_uri, 'drowssap') >= 0 or indexof(reversed_uri, 'nigol') >= 0
| where has_reversed_exploit or status == '403' or status == '401'
| project _time, uri, reversed_uri, has_reversed_exploit, id, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20reversed_uri%20%3D%20reverse\(uri\)%20%7C%20extend%20has_reversed_exploit%20%3D%20indexof\(reversed_uri%2C%20%27drowssap%27\)%20%3E%3D%200%20or%20indexof\(reversed_uri%2C%20%27nigol%27\)%20%3E%3D%200%20%7C%20where%20has_reversed_exploit%20or%20status%20%3D%3D%20%27403%27%20or%20status%20%3D%3D%20%27401%27%20%7C%20project%20_time%2C%20uri%2C%20reversed_uri%2C%20has_reversed_exploit%2C%20id%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | reversed\_uri | has\_reversed\_exploit | id | status |
| -------------------- | -------------- | -------------- | ---------------------- | ------- | ------ |
| 2024-11-06T10:00:00Z | /admin | nimda/ | false | user123 | 403 |
| 2024-11-06T10:01:00Z | /loginpassword | drowssapnigol/ | true | user456 | 401 |
This query detects potentially obfuscated attack patterns by examining reversed URIs for suspicious keywords like 'password' or 'login' spelled backwards.
## List of related functions
* [substring](/apl/scalar-functions/string-functions/substring): Extracts parts of strings. Use this with reverse to extract from the end of strings.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns string length. Use this with reverse for position calculations from the right.
* [strcat](/apl/scalar-functions/string-functions/strcat): Concatenates strings. Use this to build strings with reversed components.
* [split](/apl/scalar-functions/string-functions/split): Splits strings into arrays. Use this with reverse to process tokens in reverse order.
# split
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/split
This page explains how to use the split function in APL.
The `split` function splits a string into an array of substrings based on a delimiter. Use this function to tokenize log messages, parse delimited data, or break down structured text into individual components for analysis.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use the `split` function similarly. APL's `split` provides the same functionality.
```sql Splunk example theme={null}
| eval parts=split(field, ",")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parts = split(field, ',')
```
In ANSI SQL, string splitting varies by database. APL's `split` provides standardized string splitting.
```sql SQL example theme={null}
SELECT STRING_SPLIT(field, ',') AS parts FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend parts = split(field, ',')
```
## Usage
### Syntax
```kusto theme={null}
split(source, delimiter)
```
### Parameters
| Name | Type | Required | Description |
| --------- | ------ | -------- | --------------------------------- |
| source | string | Yes | The source string to split. |
| delimiter | string | Yes | The delimiter string to split on. |
### Returns
Returns a string array containing the substrings separated by the delimiter.
## Use case examples
Split URI paths into segments for hierarchical analysis of API endpoint structure.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend path_segments = split(uri, '/')
| extend segment_count = array_length(path_segments)
| extend first_segment = tostring(path_segments[1])
| summarize request_count = count() by first_segment, segment_count
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20path_segments%20%3D%20split\(uri%2C%20%27%2F%27\)%20%7C%20extend%20segment_count%20%3D%20array_length\(path_segments\)%20%7C%20extend%20first_segment%20%3D%20tostring\(path_segments%5B1%5D\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20first_segment%2C%20segment_count%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| first\_segment | segment\_count | request\_count |
| -------------- | -------------- | -------------- |
| api | 4 | 5432 |
| users | 3 | 2341 |
| products | 3 | 1987 |
This query splits URIs by forward slashes to analyze API endpoint hierarchy and identify the most accessed top-level paths.
Parse dot-notation service names into components for hierarchical analysis.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend service_parts = split(['service.name'], '-')
| extend service_type = tostring(service_parts[0])
| extend part_count = array_length(service_parts)
| summarize span_count = count() by service_type, part_count
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20service_parts%20%3D%20split\(%5B%27service.name%27%5D%2C%20%27-%27\)%20%7C%20extend%20service_type%20%3D%20tostring\(service_parts%5B0%5D\)%20%7C%20extend%20part_count%20%3D%20array_length\(service_parts\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20service_type%2C%20part_count%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service\_type | part\_count | span\_count |
| ------------- | ----------- | ----------- |
| frontend | 1 | 4532 |
| checkout | 1 | 3421 |
| cart | 1 | 2987 |
This query splits service names by hyphens to extract service type prefixes and analyze service naming patterns.
Parse comma-separated attack indicators from security headers or URIs.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend simulated_threats = 'sql_injection,xss,path_traversal'
| extend threat_list = split(simulated_threats, ',')
| extend threat_count = array_length(threat_list)
| extend has_multiple_threats = threat_count > 1
| project _time, uri, threat_list, threat_count, has_multiple_threats, id, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20simulated_threats%20%3D%20'sql_injection%2Cxss%2Cpath_traversal'%20%7C%20extend%20threat_list%20%3D%20split\(simulated_threats%2C%20'%2C'\)%20%7C%20extend%20threat_count%20%3D%20array_length\(threat_list\)%20%7C%20extend%20has_multiple_threats%20%3D%20threat_count%20%3E%201%20%7C%20project%20_time%2C%20uri%2C%20threat_list%2C%20threat_count%2C%20has_multiple_threats%2C%20id%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | uri | threat\_list | threat\_count | has\_multiple\_threats | id | status |
| -------------------- | ------ | ------------------------------------------- | ------------- | ---------------------- | ------- | ------ |
| 2024-11-06T10:00:00Z | /admin | \["sql\_injection","xss","path\_traversal"] | 3 | true | user123 | 403 |
This query splits comma-separated threat indicators to analyze the types and combinations of security threats.
## List of related functions
* [parse\_csv](/apl/scalar-functions/string-functions/parse-csv): Parses CSV strings with proper quote handling. Use this for CSV data instead of split.
* [extract\_all](/apl/scalar-functions/string-functions/extract-all): Extracts multiple regex matches. Use this when you need pattern-based tokenization.
* [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim): Concatenates strings with delimiters. Use this to reverse the split operation.
* [indexof](/apl/scalar-functions/string-functions/indexof): Finds delimiter positions. Use this when you need to know where splits would occur.
# strcat
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strcat
This page explains how to use the strcat function in APL.
The `strcat` function concatenates between 1 and 64 string arguments into a single string. Use this function to combine multiple fields, build composite identifiers, or construct formatted messages from log data.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you concatenate strings using the `.` operator or the `concat` function. APL's `strcat` provides similar functionality.
```sql Splunk example theme={null}
| eval combined=field1."-".field2."-".field3
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend combined = strcat(field1, '-', field2, '-', field3)
```
In ANSI SQL, you use `CONCAT` to join strings. APL's `strcat` provides the same functionality.
```sql SQL example theme={null}
SELECT CONCAT(field1, '-', field2, '-', field3) AS combined FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend combined = strcat(field1, '-', field2, '-', field3)
```
## Usage
### Syntax
```kusto theme={null}
strcat(arg1, arg2, ..., argN)
```
### Parameters
| Name | Type | Required | Description |
| --------------------- | ---- | -------- | ---------------------------------------------------------------------------------------- |
| arg1, arg2, ..., argN | any | Yes | Between 1 and 64 expressions to concatenate. Non-string values are converted to strings. |
### Returns
Returns all arguments concatenated into a single string.
## Use case examples
Build composite keys from multiple fields for unique request identification.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend request_key = strcat(method, '-', status, '-', ['geo.country'])
| summarize request_count = count() by request_key
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20request_key%20%3D%20strcat\(method%2C%20%27-%27%2C%20status%2C%20%27-%27%2C%20%5B%27geo.country%27%5D\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20request_key%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| request\_key | request\_count |
| ---------------------- | -------------- |
| GET-200-United States | 3456 |
| POST-201-United States | 2341 |
| GET-404-Unknown | 1987 |
This query concatenates HTTP method, status, and country to create composite keys for analyzing request patterns by multiple dimensions.
Build formatted trace identifiers combining service and span information.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_identifier = strcat(['service.name'], ':', kind, ':', trace_id)
| project _time, trace_identifier, duration
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20trace_identifier%20%3D%20strcat\(%5B%27service.name%27%5D%2C%20%27%3A%27%2C%20kind%2C%20%27%3A%27%2C%20trace_id\)%20%7C%20project%20_time%2C%20trace_identifier%2C%20duration%20%7C%20limit%2010%22%7D)
**Output**
| \_time | trace\_identifier | duration |
| -------------------- | ---------------------- | -------- |
| 2024-11-06T10:00:00Z | frontend:server:abc123 | 125ms |
| 2024-11-06T10:01:00Z | checkout:client:def456 | 234ms |
This query creates formatted trace identifiers by concatenating service name, span kind, and trace ID for comprehensive trace referencing.
Build security event descriptions combining multiple threat indicators.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend event_description = strcat('Failed ', method, ' request to ', uri, ' from ', id, ' (', ['geo.country'], ')')
| project _time, event_description, status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20event_description%20%3D%20strcat\('Failed%20'%2C%20method%2C%20'%20request%20to%20'%2C%20uri%2C%20'%20from%20'%2C%20id%2C%20'%20\('%2C%20%5B'geo.country'%5D%2C%20'\)'\)%20%7C%20project%20_time%2C%20event_description%2C%20status%20%7C%20limit%2010%22%7D)
**Output**
| \_time | event\_description | status |
| -------------------- | --------------------------------------------------------- | ------ |
| 2024-11-06T10:00:00Z | Failed GET request to /admin from user123 (United States) | 403 |
| 2024-11-06T10:01:00Z | Failed POST request to /api from user456 (Unknown) | 401 |
This query builds human-readable security event descriptions by concatenating multiple fields into informative alert messages.
## List of related functions
* [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim): Concatenates strings with a delimiter. Use this when you want consistent separators between all arguments.
* [split](/apl/scalar-functions/string-functions/split): Splits strings into arrays. Use this to reverse concatenation operations.
* [replace\_string](/apl/scalar-functions/string-functions/replace-string): Replaces parts of strings. Use this when you need to modify concatenated strings.
* [format\_url](/apl/scalar-functions/string-functions/format-url): Formats URL components. Use this specifically for URL construction rather than general concatenation.
# strcat_delim
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strcat-delim
This page explains how to use the strcat_delim function in APL.
The `strcat_delim` function concatenates between 2 and 64 arguments with a specified delimiter between each argument. Use this function to build delimited strings like CSV rows, create formatted lists, or join fields with consistent separators.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically concatenate with repeated delimiters. APL's `strcat_delim` provides a more concise approach.
```sql Splunk example theme={null}
| eval combined=field1.",".field2.",".field3
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend combined = strcat_delim(',', field1, field2, field3)
```
In ANSI SQL, you use `CONCAT_WS` (concat with separator) for delimited concatenation. APL's `strcat_delim` provides similar functionality.
```sql SQL example theme={null}
SELECT CONCAT_WS(',', field1, field2, field3) AS combined FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend combined = strcat_delim(',', field1, field2, field3)
```
## Usage
### Syntax
```kusto theme={null}
strcat_delim(delimiter, arg1, arg2, ..., argN)
```
### Parameters
| Name | Type | Required | Description |
| --------------------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| delimiter | string | Yes | The separator string to insert between arguments. |
| arg1, arg2, ..., argN | any | Yes | Between 2 and 64 expressions to concatenate. Non-string values are converted to strings. |
### Returns
Returns all arguments concatenated with the delimiter between each argument.
## Use case examples
Create CSV-formatted log records for export or integration with external systems.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend csv_record = strcat_delim(',', method, status, uri, req_duration_ms, ['geo.country'])
| project _time, csv_record
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20csv_record%20%3D%20strcat_delim\(%27%2C%27%2C%20method%2C%20status%2C%20uri%2C%20req_duration_ms%2C%20%5B%27geo.country%27%5D\)%20%7C%20project%20_time%2C%20csv_record%20%7C%20limit%2010%22%7D)
**Output**
| \_time | csv\_record |
| -------------------- | --------------------------------------- |
| 2024-11-06T10:00:00Z | GET,200,/api/users,145,United States |
| 2024-11-06T10:01:00Z | POST,201,/api/orders,234,United Kingdom |
This query formats log fields as CSV records with comma delimiters, making them ready for export to spreadsheet applications or data warehouses.
Build pipe-delimited trace summaries for log aggregation systems.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_summary = strcat_delim(' | ', ['service.name'], kind, tostring(duration), trace_id)
| project _time, trace_summary
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20trace_summary%20%3D%20strcat_delim\(%27%20%7C%20%27%2C%20%5B%27service.name%27%5D%2C%20kind%2C%20tostring\(duration\)%2C%20trace_id\)%20%7C%20project%20_time%2C%20trace_summary%20%7C%20limit%2010%22%7D)
**Output**
| \_time | trace\_summary |
| -------------------- | ------------------------------------- |
| 2024-11-06T10:00:00Z | frontend \| server \| 125ms \| abc123 |
| 2024-11-06T10:01:00Z | checkout \| client \| 234ms \| def456 |
This query creates pipe-delimited trace summaries that are easy to read and parse, combining service, kind, duration, and trace ID.
Format security alerts with structured field separators for SIEM integration.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend alert = strcat_delim(' :: ', 'SECURITY_EVENT', status, method, uri, id, ['geo.country'])
| project _time, alert
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20alert%20%3D%20strcat_delim\('%20%3A%3A%20'%2C%20'SECURITY_EVENT'%2C%20status%2C%20method%2C%20uri%2C%20id%2C%20%5B'geo.country'%5D\)%20%7C%20project%20_time%2C%20alert%20%7C%20limit%2010%22%7D)
**Output**
| \_time | alert |
| -------------------- | ------------------------------------------------------------------- |
| 2024-11-06T10:00:00Z | SECURITY\_EVENT :: 403 :: GET :: /admin :: user123 :: United States |
| 2024-11-06T10:01:00Z | SECURITY\_EVENT :: 401 :: POST :: /api :: user456 :: Unknown |
This query creates structured security alerts with double-colon delimiters, making them easy to parse in SIEM systems while remaining human-readable.
## List of related functions
* [strcat](/apl/scalar-functions/string-functions/strcat): Concatenates strings without delimiters. Use this when you want direct concatenation or need custom separators for each position.
* [split](/apl/scalar-functions/string-functions/split): Splits strings by delimiters. Use this to reverse strcat\_delim operations and extract individual fields.
* [parse\_csv](/apl/scalar-functions/string-functions/parse-csv): Parses CSV strings. Use this to parse the output of strcat\_delim with comma delimiters.
* [format\_url](/apl/scalar-functions/string-functions/format-url): Formats URLs from components. Use this specifically for URL construction rather than general delimited concatenation.
# strcmp
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strcmp
This page explains how to use the strcmp function in APL.
The `strcmp` function compares two strings lexicographically and returns an integer indicating their relationship. Use this function to sort strings, validate string ordering, or implement custom comparison logic in your queries.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use comparison operators. APL's `strcmp` provides explicit lexicographic comparison with numeric return values.
```sql Splunk example theme={null}
| eval result=case(str1str2, 1, true(), 0)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend result = strcmp(str1, str2)
```
In ANSI SQL, string comparison varies. APL's `strcmp` provides C-style string comparison returning -1, 0, or 1.
```sql SQL example theme={null}
SELECT CASE
WHEN str1 < str2 THEN -1
WHEN str1 > str2 THEN 1
ELSE 0
END AS result FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend result = strcmp(str1, str2)
```
## Usage
### Syntax
```kusto theme={null}
strcmp(string1, string2)
```
### Parameters
| Name | Type | Required | Description |
| ------- | ------ | -------- | ----------------------------- |
| string1 | string | Yes | The first string to compare. |
| string2 | string | Yes | The second string to compare. |
### Returns
Returns an integer: -1 if string1 is less than string2, 0 if they are equal, 1 if string1 is greater than string2.
## Use case examples
Compare HTTP methods to establish custom ordering for request type analysis.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend method_order = strcmp(method, 'GET')
| summarize get_requests = countif(method_order == 0),
before_get = countif(method_order < 0),
after_get = countif(method_order > 0) by status
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20method_order%20%3D%20strcmp\(method%2C%20%27GET%27\)%20%7C%20summarize%20get_requests%20%3D%20countif\(method_order%20%3D%3D%200\)%2C%20before_get%20%3D%20countif\(method_order%20%3C%200\)%2C%20after_get%20%3D%20countif\(method_order%20%3E%200\)%20by%20status%20%7C%20limit%2010%22%7D)
**Output**
| status | get\_requests | before\_get | after\_get |
| ------ | ------------- | ----------- | ---------- |
| 200 | 5432 | 1234 | 2109 |
| 404 | 1987 | 234 | 120 |
This query uses strcmp to categorize HTTP methods relative to 'GET', enabling analysis of request type distribution by status code.
Compare service names to establish ordering for service dependency analysis.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend name_comparison = strcmp(['service.name'], 'frontend')
| extend is_frontend = name_comparison == 0
| extend before_frontend = name_comparison < 0
| extend after_frontend = name_comparison > 0
| summarize span_count = count() by is_frontend, before_frontend, after_frontend
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20name_comparison%20%3D%20strcmp\(%5B%27service.name%27%5D%2C%20%27frontend%27\)%20%7C%20extend%20is_frontend%20%3D%20name_comparison%20%3D%3D%200%20%7C%20extend%20before_frontend%20%3D%20name_comparison%20%3C%200%20%7C%20extend%20after_frontend%20%3D%20name_comparison%20%3E%200%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20is_frontend%2C%20before_frontend%2C%20after_frontend%22%7D)
**Output**
| is\_frontend | before\_frontend | after\_frontend | span\_count |
| ------------ | ---------------- | --------------- | ----------- |
| true | false | false | 4532 |
| false | true | false | 3421 |
| false | false | true | 6012 |
This query categorizes services based on their lexicographic position relative to 'frontend', helping organize service hierarchies.
## List of related functions
* [tolower](/apl/scalar-functions/string-functions/tolower): Converts strings to lowercase. Use this before strcmp for case-insensitive comparison.
* [toupper](/apl/scalar-functions/string-functions/toupper): Converts strings to uppercase. Use this before strcmp for case-insensitive comparison.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns string length. Use this to compare strings by length rather than lexicographically.
* [indexof](/apl/scalar-functions/string-functions/indexof): Finds substring positions. Use this for substring comparison rather than full string comparison.
# string_size
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/string-size
This page explains how to use the string_size function in APL.
The `string_size` function returns the number of bytes in a string. You use it when you want to measure the length of text fields such as user IDs, URLs, or status codes. This function is useful for detecting anomalies, filtering out unusually long values, or analyzing patterns in textual data.
For example, you can use `string_size` to detect requests with excessively long URIs, identify outlier user IDs, or monitor payload lengths in traces.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use the `len` function to calculate the number of characters in a string. In APL, you use `string_size` to calculate the number of bytes in a string.
```sql Splunk example theme={null}
... | eval uri_length=len(uri)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend uri_length = string_size(uri)
```
In ANSI SQL, you use the `LENGTH` or `CHAR_LENGTH` function to calculate string length. In APL, the equivalent is `string_size` to calculate the number of bytes in a string.
```sql SQL example theme={null}
SELECT LENGTH(uri) AS uri_length
FROM sample_http_logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend uri_length = string_size(uri)
```
## Usage
### Syntax
```kusto theme={null}
string_size(source)
```
### Parameters
| Parameter | Type | Description |
| --------- | -------- | ---------------------------- |
| `source` | `string` | The input string expression. |
### Returns
An integer representing the number of bytes in the string. If the string is empty, the function returns `0`.
## Use case examples
You can use `string_size` to detect unusually long URIs that might indicate an attempted exploit or malformed request.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend uri_length = string_size(uri)
| where uri_length > 100
| project _time, method, uri, uri_length, status
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20uri_length%20%3D%20string_size%28uri%29%20%7C%20where%20uri_length%20%3E%2010%20%7C%20project%20_time%2C%20method%2C%20uri%2C%20uri_length%2C%20status%22%7D)
**Output**
| \_time | method | uri | uri\_length | status |
| -------------------- | ------ | --------------------------------- | ----------- | ------ |
| 2025-09-11T10:01:45Z | GET | /search/products?q=... | 142 | 200 |
| 2025-09-11T10:02:13Z | POST | /checkout/submit/order/details... | 187 | 400 |
This query finds all HTTP requests with URIs longer than 10 characters and lists their details.
You can measure the length of trace IDs or span IDs to ensure data consistency and identify malformed identifiers.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_length = string_size(trace_id)
| summarize avg_length = avg(trace_length) by ['service.name']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20extend%20trace_length%20%3D%20string_size%28trace_id%29%20%7C%20summarize%20avg_length%20%3D%20avg%28trace_length%29%20by%20%5B'service.name'%5D%22%7D)
**Output**
| service.name | avg\_length |
| --------------- | ----------- |
| frontend | 32 |
| checkoutservice | 32 |
| loadgenerator | 31.8 |
This query calculates the average trace ID length per service to verify identifier consistency across the system.
You can check for anomalous user IDs by looking at the length of the `id` field. Very short or very long IDs may signal invalid or suspicious activity.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend id_length = string_size(id)
| where id_length < 5 or id_length > 20
| project _time, id, id_length, status, ['geo.country']
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20id_length%20%3D%20string_size\(id\)%20%7C%20where%20id_length%20%3C%205%20or%20id_length%20%3E%2020%20%7C%20project%20_time%2C%20id%2C%20id_length%2C%20status%2C%20%5B'geo.country'%5D%22%7D)
**Output**
| \_time | id | id\_length | status | geo.country |
| -------------------- | ----------------------------- | ---------- | ------ | ----------- |
| 2025-09-11T09:55:01Z | a12 | 3 | 401 | US |
| 2025-09-11T09:58:42Z | user\_long\_id\_example\_test | 24 | 200 | DE |
This query detects requests with suspiciously short or long user IDs, which might indicate invalid credentials or malicious activity.
# strip_ansi_escapes
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strip-ansi-escapes
This page explains how to use the strip_ansi_escapes function in APL.
Use the `strip_ansi_escapes` function in APL to remove ANSI escape sequences from strings. ANSI escape codes are special character sequences used in terminal output to control text formatting, colors, and cursor positioning. When analyzing logs or terminal output, these codes can interfere with text processing, search operations, and data analysis.
You use `strip_ansi_escapes` when you need to clean up terminal output, colorized logs, or any text that contains ANSI formatting codes. This is particularly useful when ingesting logs from command-line tools, CI/CD pipelines, container logs, or any system that outputs colored or formatted terminal text.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you typically use regex-based replacements to remove ANSI escape sequences. APL's `strip_ansi_escapes` provides a dedicated function that handles all standard ANSI escape sequence patterns automatically.
```sql Splunk example theme={null}
| rex mode=sed field=message 's/\x1b\[[0-9;]*m//g'
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend clean_message = strip_ansi_escapes(uri)
```
ANSI SQL doesn't have a built-in function for removing ANSI escape sequences. You need to use regex replacement functions specific to your SQL dialect. APL's `strip_ansi_escapes` provides a cross-platform solution that handles various ANSI escape patterns.
```sql SQL example theme={null}
SELECT REGEXP_REPLACE(message, '\x1b\[[0-9;]*m', '', 'g')
FROM logs
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend clean_message = strip_ansi_escapes(message)
```
## Usage
### Syntax
```kusto theme={null}
strip_ansi_escapes(text)
```
### Parameters
| Name | Type | Description |
| ------ | -------- | -------------------------------------------------------------------- |
| `text` | `string` | The string containing ANSI escape sequences that you want to remove. |
### Returns
A string with all ANSI escape sequences removed, leaving only the plain text content.
## List of related functions
* [trim](/apl/scalar-functions/string-functions/trim): Use `trim` to remove leading and trailing characters. Use `strip_ansi_escapes` specifically for removing ANSI escape sequences anywhere in the string.
* [replace\_regex](/apl/scalar-functions/string-functions#replace-regex): Use `replace_regex` for custom pattern-based replacements. Use `strip_ansi_escapes` as a specialized, optimized solution for ANSI codes.
* [trim\_space](/apl/scalar-functions/string-functions/trim-space): Use `trim_space` to remove whitespace. Use `strip_ansi_escapes` to clean formatting codes.
* [extract](/apl/scalar-functions/string-functions#extract): Use `extract` to pull specific patterns from text. Use `strip_ansi_escapes` to clean the text before extraction for better pattern matching.
# strlen
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strlen
This page explains how to use the strlen function in APL.
The `strlen` function returns the length of a string in characters. Use this function to validate field lengths, filter by size constraints, or analyze text content patterns in your logs.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use the `len` function. APL's `strlen` provides the same functionality.
```sql Splunk example theme={null}
| eval length=len(field)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend length = strlen(field)
```
In ANSI SQL, you use `LENGTH` or `LEN` depending on the database. APL's `strlen` provides standardized string length measurement.
```sql SQL example theme={null}
SELECT LENGTH(field) AS length FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend length = strlen(field)
```
## Usage
### Syntax
```kusto theme={null}
strlen(source)
```
### Parameters
| Name | Type | Required | Description |
| ------ | ------ | -------- | ---------------------- |
| source | string | Yes | The string to measure. |
### Returns
Returns the length of the string in characters (not bytes).
## Use case examples
Analyze URI lengths to identify potential long-URL attacks or data exfiltration attempts.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend uri_length = strlen(uri)
| summarize avg_length = avg(uri_length),
max_length = max(uri_length),
long_uri_count = countif(uri_length > 200) by method, status
| sort by max_length desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20uri_length%20%3D%20strlen\(uri\)%20%7C%20summarize%20avg_length%20%3D%20avg\(uri_length\)%2C%20max_length%20%3D%20max\(uri_length\)%2C%20long_uri_count%20%3D%20countif\(uri_length%20%3E%20200\)%20by%20method%2C%20status%20%7C%20sort%20by%20max_length%20desc%20%7C%20limit%2010%22%7D)
**Output**
| method | status | avg\_length | max\_length | long\_uri\_count |
| ------ | ------ | ----------- | ----------- | ---------------- |
| GET | 200 | 45.3 | 512 | 234 |
| POST | 404 | 38.7 | 387 | 89 |
This query analyzes URI length patterns to identify unusually long requests that might indicate attacks or data exfiltration attempts.
Monitor trace ID and span ID length consistency to validate instrumentation correctness.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_id_length = strlen(trace_id)
| extend span_id_length = strlen(span_id)
| summarize id_length_consistent = countif(trace_id_length == span_id_length),
trace_avg = avg(trace_id_length),
span_avg = avg(span_id_length) by ['service.name']
| sort by id_length_consistent desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20trace_id_length%20%3D%20strlen\(trace_id\)%20%7C%20extend%20span_id_length%20%3D%20strlen\(span_id\)%20%7C%20summarize%20id_length_consistent%20%3D%20countif\(trace_id_length%20%3D%3D%20span_id_length\)%2C%20trace_avg%20%3D%20avg\(trace_id_length\)%2C%20span_avg%20%3D%20avg\(span_id_length\)%20by%20%5B%27service.name%27%5D%20%7C%20sort%20by%20id_length_consistent%20desc%20%7C%20limit%2010%22%7D)
**Output**
| service.name | id\_length\_consistent | trace\_avg | span\_avg |
| ------------ | ---------------------- | ---------- | --------- |
| frontend | 4532 | 32.0 | 16.0 |
| checkout | 3421 | 32.0 | 16.0 |
This query validates that trace and span IDs have expected lengths, helping identify instrumentation issues where IDs might be malformed.
Detect potential buffer overflow attacks by identifying unusually long user identifiers or input fields.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend id_length = strlen(id)
| extend uri_length = strlen(uri)
| where id_length > 50 or uri_length > 500
| project _time, id, id_length, uri, uri_length, status, ['geo.country']
| sort by id_length desc, uri_length desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20id_length%20%3D%20strlen\(id\)%20%7C%20extend%20uri_length%20%3D%20strlen\(uri\)%20%7C%20where%20id_length%20%3E%2050%20or%20uri_length%20%3E%20500%20%7C%20project%20_time%2C%20id%2C%20id_length%2C%20uri%2C%20uri_length%2C%20status%2C%20%5B'geo.country'%5D%20%7C%20sort%20by%20id_length%20desc%2C%20uri_length%20desc%20%7C%20limit%2010%22%7D)
**Output**
| \_time | id | id\_length | uri | uri\_length | status | geo.country |
| -------------------- | ----------- | ---------- | -------- | ----------- | ------ | ----------- |
| 2024-11-06T10:00:00Z | verylong... | 87 | /api/... | 612 | 403 | Unknown |
This query identifies requests with abnormally long identifiers or URIs, which could indicate buffer overflow attempts or other injection attacks.
## List of related functions
* [substring](/apl/scalar-functions/string-functions/substring): Extracts parts of strings. Use this with strlen to extract specific length substrings.
* [isempty](/apl/scalar-functions/string-functions/isempty): Checks if a string is empty. Use this to test for zero-length strings more explicitly.
* [countof](/apl/scalar-functions/string-functions/countof): Counts substring occurrences. Use this when you need occurrence counts rather than total length.
* [format\_bytes](/apl/scalar-functions/string-functions/format-bytes): Formats bytes as strings. Use this to format length values for display.
# strrep
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/strrep
This page explains how to use the strrep function in APL.
The `strrep` function repeats a string a specified number of times with an optional delimiter. Use this function to generate test data, create patterns for matching, or build formatted strings with repeated elements.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, repeating strings typically requires custom functions or loops. APL's `strrep` provides this functionality natively.
```sql Splunk example theme={null}
| eval repeated=mvjoin(mvappend("text","text","text"), "")
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend repeated = strrep('text', 3)
```
In ANSI SQL, you use `REPEAT` or `REPLICATE` depending on the database. APL's `strrep` provides standardized string repetition.
```sql SQL example theme={null}
SELECT REPEAT('text', 3) AS repeated FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend repeated = strrep('text', 3)
```
## Usage
### Syntax
```kusto theme={null}
strrep(value, multiplier, delimiter)
```
### Parameters
| Name | Type | Required | Description |
| ---------- | ------ | -------- | ----------------------------------------------------------------------- |
| value | string | Yes | The string to repeat. |
| multiplier | int | Yes | Number of repetitions (1 to 1024). Values over 1024 are capped at 1024. |
| delimiter | string | No | Optional delimiter between repetitions (default: empty string). |
### Returns
Returns the string repeated the specified number of times with optional delimiters between repetitions.
## Use case examples
Create visual separators or formatting patterns for log output visualization.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend separator = strrep('=', 50)
| extend formatted_log = strcat(separator, '\n', method, ' ', uri, ' ', status, '\n', separator)
| project _time, formatted_log
| limit 5
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20separator%20%3D%20strrep\(%27%3D%27%2C%2050\)%20%7C%20extend%20formatted_log%20%3D%20strcat\(separator%2C%20%27%5Cn%27%2C%20method%2C%20%27%20%27%2C%20uri%2C%20%27%20%27%2C%20status%2C%20%27%5Cn%27%2C%20separator\)%20%7C%20project%20_time%2C%20formatted_log%20%7C%20limit%205%22%7D)
**Output**
| \_time | formatted\_log |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| 2024-11-06T10:00:00Z | ================================================== GET /api/users 200 ================================================== |
This query creates visual separators using repeated equal signs, making log output more readable and organized.
Generate indentation patterns based on trace depth for hierarchical visualization.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend simulated_depth = 3
| extend indentation = strrep(' ', simulated_depth)
| extend formatted_span = strcat(indentation, ['service.name'], ': ', kind)
| project _time, formatted_span, trace_id
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20simulated_depth%20%3D%203%20%7C%20extend%20indentation%20%3D%20strrep\(%27%20%20%27%2C%20simulated_depth\)%20%7C%20extend%20formatted_span%20%3D%20strcat\(indentation%2C%20%5B%27service.name%27%5D%2C%20%27%3A%20%27%2C%20kind\)%20%7C%20project%20_time%2C%20formatted_span%2C%20trace_id%20%7C%20limit%2010%22%7D)
**Output**
| \_time | formatted\_span | trace\_id |
| -------------------- | ---------------------- | --------- |
| 2024-11-06T10:00:00Z | frontend: server | abc123 |
| 2024-11-06T10:01:00Z | checkout: client | def456 |
This query creates hierarchical indentation for trace visualization by repeating spaces based on simulated span depth.
Generate test patterns for attack signature detection or create anonymization masks.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend id_length = strlen(id)
| extend anonymized_id = strcat(substring(id, 0, 3), strrep('*', id_length - 6), substring(id, id_length - 3, 3))
| project _time, id, anonymized_id, status, uri
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20extend%20id_length%20%3D%20strlen\(id\)%20%7C%20extend%20anonymized_id%20%3D%20strcat\(substring\(id%2C%200%2C%203\)%2C%20strrep\('*'%2C%20id_length%20-%206\)%2C%20substring\(id%2C%20id_length%20-%203%2C%203\)\)%20%7C%20project%20_time%2C%20id%2C%20anonymized_id%2C%20status%2C%20uri%20%7C%20limit%2010%22%7D)
**Output**
| \_time | id | anonymized\_id | status | uri |
| -------------------- | ---------- | -------------- | ------ | ------ |
| 2024-11-06T10:00:00Z | user123456 | use\*\*\*\*456 | 403 | /admin |
| 2024-11-06T10:01:00Z | admin12345 | adm\*\*\*345 | 401 | /api |
This query creates anonymized user IDs by replacing middle characters with repeated asterisks, maintaining partial visibility for analysis while protecting privacy.
## List of related functions
* [strcat](/apl/scalar-functions/string-functions/strcat): Concatenates strings. Use this with strrep to build complex repeated patterns.
* [strcat\_delim](/apl/scalar-functions/string-functions/strcat-delim): Concatenates strings with delimiters. Use strrep's delimiter parameter as an alternative for repeated patterns.
* [strlen](/apl/scalar-functions/string-functions/strlen): Returns string length. Use this to calculate how many repetitions you need.
* [substring](/apl/scalar-functions/string-functions/substring): Extracts parts of strings. Use this with strrep for complex pattern building.
# substring
Source: https://axiom.co/docs/apl/scalar-functions/string-functions/substring
This page explains how to use the substring function in APL.
The `substring` function extracts a substring from a source string starting at a specified position. Use this function to parse fixed-format logs, extract specific segments from structured strings, or truncate text fields.
## For users of other query languages
If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL.
In Splunk SPL, you use the `substr` function. APL's `substring` provides similar functionality with zero-based indexing.
```sql Splunk example theme={null}
| eval extracted=substr(field, 5, 10)
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend extracted = substring(field, 4, 10)
```
Note: Splunk uses 1-based indexing while APL uses 0-based indexing.
In ANSI SQL, you use `SUBSTRING` with similar syntax. APL's `substring` provides the same functionality.
```sql SQL example theme={null}
SELECT SUBSTRING(field, 5, 10) AS extracted FROM logs;
```
```kusto APL equivalent theme={null}
['sample-http-logs']
| extend extracted = substring(field, 4, 10)
```
## Usage
### Syntax
```kusto theme={null}
substring(source, startingIndex, length)
```
### Parameters
| Name | Type | Required | Description |
| ------------- | ------ | -------- | --------------------------------------------------------------------- |
| source | string | Yes | The source string to extract from. |
| startingIndex | int | Yes | The zero-based starting position. |
| length | int | No | The number of characters to extract. If omitted, extracts to the end. |
### Returns
Returns the extracted substring. Returns empty string if startingIndex is beyond the string length.
## Use case examples
Extract specific segments from fixed-format URIs or identifiers.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend api_version = substring(uri, 1, 4)
| where api_version == 'api/'
| extend endpoint = substring(uri, 5, 20)
| summarize request_count = count() by endpoint, method
| sort by request_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27sample-http-logs%27%5D%20%7C%20extend%20api_version%20%3D%20substring\(uri%2C%201%2C%204\)%20%7C%20where%20api_version%20%3D%3D%20%27api%2F%27%20%7C%20extend%20endpoint%20%3D%20substring\(uri%2C%205%2C%2020\)%20%7C%20summarize%20request_count%20%3D%20count\(\)%20by%20endpoint%2C%20method%20%7C%20sort%20by%20request_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| endpoint | method | request\_count |
| -------- | ------ | -------------- |
| users | GET | 2341 |
| orders | POST | 1987 |
| products | GET | 1654 |
This query extracts API endpoints from URIs by taking specific character ranges, enabling analysis of API usage patterns.
Extract prefixes from trace IDs for partitioning or routing analysis.
**Query**
```kusto theme={null}
['otel-demo-traces']
| extend trace_prefix = substring(trace_id, 0, 4)
| extend trace_suffix = substring(trace_id, strlen(trace_id) - 4, 4)
| summarize span_count = count() by trace_prefix
| sort by span_count desc
| limit 10
```
[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B%27otel-demo-traces%27%5D%20%7C%20extend%20trace_prefix%20%3D%20substring\(trace_id%2C%200%2C%204\)%20%7C%20extend%20trace_suffix%20%3D%20substring\(trace_id%2C%20strlen\(trace_id\)%20-%204%2C%204\)%20%7C%20summarize%20span_count%20%3D%20count\(\)%20by%20trace_prefix%20%7C%20sort%20by%20span_count%20desc%20%7C%20limit%2010%22%7D)
**Output**
| trace\_prefix | span\_count |
| ------------- | ----------- |
| abcd | 1234 |
| ef12 | 1123 |
| 89ab | 987 |
This query extracts trace ID prefixes to analyze trace distribution patterns, which can help with load balancing and trace routing strategies.
Extract and analyze specific segments of suspicious URIs or user identifiers.
**Query**
```kusto theme={null}
['sample-http-logs']
| extend uri_start = substring(uri, 0, 10)
| extend uri_has_exploit = indexof(uri_start, '..') >= 0 or indexof(uri_start, '