# Oodle > Oodle is a fully managed, AI-native observability platform — a drop-in replacement for Prometheus and ELK stack at scale. Logs, metrics, traces, and Kubernetes monitoring at 1/5th the cost with zero management. This file contains all documentation content in a single document following the llmstxt.org standard. ## Datasets & Experiments Datasets let you organize test cases for systematic evaluation of your LLM application. Each dataset contains input/expected-output pairs that you can run through an LLM with attached evaluators to measure quality at scale. Navigate to **Agent Observability → Datasets** in the sidebar (). ## Creating a Dataset 1. Click **+ New Dataset** 2. Enter a name (e.g. `sms-dataset-v1`) 3. Optionally upload a CSV file to import items 4. Click **Create** :::note Creating and managing datasets requires the **Editor** or **Admin** role. ::: ### CSV Import You can bulk-import items from a CSV file (up to 50 MB). The CSV must include an `input` column (exact case). Optional columns: | Column | Description | |--------|-------------| | `input` | The user query or prompt (required) | | `expected_output` | The ideal response | | `metadata` | Additional context as JSON | A preview of the first few rows is shown before import. Partial imports are supported — if some rows fail, the successful rows are still added and a summary toast shows the error count. ## Adding Items From a dataset's detail page, click **+ New Item** to add entries manually or via CSV. ### Manual Entry Add one or more rows with **Input** (required), **Expected Output**, and **Metadata** fields. Click **+ Add another row** to add multiple items at once. ### From a Trace On any trace detail page, click **Add to Dataset**. Oodle splits the conversation into input and expected output and adds it to the dataset you select. This is useful for capturing real production examples as test cases. ## Running Experiments Experiments run your dataset items through an LLM and optionally score the outputs with evaluators. 1. Open a dataset and click **Run Experiment** 2. Configure the experiment: | Field | Description | |-------|-------------| | **LLM Connection** | Which provider credentials to use ([configure here](/agent-observability/llm-connections)) | | **Model** | The model to run items against | | **Evaluators** | One or more active evaluator rules to score outputs | | **Prompt** | Optional prompt template to wrap inputs (by label or version) | 3. Click **Run Experiment** The experiment runs asynchronously. Results appear in the **Runs** tab. :::tip If you don't have an LLM connection configured yet, the form links to **Settings → LLM Connections** where you can add one. ::: ## Viewing Results The **Runs** tab shows all experiment runs with their status, LLM connection, evaluator, prompt reference, and creation time. Click any run to open a detail drawer showing: - **Run metadata** — status, connection, model, evaluators, prompt, and any errors - **Per-item results** — a table with Input, Metadata, Expected Output, Actual Output, and score badges from each evaluator While an experiment is running, the drawer polls for new results every few seconds and shows a progress indicator. ### Run Statuses | Status | Meaning | |--------|---------| | Pending | Queued, not yet started | | Running | Processing dataset items | | Completed | All items processed | | Failed | An error stopped the run | --- ## Agent Observability Oodle's Agent Observability helps engineering teams track how large language models are used across their applications - including prompts, responses, token consumption, and estimated costs. ## How It Works Oodle ingests traces and logs that follow the [OpenTelemetry Gen AI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/). When your application instruments LLM calls with these conventions, Oodle automatically: - Detects the **model** from `gen_ai.request.model` and `gen_ai.response.model` span attributes - Extracts **input and output token counts** from `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` - Displays **prompt and response content** from `gen_ai.input.messages`, `gen_ai.output.messages`, and `gen_ai.system_instructions` span attributes - Calculates **estimated costs** based on your configured [model pricing](./model-definitions) All of this is surfaced in the Agent Observability hub at **Agent Observability → Overview** (). ## Exploring Traces The Agent Observability hub has several tabs for different views of your GenAI data. ### Overview The default tab shows an embedded Grafana dashboard with key metrics: token usage, cost trends, trace counts, and model breakdowns over time. ### Traces The main trace list with columns for timestamp, name, input, output, model, sentiment, turns, latency, and cost. Search traces by **Name**, **Input**, **Output**, or **Input/Output** combined using the field dropdown next to the search box. Click any trace to open the detail view (see [Trace Detail](#trace-detail) below). ### Agent Graph An interactive topology graph showing how agents, tools, and LLM calls connect in your application. Each node displays request count, error rate, and average latency. Click a node to see: - **Decision Paths** — outgoing connections split into sub-agents and tool calls with probability, error rate, and latency - **Traces** — filtered trace list for that agent - **Insights** — AI-generated recommendations specific to that agent (if available) The Agent Graph has its own sidebar with environment and service filters, plus a node search and visibility toggle. ### Observations A span-level table showing individual LLM calls rather than full traces. Columns include start time, span type, name, input, output, model, sentiment, log level, latency, and cost. ### Sessions Groups traces by `session.id` into multi-turn conversations. Each row shows the session ID, trace count, turns, sentiment, total cost, duration, and last activity time. Click a session to open a detail drawer with stats and a cross-trace transcript. :::tip To enable sessions, set `session.id` as a span attribute in your instrumentation. Traces sharing the same session ID are grouped together automatically. ::: ### Users Aggregates usage by `user.id` — showing total events, turns, sentiment, token count, and cost per user. Click a user to filter the Traces tab to that user's activity. :::tip Set `user.id` as a span attribute to enable per-user tracking. ::: ### Insights Appears when Oodle has generated AI-powered recommendations for your GenAI application. Insights are categorized by type (Quality, Efficiency, Resource, Performance) and severity (High, Medium, Low). Use this tab to identify optimization opportunities and quality issues. ## Sidebar Filters The sidebar on the left lets you filter data across most tabs: | Filter | Description | |--------|-------------| | **Time Range** | Controls the query window | | **Has Error** | Show only traces with errors | | **Environment** | Filter by `resource::env` | | **Service** | Filter by `resource::service.name` | | **Model** | Filter by `gen_ai.request.model` | | **Agent Name** | Filter by `gen_ai.agent.name` | | **Operation Name** | Filter by `gen_ai.operation.name` | | **User ID** | Filter by `user.id` | | **Sentiment** | Range slider from −1 to 1 | | **Score** | Range slider from 0 to 1 | | **Score Name** | Filter by evaluator score name | You can also add arbitrary label filters using the filter bar above the tab content. ## Trace Detail Clicking on a trace opens the detail view with tabs for **Transcript**, **Trace**, **Logs**, **Scores**, and **Costs**. ### Transcript Displays the full conversation — system instructions, user prompts, and assistant responses — alongside the model name and token count. Use the search bar to find text across all messages. Matches are highlighted and the view scrolls to each result. For traces with multiple agents (identified by `gen_ai.agent.name`), a **By Agent** toggle groups the conversation by agent. ### Trace Shows the span waterfall with the full request lifecycle, including nested agent invocations and LLM calls. By default, non-GenAI infrastructure spans are hidden. Toggle **Show all spans** to reveal them. GenAI-related spans (GENERATION, AGENT, TOOL) are expanded automatically while infrastructure spans start collapsed. Each span has sub-tabs for **Overview**, **Gen AI** (model/token/cost summary), **Transcript**, and **Errors**. ### Scores Shows all evaluator scores attached to the trace. Each row displays the score name, value, source (EVAL, ANNOTATION, or API), data type, model used, and comment. ### Costs Summarizes total cost, tokens, and LLM calls broken down by model. Shows separate breakdowns for model costs and evaluator costs. ### Trace Actions The trace detail toolbar includes several actions: | Action | Description | |--------|-------------| | **Analyze with AI** | Opens the AI sidebar assistant with the trace as context | | **Open in Playground** | Loads the trace conversation into the [Playground](./playground) for iteration | | **Add to Dataset** | Adds the trace as a test case to a [dataset](./datasets) | | **Open Trace** | Opens the trace in the standard trace lookup view | ## Getting Started ### 1. Install OpenTelemetry GenAI instrumentation The official OpenTelemetry GenAI instrumentation libraries auto-instrument LLM provider SDKs - no manual span creation required. Install the instrumentation package for your LLM provider alongside the core OpenTelemetry SDK and OTLP exporter: ```txt opentelemetry-instrumentation-google-genai>=0.7b1 opentelemetry-exporter-otlp-proto-http>=1.30.0 opentelemetry-sdk>=1.30.0 google-genai>=1.0.0 ``` ```txt opentelemetry-instrumentation-openai-v2>=2.0b0 opentelemetry-exporter-otlp-proto-http>=1.30.0 opentelemetry-sdk>=1.30.0 openai>=1.0.0 ``` ```txt pydantic-ai opentelemetry-exporter-otlp-proto-http>=1.30.0 opentelemetry-sdk>=1.30.0 ``` ```txt traceloop-sdk google-genai>=1.0.0 ``` ```txt openlit ``` See the [OpenTelemetry GenAI instrumentation registry](https://opentelemetry.io/ecosystem/registry/?language=python&component=instrumentation&search=gen+ai) for a full list of supported providers (Anthropic, AWS Bedrock, LangChain, and more). ### 2. Instrument your application Initialize the OpenTelemetry SDK with OTLP export, then activate the GenAI instrumentor for your provider: ```python from opentelemetry import _logs as otel_logs from opentelemetry import trace as otel_trace from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs.export import BatchLogRecordProcessor from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor def setup_opentelemetry(): resource = Resource.create({"service.name": "my-llm-app"}) tracer_provider = TracerProvider(resource=resource) tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) otel_trace.set_tracer_provider(tracer_provider) logger_provider = LoggerProvider(resource=resource) logger_provider.add_log_record_processor( BatchLogRecordProcessor(OTLPLogExporter()) ) otel_logs.set_logger_provider(logger_provider) setup_opentelemetry() GoogleGenAiSdkInstrumentor().instrument() ``` After this, all calls to the Google Gemini SDK (e.g. `client.models.generate_content(...)`) are automatically traced with `gen_ai.*` attributes. ```python from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.trace import set_tracer_provider from pydantic_ai import Agent from pydantic_ai.models.instrumented import InstrumentationSettings def setup_opentelemetry(): resource = Resource.create({"service.name": "my-llm-app"}) tracer_provider = TracerProvider(resource=resource) tracer_provider.add_span_processor( BatchSpanProcessor(OTLPSpanExporter()) ) set_tracer_provider(tracer_provider) setup_opentelemetry() Agent.instrument_all(InstrumentationSettings( use_aggregated_usage_attribute_names=False, )) ``` Pydantic AI captures prompt and response content by default. Pass `include_content=False` to `InstrumentationSettings` to disable. [Traceloop's OpenLLMetry](https://github.com/traceloop/openllmetry) SDK auto-instruments supported LLM SDKs with a single init call. No manual span or provider setup is required: ```python from google import genai from traceloop.sdk import Traceloop from traceloop.sdk.decorators import workflow # Auto-instruments Gemini, OpenAI, Anthropic, LangChain, and more. Traceloop.init(app_name="my-llm-app") client = genai.Client() # Optional: group related LLM calls under a named workflow span. @workflow(name="chat") def chat(message: str) -> str: response = client.models.generate_content( model="gemini-flash-latest", contents=message, ) return response.text ``` Traceloop captures prompt and response content by default. Set `TRACELOOP_TRACE_CONTENT=false` to disable. :::note Traceloop uses its own environment variable instead of the `OTEL_*` ones below. Point the SDK at your collector with `TRACELOOP_BASE_URL=http://otel-collector:4318`. Content capture is on by default; disable it with `TRACELOOP_TRACE_CONTENT=false`. ::: [OpenLIT](https://github.com/openlit/openlit) auto-instruments supported LLM providers, vector databases, MCP tools, and GPUs with a single `init()` call. No manual span creation needed: ```python openlit.init( service_name="my-llm-app", otlp_endpoint="http://otel-collector:4318", capture_message_content=True, ) ``` Set `capture_message_content=True` to capture prompt and response content in span attributes. Review your organization's data policies before enabling in production. :::note OpenLIT does not require any `OTEL_*` environment variables. The `otlp_endpoint` and `capture_message_content` parameters are passed directly to `openlit.init()`. ::: ### 3. Set environment variables The application needs these environment variables: | Variable | Description | |----------|-------------| | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint — points to your OTel Collector (e.g. `http://otel-collector:4318`) | | `OTEL_SEMCONV_STABILITY_OPT_IN` | Set to `gen_ai_latest_experimental` to enable GenAI semantic conventions (not needed for Pydantic AI) | | `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | Set to `span_and_event` to capture prompt/response content (not needed for Pydantic AI — it captures content by default) | :::caution `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` captures full prompt and response text. This may include sensitive data — review your organization's data policies before enabling in production. ::: ### 4. Configure the OpenTelemetry Collector Route traces and logs from your application to Oodle through an OpenTelemetry Collector. The collector exports to Oodle's OTLP endpoint which supports standard OTLP paths (`/v1/traces`, `/v1/logs`): ```yaml receivers: otlp: protocols: http: endpoint: "0.0.0.0:4318" processors: batch: timeout: 5s send_batch_size: 512 exporters: otlphttp/oodle: endpoint: "https://-otlp.collector.oodle.ai" headers: "X-OODLE-INSTANCE": "" "X-API-KEY": "" service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlphttp/oodle] logs: receivers: [otlp] processors: [batch] exporters: [otlphttp/oodle] ``` Replace `` and `` with your credentials from **Settings → API Keys** in the Oodle UI (). See the [OpenTelemetry integration guide](/integrations/traces/otel) for full collector and SDK configuration options. ### 5. Verify in Oodle Navigate to **GenAI > Traces** (). Click on a trace to see the Transcript, Trace waterfall, and Costs breakdown. ## Demo Applications The [oodle-onboarding](https://github.com/oodle-ai/oodle-onboarding) repository includes ready-to-run demos: - [**llmops-otel-demo**](https://github.com/oodle-ai/oodle-onboarding/tree/main/llmops-otel-demo) — Google Gemini with OTel instrumentation - [**pydantic-ai-demo**](https://github.com/oodle-ai/oodle-onboarding/tree/main/pydantic-ai-demo) — Pydantic AI multi-agent example with tool use and structured outputs - [**traceloop-demo**](https://github.com/oodle-ai/oodle-onboarding/tree/main/traceloop-demo) — Traceloop (OpenLLMetry) SDK auto-instrumenting Google Gemini - [**openlit-demo**](https://github.com/oodle-ai/oodle-onboarding/tree/main/openlit-demo) — OpenLIT with agents (LangGraph), guardrails, VectorDB (ChromaDB RAG), and MCP tool tracing ```bash git clone https://github.com/oodle-ai/oodle-onboarding.git cd oodle-onboarding/llmops-otel-demo cp .env.example .env # Edit .env with your OODLE_INSTANCE, OODLE_API_KEY, and GEMINI_API_KEY make up make test-chat ``` Each demo includes an OTel Collector and Docker Compose configuration — everything needed to see LLM traces in Oodle in minutes. ## Cost Tracking Once [model definitions](./model-definitions) are configured, Oodle shows cost data on every LLM trace. The cost is calculated as: ``` cost = (input_tokens / 1,000,000) × input_price + (output_tokens / 1,000,000) × output_price ``` For parent spans (agent invocations), Oodle aggregates token counts from child LLM spans and shows the total estimated cost. See the [Model Definitions](./model-definitions) page for pricing configuration. --- ## LLM Connections LLM Connections store your provider API credentials so Oodle can call LLMs on your behalf. They are required for the [Playground](/agent-observability/playground), [LLM-as-Judge evaluators](/evaluators/llm-as-judge), and [dataset experiments](/agent-observability/datasets). Navigate to **Settings → LLM Connections** (). :::note Only **Admin** users can create and manage LLM connections. ::: ## Supported Providers | Provider | Default Base URL | |----------|------------------| | **Google** | `https://generativelanguage.googleapis.com` | | **Anthropic** | `https://api.anthropic.com` | | **OpenAI** | `https://api.openai.com/v1` | ## Creating a Connection 1. Click **+ New Connection** 2. Fill in the required fields: | Field | Required | Description | |-------|----------|-------------| | **Name** | Yes | Display name for the connection | | **Provider** | Yes | Google, Anthropic, or OpenAI | | **API Key** | Yes | Your provider API key | | **Default Model** | No | Model used when none is explicitly selected | | **Base URL** | No | Override the default API endpoint (useful for proxies or custom deployments) | | **Custom Models** | No | Additional model names not in the built-in list (e.g. `deepseek/deepseek-v4-flash`) | | **Custom Headers** | No | Extra HTTP headers sent with every request | 3. Optionally toggle **Set as default connection** to make this the primary connection 4. Optionally toggle **Show default models from provider** off to hide built-in models and only show your custom models 5. Click **Create** ## Default Connection One connection can be marked as the default. The Playground, evaluator wizard, and experiment form auto-select the default connection when no other is specified. ## Custom Models If your provider supports models not in Oodle's built-in list, add them as **Custom Models**. These appear alongside built-in models in every model selector. You can also type any model name directly in model dropdowns — select **Use "model-name"** to use an unlisted model. ## Editing and Deleting Click the edit icon on any connection to update its configuration. When editing, leave the API key blank to keep the existing key. :::warning Deleting a connection stops any evaluators and Playground sessions that use it. ::: --- ## Model Definitions Model Definitions let you configure pricing information for each LLM model your applications use. Navigate to **Agent Observability → Model Definitions** in the sidebar (). ## How pricing works Each model definition includes: | Field | Description | |-------|-------------| | **Model Name** | Display name (e.g. `gemini-flash-latest`) | | **Match Pattern** | Regex matched against the model name in traces | | **Input Price** | Cost per 1M input tokens (USD) | | **Output Price** | Cost per 1M output tokens (USD) | When a trace span includes token usage and a model name, Oodle walks the model definitions list and applies the first matching pattern to estimate the cost. ## Adding a model 1. Go to **Agent Observability → Model Definitions** () 2. Models detected in your traces that don't match any existing pattern appear at the top of the table with a warning - click to add pricing 3. Enter the model name, a regex match pattern, and input/output prices per 1M tokens --- ## Playground The Playground is an interactive testing environment for LLM prompts. Compare outputs across models side by side, define tools for function calling, and iterate on prompts before deploying them. Navigate to **Agent Observability → Playground** in the sidebar (). :::info Beta The Playground is currently in Beta. ::: ## Prerequisites The Playground requires at least one [LLM Connection](/agent-observability/llm-connections) to be configured. If no connections exist, the model selector shows a link to **Settings → LLM Connections**. ## Getting Started 1. Select an **LLM Connection** and **Model** from the dropdowns at the top of the window 2. Add messages using the **+ Message** dropdown (User, Assistant, System, Developer, or Tool) 3. Type your prompt content in the message editor 4. Click **Submit** or press **⌘ Enter** to run The output appears at the bottom of the window with the model's response, including any tool calls or reasoning output. ## Multi-Window Comparison Click **New split window** to add up to four side-by-side windows. Each window has its own connection, model, messages, and output — useful for A/B testing prompts across different models or configurations. - **Duplicate window** — copies the window configuration - **Remove window** — removes a window (available when more than one is open) - **Run All** (⌘ Enter) — submits all windows that have a model selected, in parallel - **Reset** — replaces all windows with a single empty window ## Messages Messages support the following roles: | Role | Purpose | |------|---------| | **System** | System instructions for the model | | **User** | User input / prompt | | **Assistant** | Model response (for few-shot examples) | | **Developer** | Developer instructions | | **Tool** | Tool result linked to a tool call | Click the role badge on any message to cycle through roles. Drag the handle to reorder messages. ### Prompt References Type `@@@` in a message to link to a saved [prompt](/prompt-management). Oodle resolves prompt references before sending the request to the model. ## Template Variables Use `{{variable_name}}` syntax in message content to create template variables. The **Variables** panel (badge shows count) lets you fill in values for each variable before running. All variables must have values before submitting — the Playground warns if any are unfilled. ## Tools (Function Calling) Click **Tools** to define function tools the model can call. Each tool has: - **Name** — e.g. `get_weather` - **Description** — what the tool does - **JSON Schema** — parameter schema (visual builder or raw JSON) When the model responds with tool calls, they appear as expandable blocks in the output. :::caution Tools and structured output cannot be used at the same time. ::: ## Structured Output Click **Schema** to define a JSON schema that constrains the model's output format. Specify a name, description, and schema using the visual builder or raw JSON editor. ## Model Parameters Click the gear icon to configure optional parameters: | Parameter | Range | Default | |-----------|-------|---------| | **Temperature** | 0–2 | 1.0 | | **Max Tokens** | 1–65536 | 2048 | | **Top P** | 0–1 | 1.0 | | **Additional options** | Provider-specific JSON | `{}` | Each parameter has a toggle — enable it to override the model's default. ## Seeding from Traces From any trace detail page, click **Open in Playground** to load the trace's conversation into the Playground. This seeds: - All messages (system, user, assistant, tool) - The model name - Tool definitions (if present in the trace) Use this to replay and iterate on real production conversations. ## Seeding from Prompts From any prompt detail page, click **Test in Playground** to load the prompt content into the Playground. Chat prompts load as multi-message conversations; text prompts load as a single system message. ## Save as Prompt Click **Save as Prompt** in the toolbar to save the current messages as a new prompt. This opens the prompt creation page with the messages pre-filled. ## Streaming The **Stream** checkbox (enabled by default) controls whether responses stream token by token or arrive as a complete response. Streaming shows a **Stop** button to end generation early. --- ## Agents Oodle Agents allow you to securely connect your private infrastructure to Oodle. An agent runs inside your network (typically as a Kubernetes deployment) and establishes an outbound WebSocket connection to Oodle. This tunnel lets Oodle reach resources that are not publicly accessible, such as internal APIs, databases, and services behind a firewall. The agent is open source: [github.com/oodle-ai/oodle-agent](https://github.com/oodle-ai/oodle-agent). ## Use Cases | Use Case | Description | |----------|-------------| | **Private endpoint monitoring** | Run [synthetic checks](/synthetic-monitoring) against internal HTTP endpoints, DNS, TCP, Ping, SSL, and Traceroute targets that are not reachable from the public internet | | **Private database datasources** | Connect Grafana to Postgres databases inside your VPC without exposing them publicly | ## How It Works 1. You create an agent in the Oodle UI and receive a one-time registration token 2. You deploy the agent into your Kubernetes cluster using Helm 3. On first connection the agent uses the registration token to obtain a client certificate via mTLS 4. The agent opens a persistent WebSocket connection to Oodle 5. Oodle routes requests (synthetic checks, database queries) through the tunnel to your private infrastructure All traffic is encrypted in transit using mutual TLS (mTLS). The agent authenticates with a client certificate, and Oodle authenticates via its server certificate pinned to the Amazon CA. ## Creating an Agent :::note Creating an agent requires the **Admin** role. Users with the Editor or Viewer role cannot create or manage agents. ::: 1. Navigate to **Agents** in the sidebar 2. Click **Create Agent** 3. Enter a descriptive name (e.g. `production-cluster`, `staging-vpc`) 4. Click **Create** After creation, you will see two commands that must be run in order. **Save the registration token immediately** — it is single-use and expires in 24 hours. ### Step 1: Create the Kubernetes Secret The first command creates a namespace and a Kubernetes secret containing the agent's one-time registration token: ```bash kubectl get namespace oodle-monitoring 2>/dev/null || \ kubectl create namespace oodle-monitoring kubectl create secret generic oodle-agent- \ --namespace oodle-monitoring \ --from-literal=registration-token= ``` ### Step 2: Install the Agent via Helm The second command deploys the agent using the official [Helm chart](https://github.com/oodle-ai/oodle-agent): ```bash helm repo add oodle-ai https://oodle-ai.github.io/helm-charts helm repo update helm upgrade --install oodle-agent oodle-ai/oodle-agent \ --namespace oodle-monitoring \ --create-namespace \ --wait \ --set oodle.instance= \ --set oodle.agentId= \ --set oodle.agentName= \ --set existingSecret=oodle-agent- \ --set oodle.gatewayUrls=wss:///ws ``` :::tip Both commands are pre-filled with the correct values in the UI. You can copy them directly. ::: :::info On its first connection, the agent uses the registration token to submit a Certificate Signing Request (CSR) and receives a signed client certificate. All subsequent connections use mutual TLS (mTLS) — the registration token is automatically consumed and no longer needed. ::: ### Step 3: Restrict Allowed Endpoints (Optional) By default the agent can reach any endpoint in your network. To restrict which hosts the agent is allowed to connect to, add the `policy` values to the Helm install command: ```bash helm repo add oodle-ai https://oodle-ai.github.io/helm-charts helm repo update helm upgrade --install oodle-agent oodle-ai/oodle-agent \ --namespace oodle-monitoring \ # ... same flags as above ... --set policy.enable_access_restrictions=true \ --set "policy.allowed_endpoints[0]=my-postgres.internal:5432" \ --set "policy.allowed_endpoints[1]=*.prod.internal:443" \ --set "policy.allowed_endpoints[2]=10.0.1.50:8080" ``` Supported endpoint patterns: | Pattern | Description | |---------|-------------| | `host:port` | Exact match (e.g. `my-db.internal:5432`) | | `host` | Any port on that host | | `*.example.com:443` | Wildcard subdomain with specific port | | `*:5432` | Any host on a specific port | The policy is enforced for **all** protocols: HTTP, Postgres, TCP, Ping, DNS, SSL, and Traceroute. You can also restrict which Kubernetes namespaces and resources the agent can read: ```bash --set "policy.kubernetes.allowed_namespaces[0]=production" \ --set "policy.kubernetes.allowed_namespaces[1]=monitoring" \ --set "policy.kubernetes.deny_resources[0]=secrets" ``` To update the policy after installation, run `helm upgrade` with the new values, or edit the `oodle-agent-policy` ConfigMap directly and restart the agent pod. ## Viewing Agent Status The Agents list page displays all registered agents with: - **Name**: The agent name and its unique ID - **Status**: Current connection status (`ACTIVE` or `DISCONNECTED`) - **Created**: When the agent was registered - **Last Heartbeat**: The most recent heartbeat received from the agent Click on any agent row to open the detail drawer. ## Agent Detail Drawer ### Overview Tab Shows creation time, last heartbeat, capabilities, and an embedded health dashboard with real-time metrics for the agent. ### Datasources Tab Manage Postgres datasources that are routed through this agent. Each datasource creates a corresponding Grafana Postgres datasource that tunnels queries through the agent to a private database. #### Adding a Postgres Datasource 1. Open the agent detail drawer and go to the **Datasources** tab 2. Click **Add Datasource** 3. Fill in the connection details: - **Datasource Name**: A human-readable name - **Host**: The private Postgres host and port (e.g. `my-db.internal:5432`) - **Database**: The database name - **User / Password**: Postgres credentials - **SSL Mode**: `disable`, `require`, `verify-ca`, or `verify-full` - **TLS Certificates**: (Optional) CA cert, client cert, and client key for mTLS 4. Click **Create** Once created, a Grafana Postgres datasource is automatically provisioned. You can click **Explore** to query the database directly from Grafana, or use **Save & Test** to verify connectivity. ### Uninstall Tab Provides step-by-step commands to cleanly remove the agent: 1. **Uninstall the Helm release**: ```bash helm uninstall oodle-agent \ --namespace oodle-monitoring ``` 2. **Delete the Kubernetes secret**: ```bash kubectl delete secret oodle-agent- \ --namespace oodle-monitoring ``` 3. **Delete the agent from Oodle** using the **Delete Agent** button :::caution Deleting an agent disconnects all datasources and synthetic monitors routed through it. ::: ## Best Practices ### Naming Conventions Use names that identify the environment and cluster, e.g.: - `prod-us-east-1` - `staging-eks` - `dev-local` ### High Availability The agent maintains multiple connections for redundancy. If one connection becomes unavailable, traffic is automatically routed through another. ### Network Requirements The agent only requires **outbound** access on port 443 (WebSocket over TLS). No inbound firewall rules are needed. --- ## AI Canvas AI Canvas is a full-page, conversational AI assistant built into Oodle. It can query your metrics, logs, and traces, create Grafana dashboards and alerts, navigate the UI on your behalf, and answer questions about your infrastructure — all through natural language. ## Accessing AI Canvas There are two ways to interact with the AI assistant: ### Full-Page Canvas Click **AI Assistant** in the sidebar, then select **AI Canvas** to open the dedicated full-page experience. You can also navigate directly to `/ai-canvas`. The full-page canvas includes a chat history sidebar on the left so you can revisit previous conversations. ### Sidebar Assistant Click **AI Assistant** (or press Cmd + I) from any page to open the sidebar panel. The sidebar overlays the current page, giving you quick access to the AI while keeping your current context visible. ## Capabilities | Capability | Description | |------------|-------------| | **Metrics** | Find metrics, query with PromQL, and visualize results inline | | **Logs** | Search and summarize logs, identify error patterns | | **Traces** | Query traces and analyze latency or error patterns | | **Dashboards** | Create and update Grafana dashboards from a prompt | | **Alerts** | Create, update, and explain alert monitors | | **Anomalies** | Query detected anomalies across your infrastructure | | **Navigation** | Navigate to pages, adjust time ranges, and fill forms | | **Integrations** | Answer setup questions using Oodle documentation | ## Home Page When you open AI Canvas without an active conversation, the home page shows contextual information below the chat input to help you get started: - **Recent Dashboards** — your most-visited Grafana dashboards from the past week, sorted by frequency. If you have fewer than ten personal dashboards, the list is filled with popular dashboards across your organization. Click any dashboard to open it directly. - **Anomalies** — the top anomalies detected across your infrastructure in the last 24 hours, sorted by severity and recency. Click any anomaly to jump to the anomalies page with that item expanded. Both section headers link to their respective full pages (Dashboards and Anomalies). ## Using AI Canvas ### Starting a Conversation Type your question in the input box at the bottom and press Enter. You can also click one of the suggested prompts to get started. Example prompts: - "Create dashboard for high CPU usage by container" - "Search error logs for last 15 min" - "Create an alert for CPU throttling" - "Summarize logs for last 15 mins" - "How do I send k8s logs to Oodle?" ### Tool Calls and Inline Results The AI assistant uses tools to interact with your data. As it works, you will see expandable tool call cards showing what the assistant is doing — for example, finding metrics, running PromQL queries, or saving a dashboard. Query results such as charts and tables are rendered inline in the conversation. ### Auto-Approve Actions Some actions (like creating an alert or navigating to a page) require approval before executing. Enable the **Auto-approve actions** checkbox at the bottom of the chat to let the assistant execute these actions without prompting. ### Following Up After the assistant responds, you can ask follow-up questions in the same conversation. For example, after creating a dashboard you might ask the assistant to add more panels or adjust the time range. ## Chat History AI Canvas persists your conversations. The **Chat History** sidebar on the left lists all previous sessions with their title, age, and message count. Click any entry to reload that conversation and continue where you left off. Click **Start New Chat** to begin a fresh session. ## Example Workflows ### Create a Dashboard > "Create a health dashboard for the frontend service" The assistant will: 1. Search for relevant metrics in your environment 2. Run PromQL queries to verify the metrics return data 3. Build a Grafana dashboard with appropriate panels 4. Save the dashboard and provide a link to open it ### Debug an Alert > "Why is my CPU throttling alert firing?" The assistant will: 1. Look up the alert definition and current status 2. Query the underlying metrics to identify the source 3. Check logs and traces for correlated errors 4. Summarize findings and suggest next steps ### Search Logs > "Show me error logs from the payment service in the last hour" The assistant will: 1. Identify the appropriate log index 2. Run a filtered query for error-level logs 3. Summarize the top patterns and provide representative log entries --- ## Claude Claude ships with built-in OpenTelemetry support across all of its coding surfaces: the Claude Code CLI, the desktop app, and Cowork. Once enabled, Oodle collects metrics and event logs and turns them into use-case dashboards covering adoption, cost and ROI, engineering impact, tool behavior, and efficiency across your organization. ## Getting Started ### 1. Enable Telemetry The fastest way is to use the integration tile in the Oodle UI: 1. Navigate to **Settings → Integrations** 2. Open the **AI Observability** section 3. Click the **Claude Observability** tile 4. Select an API key and follow the steps shown The tile generates a ready-to-use configuration and lets you choose how to distribute it: to a single developer, to everyone working in a repository, or to your whole organization. Replace ``, ``, and `` in the snippets below with values from the integration tile. #### Individual For a single developer, export the variables directly in your shell before running `claude`: ```bash export CLAUDE_CODE_ENABLE_TELEMETRY=1 export OTEL_METRICS_EXPORTER=otlp export OTEL_LOGS_EXPORTER=otlp export OTEL_TRACES_EXPORTER=otlp export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf export OTEL_EXPORTER_OTLP_ENDPOINT=https:// export OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY=, X-OODLE-INSTANCE=" export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=delta export OTEL_LOG_USER_PROMPTS=1 export OTEL_LOG_TOOL_DETAILS=1 # Optional: resource attributes for team/repo breakdowns export OTEL_RESOURCE_ATTRIBUTES="git.repository.name=my-repo,org.team.name=platform,org.department=engineering,org.cost_center=eng-123" ``` :::tip For faster feedback during initial setup, lower the export intervals: ```bash export OTEL_METRIC_EXPORT_INTERVAL=10000 export OTEL_LOGS_EXPORT_INTERVAL=5000 ``` ::: #### Repository To share telemetry settings with everyone working in a repository, commit them to `.claude/settings.json` in the project. Claude Code applies them for anyone who works in the repo, with no per-developer setup required: ```json { "env": { "CLAUDE_CODE_ENABLE_TELEMETRY": "1", "OTEL_METRICS_EXPORTER": "otlp", "OTEL_LOGS_EXPORTER": "otlp", "OTEL_TRACES_EXPORTER": "otlp", "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1", "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf", "OTEL_EXPORTER_OTLP_ENDPOINT": "https://", "OTEL_EXPORTER_OTLP_HEADERS": "X-API-KEY=, X-OODLE-INSTANCE=", "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE": "delta", "OTEL_LOG_USER_PROMPTS": "1", "OTEL_LOG_TOOL_DETAILS": "1", "OTEL_RESOURCE_ATTRIBUTES": "git.repository.name=my-repo,org.team.name=platform,org.department=engineering,org.cost_center=eng-123" } } ``` #### Server-managed (organization-wide) To distribute settings across your whole organization without device management (MDM), use the [Claude Code admin console](https://claude.ai/admin-settings/claude-code). Paste the same `env` configuration into **Admin Settings → Claude Code → Managed settings**. Clients fetch it when users authenticate and poll for updates hourly. - Requires a Claude for Teams or Enterprise plan, and the Owner or Primary Owner role to edit. - OpenTelemetry settings take effect after a full Claude Code restart. See the [server-managed settings documentation](https://code.claude.com/docs/en/server-managed-settings) for delivery, caching, and precedence details. ### 2. Verify Data Once telemetry starts flowing (metrics export every 60 s, events every 5 s by default), navigate to **AI Cost Management → Claude** in the Oodle sidebar. ## Dashboards The Claude analysis page is organized around the questions engineering leadership asks of a coding agent. Each tab embeds a dashboard that leads with headline numbers, followed by trends and ranked breakdowns. ### Usage and Adoption Is the team using it? | Panel | Description | |-------|-------------| | **Active Users** | Distinct engineers who ran Claude | | **Sessions** | Sessions started, plus sessions per active user | | **Active Time** | CLI processing time vs user interaction time | | **Surface / Model / Terminal Mix** | Where and how sessions run (CLI, desktop, Cowork; model share; editor terminals) | | **Version Adoption** | Activity by CLI version, to track upgrade velocity | | **Top Users** | Engineers ranked by sessions and active time | ### Cost and ROI What are we paying, and what do we get for it? | Panel | Description | |-------|-------------| | **Total Spend** | Estimated USD spend, the headline budget number | | **Cost per Active User** | Average spend per engineer, for seat-cost modeling | | **Cost per Commit / Cost per PR** | Unit economics of shipped output | | **Sessions Producing Outcomes** | Share of sessions that led to a commit or PR | | **Top Spenders** | Users and sessions ranked by spend | | **Spend Breakdown** | Spend by model, query source, agent, and skill | | **Team and Repository Breakdown** | Spend by `org.team.name` and `git.repository.name` resource attributes | ### Engineering Impact What did it produce? | Panel | Description | |-------|-------------| | **Lines Added / Removed** | Code throughput from accepted suggestions | | **Commits and Pull Requests** | Shipped output over time | | **Edit Accept Rate** | Share of suggested edits engineers accepted, with health thresholds | | **Avg Lines per Session** | Output volume per session | | **Output Leaderboards** | Top users by lines added, commits, and PRs | ### Tools and Automation Is the agent working well? | Panel | Description | |-------|-------------| | **Tool Calls** | Invocation volume, distinct tools, and per-tool trends | | **Tool Duration** | Average execution time by tool | | **Result Size** | Bytes returned per tool, the main driver of context growth and cost | | **Bash CLI Breakdown** | Specific commands run through Bash, by count and output size | | **MCP Servers and Tools** | External integration usage and latency | | **Skills** | Invocations of codified team workflows | ### Efficiency Is spend being used well? | Panel | Description | |-------|-------------| | **Cache Hit Rate** | Share of context served from cache, with health thresholds | | **Token Split** | Input vs cache read vs cache creation volume | | **Ghost Cost Leaderboard** | Sessions ranked by cache re-priming relative to new input | | **Cost per Line of Code** | Output per dollar, overall and by model | | **Prompt Length Analysis** | Prompt size distribution and cost per prompt | ## Sessions The Sessions tab shows individual Claude sessions: | Column | Description | |--------|-------------| | **Start Time** | When the session began | | **User** | Email of the developer | | **Model** | Primary model used | | **Prompts** | Number of user prompts | | **Duration** | Wall-clock duration | | **Cost** | Total estimated cost | | **Tools** | Number of tool calls | | **Tokens** | Total tokens (input + output) | | **Errors** | Count of errors | Click any row to open a **Session Detail** drawer showing a turn-by-turn timeline of every event. ### Session Detail Drawer The drawer displays: - **Session metadata**: user, model, app version, terminal type - **Aggregated stats**: total cost, tokens, tool calls, errors, duration - **Turn-by-turn timeline**: each turn is collapsible and shows the user prompt followed by individual events (API requests, tool calls, decisions). Every event row is expandable to reveal the full raw JSON payload. ## What Gets Collected ### Metrics Claude exports the following as OpenTelemetry metrics (delta temporality): | Metric | Key Labels | Description | |--------|------------|-------------| | `claude_code_token_usage` | `type`, `model`, `user_email` | Token count by type (input, output, cacheRead, cacheCreation) | | `claude_code_cost_usage` | `model`, `user_email` | Estimated cost in USD | | `claude_code_session_count` | `user_email` | Sessions started | | `claude_code_active_time_total` | `type` | User and CLI active time in seconds | | `claude_code_lines_of_code_count` | `type` | Lines added / removed | | `claude_code_commit_count` | `user_email` | Commits created | | `claude_code_pull_request_count` | `user_email` | Pull requests created | | `claude_code_code_edit_tool_decision` | `decision` | Edit accept / reject decisions | All metrics also carry `session_id`, `service_version`, `terminal_type`, and `job` (the surface: CLI, desktop app, or Cowork). Tool call counts, durations, and result sizes are derived from the `claude_code.tool_result` events below and power the Tools and Automation dashboard. ### Events (Logs) Events are exported via the OpenTelemetry logs protocol. Each event has a `log.attributes.event_type` field: | Event Type | Key Attributes | |------------|----------------| | `claude_code.user_prompt` | Prompt text (opt-in), character count | | `claude_code.api_request` | Model, cost, input/output tokens, cache tokens, duration | | `claude_code.tool_result` | Tool name, success, duration | | `claude_code.tool_use_decision` | Decision (accept/reject), source | Events are grouped by `session_id` to reconstruct the turn-by-turn timeline on the Sessions tab. ## Multi-team Organization Support Organizations with multiple teams or departments can add custom [resource attributes](https://code.claude.com/docs/en/monitoring-usage#multi-team-organization-support) to segment telemetry by repository, team, department, or cost center. ### Recommended Attributes | Attribute | Purpose | Example | |-----------|---------|---------| | `git.repository.name` | Per-repository breakdowns | `my-service` | | `org.team.name` | Team-level filtering | `platform` | | `org.department` | Department or business unit | `engineering` | | `org.cost_center` | Charge-back / cost allocation | `eng-123` | ### Configuration Set `OTEL_RESOURCE_ATTRIBUTES` as a comma-separated list of key=value pairs: ```bash export OTEL_RESOURCE_ATTRIBUTES="git.repository.name=my-repo,org.team.name=platform,org.department=engineering,org.cost_center=eng-123" ``` Or add it to your managed settings file (`.claude/settings.json`): ```json { "env": { "OTEL_RESOURCE_ATTRIBUTES": "git.repository.name=my-repo,org.team.name=platform,org.department=engineering,org.cost_center=eng-123" } } ``` :::warning Formatting requirements Values must **not** contain spaces. Use underscores, camelCase, or percent-encoding for special characters. See the [Claude Code docs](https://code.claude.com/docs/en/monitoring-usage#multi-team-organization-support) for the full specification. ::: Once set, these attributes appear on all metrics and events, allowing you to: - Filter dashboards by team or department - Track costs per cost center - Create per-repository usage reports - Set up team-specific alerts ## Further Reading - [Claude Code Monitoring Documentation](https://code.claude.com/docs/en/monitoring-usage) - [OpenTelemetry Integration](/integrations/logs/otel) - [Claude Code MCP Integration](/integrations/mcp/claude) --- ## OpenAI Codex OpenAI Codex has native OpenTelemetry support. Once enabled, Oodle collects metrics and event logs and turns them into use-case dashboards covering adoption, token consumption, tool behavior, and performance and reliability across your organization. ## Getting Started ### 1. Enable Telemetry The fastest way is to use the integration tile in the Oodle UI: 1. Navigate to **Settings → Integrations** 2. Open the **AI Observability** section 3. Click the **Codex Observability** tile 4. Select an API key and follow the steps shown Alternatively, add the following to your Codex configuration file (`~/.codex/config.toml`): ```toml #:schema https://developers.openai.com/codex/config-schema.json [otel] environment = "production" log_user_prompt = true exporter = { otlp-http = { endpoint = "https:///ingest/otel/v1/logs", protocol = "binary", headers = { "X-API-KEY" = "", "X-OODLE-INSTANCE" = "", }, }} metrics_exporter = { otlp-http = { endpoint = "https:///v2/otlp/metrics/", protocol = "binary", headers = { "X-API-KEY" = "", "X-OODLE-INSTANCE" = "", }, }} ``` Replace ``, ``, and `` with values from the integration tile. :::tip The integration tile in the Oodle UI generates a ready-to-copy `config.toml` with the correct endpoints and API key pre-filled. ::: #### Alternative: Environment Variables For quick testing you can export the standard OpenTelemetry variables directly in your shell: ```bash export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https:///v2/otlp/metrics/ export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https:///ingest/otel/v1/logs export OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY=, X-OODLE-INSTANCE=" export OTEL_LOG_USER_PROMPTS=1 export OTEL_METRIC_EXPORT_INTERVAL=10000 export OTEL_LOGS_EXPORT_INTERVAL=5000 ``` ### 2. Roll Out to Your Team Commit the `config.toml` above to your dotfiles repository or place it in each developer's `~/.codex/` directory via your configuration management tool. ### 3. Verify Data Once telemetry starts flowing, navigate to **AI Cost Management → Codex** in the Oodle sidebar. ## Dashboards The Codex analysis page is organized around the questions engineering leadership asks of a coding agent. Each tab embeds a dashboard that leads with headline numbers, followed by trends and ranked breakdowns. ### Usage and Adoption Is the team using it? | Panel | Description | |-------|-------------| | **Active Users** | Distinct engineers who ran Codex | | **Conversations** | Distinct Codex conversations, plus conversations per active user | | **Prompts** | User prompts submitted | | **Activity Trends** | Active users and conversations over time | | **Surface / Model / Version Mix** | Where activity runs: the IDE/app backend vs the terminal CLI, model share, and version adoption | | **Top Users** | Engineers ranked by prompts and tokens | ### Token Consumption Token volume is the primary driver of Codex spend, so this tab is the budget view. | Panel | Description | |-------|-------------| | **Total Tokens** | Aggregate token usage, the headline consumption number | | **Tokens per Active User / per Conversation** | Unit consumption for capacity and seat modeling | | **Cached Input Rate** | Share of input context served from cache, with health thresholds. Higher is cheaper | | **Token Breakdown** | Usage by token type (input, output, cached, reasoning), model, surface, and version | | **Top Users and Sessions** | Highest-consumption engineers and conversations | | **Prompt Economics** | Average prompt size and total tokens consumed per prompt | Use the **Token Type** dashboard filter to isolate a category (input, output, cached_input, reasoning_output). ### Tools and Automation Is the agent working well? | Panel | Description | |-------|-------------| | **Tool Calls** | Invocation volume and distinct tools used | | **Tool Success Rate** | Share of tool calls that succeeded, with health thresholds | | **Avg Tool Duration** | Mean execution time, overall and per tool | | **Per-tool Trends and Leaderboards** | Calls, failures, and latency ranked by tool | | **MCP Servers** | External integration usage grouped by MCP server | ### Performance and Reliability Codex exports rich latency telemetry, so this tab answers how fast and stable the agent feels. | Panel | Description | |-------|-------------| | **Avg Time to First Token** | How long users wait before Codex starts responding, with health thresholds | | **Avg Turn Duration** | End-to-end latency per turn | | **WebSocket Success Rate** | API transport health, with health thresholds | | **Latency Trends** | Time to first token / first message and turn duration over time | | **Startup Latency** | Prewarm and shell snapshot times from launch to a usable session | | **Per-model Latency** | Time to first token and turn duration ranked by model | This tab is built on Codex's native metrics, which carry model, originator, and version dimensions; use its dashboard filters to slice by those. ## Sessions The Sessions tab shows individual Codex sessions: | Column | Description | |--------|-------------| | **Start Time** | When the session began | | **Session** | Codex conversation ID | | **User** | Email of the developer | | **Surface** | Where Codex ran: `app-server` (IDE/extension) or `cli` (terminal) | | **Duration** | Wall-clock duration | | **Tokens** | Total tokens (input + output + cached) | Click any row to open a **Session Detail** drawer showing a turn-by-turn timeline of every event. ### Session Detail Drawer The drawer displays: - **Session metadata**: user, model, app version - **Aggregated stats**: tokens, tool calls, errors, duration (only populated fields are shown) - **Turn-by-turn timeline**: each turn is collapsible and shows individual events (SSE events, WebSocket events, tool calls). Every event row is expandable to reveal the full raw JSON payload. ## What Gets Collected ### Metrics Codex exports the following as OpenTelemetry metrics (delta temporality): | Metric | Extra Labels | Description | |--------|--------------|-------------| | `codex_turn_token_usage` | `token_type` | Token count by type (input, output, cached, reasoning, total) | | `codex_thread_started` | none | Conversations started | | `codex_turn_tool_call` | none | Tool invocations per turn | | `codex_websocket_request` | `success` | WebSocket API requests | | `codex_turn_e2e_duration_ms` | none | End-to-end turn latency | | `codex_turn_ttft_duration_ms` | none | Time to first token | | `codex_turn_ttfm_duration_ms` | none | Time to first message | | `codex_websocket_event_duration_ms` | none | WebSocket event processing time | | `codex_startup_prewarm_duration_ms` | none | Startup prewarm latency | | `codex_shell_snapshot_duration_ms` | none | Shell snapshot capture latency | | `codex_turn_network_proxy` | none | Turns routed through the network proxy | All native metrics carry `model`, `originator`, and `app_version`. These power the Performance and Reliability dashboard. Oodle also derives session-level metrics from the event logs below: `oodle_logs_codex_token_usage` (tokens keyed by conversation, user, model, surface, version, and token type), `oodle_logs_codex_tool_count` and `oodle_logs_codex_tool_duration_ms` (per-tool activity with MCP server and success dimensions), and `oodle_logs_codex_prompt_length_bytes` (prompt sizes). These power the Usage and Adoption, Token Consumption, and Tools and Automation dashboards plus the Sessions tab, and they honor the sidebar User and Surface filters. ### Events (Logs) Events are exported via the OpenTelemetry logs protocol. Each event has an `attributes.event.name` field: | Event Type | Key Attributes | |------------|----------------| | `codex.conversation_starts` | Conversation ID, model, user email | | `codex.sse_event` | Model, event kind, token counts, duration | | `codex.websocket_event` | Model, event kind, duration, success | | `codex.websocket_request` | Model, duration, success | | `codex.websocket_connect` | Model, duration | | `codex.user_prompt` | Prompt text, prompt length | | `codex.tool_decision` | Tool name, decision, source | | `codex.tool_result` | Tool name, success, duration, error | Events are grouped by `conversation.id` to reconstruct the turn-by-turn timeline on the Sessions tab. ## Further Reading - [Codex Observability Documentation](https://developers.openai.com/codex/config-advanced#observability-and-telemetry) - [OpenTelemetry Integration](/integrations/logs/otel) --- ## Factory Droid Factory's Droid CLI has native OpenTelemetry support. Once enabled, Oodle collects metrics so you can track adoption, engineering impact, tool activity, and the repositories Droid works in across your organization. ## Getting Started ### 1. Enable Telemetry The fastest way is to use the integration tile in the Oodle UI: 1. Navigate to **Settings → Integrations** 2. Open the **AI Observability** section 3. Click the **Factory Droid** tile 4. Select an API key and follow the steps shown Alternatively, set two environment variables wherever Droid runs (shell profile, CI environment, or MDM-managed configuration): ```bash # Droid appends /v1/metrics to the endpoint export OTEL_TELEMETRY_ENDPOINT=https:// export OTEL_TELEMETRY_HEADERS="X-API-KEY=,X-OODLE-INSTANCE=" ``` Replace ``, ``, and `` with values from the integration tile. Metrics export every 60 seconds while sessions are active, and flow to both Factory and Oodle simultaneously. A failure to reach Oodle never affects Droid itself. ### 2. Roll Out to Your Team Add the two environment variables to your dotfiles repository, developer machine images, or configuration management tool. ### 3. Verify Data Once telemetry starts flowing, navigate to **AI Cost Management → Factory Droid** in the Oodle sidebar. ## Analysis Page The Factory Droid page is organized around the questions engineering leadership asks, with a tab per use case. ### Adoption Is Factory getting adopted, and by whom? | Panel | Description | |-------|-------------| | **Active Users / Sessions** | Distinct users and sessions with Droid activity | | **Logins and Slash Commands** | Engagement signals over the selected range | | **Activity by Model** | Which models drive Droid usage | | **Model Mix / Autonomy Mode / Environment** | How Droid is run: model split, autonomy levels, interactive vs other environments | | **Version Adoption** | How quickly the team upgrades the Droid CLI | | **Top Users by Activity** | The power users driving adoption, sorted and paginated | ### Engineering Impact What is Droid actually producing? | Panel | Description | |-------|-------------| | **Files Modified / Lines Modified** | Code change volume attributable to Droid | | **Commits and PRs Created** | How much of Droid's work ships | | **Files Read vs Files Modified** | Exploration vs change balance | | **By Extension** | Which parts of the stack Droid touches most | | **Per-User Leaderboards** | Lines, commits, and PRs by developer | ### Tools & Automation How does Droid work, and where does it struggle? | Panel | Description | |-------|-------------| | **Tool Invocations and Success Rate** | Overall tool call volume and health | | **Execution Time (p50 / p95)** | Tool latency trends and per-tool p95 | | **Tool Failures** | Which tools fail most | | **MCP Tools / Skills / Hooks** | Automation surface usage: MCP calls by server, skill invocations, hook activity | | **Installed Skills Inventory** | Which skills are installed, and where | ### Repo Insights Where across your codebase estate does Droid operate, and how healthy are those repos? Built from the `droid_repo_metadata` metric that Droid emits for every session: | Panel | Description | |-------|-------------| | **Repos Touched / Sessions per Repo** | Coverage of your repository estate | | **Repos with Tests / CI / Linters / Docs** | Guardrail coverage of the repos Droid works in | | **Primary Language Distribution** | Language mix of touched repos | | **Repo Profile** | Per-repo size, languages, contributors, and commit velocity | | **Repo Guardrails** | Test frameworks, CI/CD, linters, security, and observability tooling per repo | :::note Repositories are identified by a privacy-preserving hash of the remote URL, so repository names and URLs never leave the developer's machine. ::: ### Sessions The Sessions tab lists individual Droid sessions built entirely from metrics: | Column | Description | |--------|-------------| | **Start Time** | When the session began | | **Session** | Droid session ID | | **User** | Email of the developer | | **Duration** | Wall-clock duration | | **# Tools** | Tool invocations in the session | | **Files Read** | Files Droid read | | **Commits** | Commits created | | **# PRs** | Pull requests created | ## What Gets Collected Droid exports the following OpenTelemetry metrics with delta temporality. Every data point carries `user.email`, `user.id`, `session.id`, `model_id`, `organization.id`, and `service.version`, so all views can be filtered by user, model, and version. | Metric | Extra Labels | Description | |--------|--------------|-------------| | `droid.tool.invocations` | `tool_name`, `tool_category`, `tool_runtime`, `tool_succeeded`, `environment_type`, `session_autonomy_mode` | Tool invocations | | `droid.tool.execution_time` | same as invocations | Tool execution time histogram (ms) | | `droid.code.files_read` | `file_extension` | Files read | | `droid.code.files_modified` | `file_extension` | Files modified | | `droid.code.lines_modified` | | Lines of code modified | | `droid.git.commits` | | Commits created | | `droid.git.pull_requests` | | Pull requests created | | `droid.mcp.tool_invocations` | `mcp_server`, `tool_name` | MCP tool invocations | | `droid.skill.invocations` | `skill_name` | Skill invocations | | `droid.hook.invocations` | | Hook invocations | | `droid.slash_command.invocations` | `slash_command_name` | Slash command usage | | `droid.auth.login_success` | `auth_login_method` | Successful logins | | `droid.repo.metadata` | `repo_*` profile labels | Per-session repository profile snapshot (language, size, tests, CI, linters, contributors, commit velocity) | ## Further Reading - [Factory Telemetry Documentation](https://docs.factory.ai/enterprise/telemetry-export) - [OpenTelemetry Integration](/integrations/logs/otel) --- ## AI Cost Management Oodle's AI Cost Management gives engineering teams visibility into how AI coding agents are used across the organization. Track token usage, costs, session activity, tool calls, and more, all in one place. ## Why Monitor AI Agents? AI coding agents are powerful but opaque. Without observability you cannot answer questions like: - **How much are we spending** on AI-assisted coding? - **Which models** are being used, and by whom? - **How many sessions** run per day or week? - **Are tool calls succeeding**, or are agents hitting errors? - **What prompts** are developers sending to the agent? Oodle collects telemetry exported by AI agents via OpenTelemetry and surfaces it through a dedicated analysis page with pre-built dashboards and a session-level event timeline. ## Supported Agents | Agent | Metrics | Events / Logs | Status | |-------|---------|---------------|--------| | [**Claude**](./claude-code) | Token usage, cost, sessions, active time, lines of code, commits, PRs | User prompts, API requests, tool results, tool decisions | GA | | [**OpenAI Codex**](./codex) | Token usage, conversations, tool calls, WebSocket requests, performance timings | SSE events, WebSocket events, conversation starts, tool decisions, tool results | GA | | [**Factory Droid**](./factory) | Tool invocations, files read/modified, lines of code, commits, PRs, skills, repo profiles | Metrics only | GA | ## Getting Started 1. Navigate to **Settings → Integrations** 2. Open the **AI Observability** section 3. Click the tile for the agent you want to set up 4. Select an API key and follow the steps shown See the agent-specific pages for detailed setup instructions: - [Claude Setup](./claude-code#getting-started) - [OpenAI Codex Setup](./codex#getting-started) - [Factory Droid Setup](./factory#getting-started) ## Analysis Page The AI Agent Analysis page is available in the sidebar under **AI Cost Management**. It has a set of dashboard tabs tailored to each agent, plus a **Sessions** tab. Use the **agent dropdown** at the top of the sidebar to switch between Claude, Codex, and Factory Droid. ### Dashboard Tabs Each agent gets pre-built dashboards organized around the questions leadership asks: adoption, cost and ROI, engineering impact, and tool activity. For example, Claude has Usage and Adoption, Cost and ROI, Engineering Impact, Tools and Automation, and Efficiency tabs; see each agent's page for its tab set. ### Sessions Tab Shows a table of individual sessions with key stats. Click any row to open a **Session Detail** drawer showing a turn-by-turn timeline of every event. ### Sidebar Filters A sidebar on the left lets you filter data by: - **Time Range**: Grafana-style time range picker - **Event Type**: filter events by type (sessions tab only) - **Status**: OK or HasError (sessions tab only) - **Model**: filter by LLM model - **User**: filter by developer email - **App Version**: filter by agent version - **Terminal**: filter by terminal type ## Best Practices - **Start with a single user** to verify telemetry is flowing before rolling out org-wide. - **Enable prompt logging** to get full prompt text in the event timeline, which is useful for understanding usage patterns. - **Set up alerts** on cost or token metrics to catch unexpected spending spikes. - **Use sidebar filters** to drill down by user or model when investigating specific sessions. --- ## Skills & Runbooks Skills and runbooks add domain expertise to Oodle's AI assistant. They capture your team's debugging workflows, investigation playbooks, and operational procedures so the AI can follow them automatically. When you ask a question in [AI Canvas](/ai-canvas) or through [MCP](/integrations/mcp), Oodle detects relevant skills and applies them to guide its investigation. You can also attach skills directly to [monitors](/alerts/monitors) so the AI follows a specific runbook when that alert fires. {/* */} ## Concepts ### Skills Skills are structured investigation workflows. Each skill has a **name**, a **description** that tells the AI when to use it, and a set of **steps** written in natural language - similar to an `AGENTS.md` file. The AI follows these steps when the skill is triggered. ### Runbooks Runbooks are attached to specific monitors and guide the AI when that alert fires. Unlike skills, runbooks are **created automatically** - Oodle learns from how your team investigates incidents and generates a runbook to handle future instances of the same alert. You can review, edit, or disable auto-generated runbooks at any time. ## Creating a Skill 1. Navigate to **AI Center > Skills** in the sidebar. 2. Click **+ New Skill** in the top-right corner. 3. Fill in the following fields: | Field | Description | |-------|-------------| | **Name** | A short identifier for the skill (up to 100 characters). | | **Description** | Tells the AI when to use this skill. Be specific - for example, *"Use when the user asks whether an issue affects one customer or multiple customers and wants cross-cluster comparison."* | | **Steps** | The investigation procedure the AI should follow. Supports Markdown. Use the **Write**, **Preview**, and **Upload file** tabs to author the content. | 4. Click **Save**. ### Writing Effective Steps Write steps the way you would brief a new on-call engineer. Include: - What to check first and in what order. - Which metrics, logs, or traces to query. - How to interpret the results. - What output format to produce (verdict, comparison table, confidence level). **Example - API query service fan-out investigation:** > **Name:** `api-query-fanout-investigation` > > **Description:** When investigating api-server query errors, > look for massive fan-out across shards and HTTP 400 error > responses returning unexpected content. ```markdown When api-server query errors spike, investigate the query-worker container logs. 1. Look for 'StatusCode: 0, canceled, context canceled' warnings - this indicates a massive fan-out across shards (e.g., data-partition-{0-9}) where queries are being canceled due to timeouts. 2. Check for 'StatusCode: 400' errors returning HTML/XML instead of expected JSON. This means an upstream dependency is returning an error page, causing deserialization failures in the query path. 3. Verify if the failing queries are missing required parameters like 'start' or 'end' timestamps. Missing parameters cause the upstream to reject the request with a 400. 4. If fan-out is confirmed, check whether the query scope is too broad (too many shards) and whether adding tighter label filters would reduce the blast radius. ``` ## Attaching Skills to Monitors You can attach a skill or runbook to a monitor so the AI references it when investigating that alert: 1. Open the monitor you want to configure. 2. In the monitor settings, select a skill under the **Runbook** field. 3. Save the monitor. When this monitor fires and you ask the AI about it, the attached runbook is loaded automatically. ## Skill Sources Skills can come from two sources, visible in the **Source** filter on the Skills & Runbooks page: | Source | Description | |--------|-------------| | **User** | Created manually by your team. | | **Auto-learned** | Generated by Oodle based on observed patterns and past investigations. You can review, edit, or disable these. | ## Enabling and Disabling Skills Each skill has an **Enabled** toggle. Disabled skills are not loaded by the AI during conversations. Use this to temporarily remove a skill without deleting it. ## Using Skills ### In AI Canvas Skills are loaded on demand. When you ask a question in AI Canvas, the AI matches your query against skill descriptions and applies relevant skills automatically. You can also reference a skill by name in your prompt. ### Via MCP Skills are exposed through the Oodle MCP server. Any AI agent connected to Oodle's MCP - such as [Claude Code](/integrations/mcp/claude) or [Cursor](/integrations/mcp/cursor) - can discover and invoke skills during a conversation. ### With Alerts When you ask the AI about a firing alert that has an attached runbook, the AI follows the runbook steps to investigate and report findings. ## Managing Skills From the Skills & Runbooks page you can: - **Search** skills using the search bar. - **Filter** by type (Skill / Runbook), source (Auto-learned / User), or status (Enabled). - **Edit** a skill by clicking on it to open the edit dialog. - **Clone** an existing skill to create a variation. - **Delete** a skill from the edit dialog. --- ## Terraform for alerts Configure all your alerts, notifiers and notification policies through terraform. ## Advantages of managing alerts as code * They are version-controlled, making changes and rollbacks more transparent and trackable. * This approach allows you to standardize alert configurations across environments, eliminating manual errors and drift between staging and production. You can apply the same alert configuration across all your deployments / environments. * Terraform's declarative nature also makes it easy to spin up or tear down alerts alongside your infrastructure, ensuring your monitoring evolves with your stack. * Integrating alert management into your CI/CD pipeline means faster, more reliable deployments, as your alerts will always align with the infrastructure they monitor. ## How :::info Use oodle's [Terraform Provider](https://registry.terraform.io/providers/oodle-ai/oodle/latest/docs) ::: ### Prerequisites To setup Oodle Alerts via Terraform files, you'll need the following values: - `OODLE_INSTANCE`: Your Oodle instance ID. Go to `Settings` icon -> `API Keys` page in your Oodle UI to find out. (Oodle UI links: ) - `OODLE_API_KEY`: Your Oodle API key for authentication. Go to `Settings` icon -> `API Keys` in your Oodle UI to choose an appropriate key. (Oodle UI links: ) ### Example usage ```hcl terraform { required_providers { oodle = { source = "registry.terraform.io/oodle-ai/oodle" } } } # These can also be set as environment variables: # export OODLE_DEPLOYMENT=https://us1.oodle.ai/ # export OODLE_INSTANCE="my-instance" # export OODLE_API_KEY="my-api-key" provider "oodle" { deployment_url = "https://us1.oodle.ai/" instance = "my-instance" api_key = "my-api-key" } # Example usage of notifier, notification policy and monitor. # Refer to https://registry.terraform.io/providers/oodle-ai/oodle/latest/docs/resources/notifier # for schema documentation. resource "oodle_notifier" "notifier_test1" { name = "terraform_test_notifier" type = "pagerduty" pagerduty_config = { service_key = "foo" send_resolved = true } } # Refer to https://registry.terraform.io/providers/oodle-ai/oodle/latest/docs/resources/notification_policy # for schema documentation. resource "oodle_notification_policy" "test1" { name = "terraform_test_policy" notifiers = { critical = [oodle_notifier.notifier_test1.id] } } # Refer to https://registry.terraform.io/providers/oodle-ai/oodle/latest/docs/resources/monitor # for schema documentation. resource "oodle_monitor" "test1" { name = "terraform_test" promql_query = "sum(rate(oober_food_delivery_revenue_usd[3m]))" conditions = { critical = { value = 1210000 operation = ">" for = "3m" alert_on_no_data = true } } notification_policy_id = oodle_notification_policy.test1.id } ``` Apply terraform configuration ```bash terraform init terraform apply ``` --- ## Alerts export const FilteredDocCardList = () => { const category = useCurrentSidebarCategory(); const filteredItems = category.items.filter(item => item.label !== 'Alerts'); return ; }; ## Entities Oodle's alerting system allows you to monitor your metrics and receive notifications when certain conditions are met. There are three entities that make up the alerting system: - Monitors - Notification Policies - Notifiers Additionally, you can create composite monitors that combine multiple monitor conditions. Alerts functionality is available in the Oodle UI under the Bell icon in the sidebar navigation. ## Monitors Monitors are the core of the alerting system. They define: - A PromQL query to evaluate - Conditions for triggering alerts (`Warning` and `Critical` thresholds) - [Notification policy](#notification-policies) to be used for routing alerts - Labels allow specifying a set of additional labels to be attached to the alert. - Annotations specifies a set of informational labels that can be used to store longer additional information such as alert descriptions or runbook links. Labels and annotations can be templated in a similar manner as documented in [Prometheus alertmanager](https://prometheus.io/docs/prometheus/latest/configuration/template_examples/). Monitors are periodically evaluated and alerts are triggered when threshold conditions are met. See the [Monitors documentation](./monitors.mdx) for detailed configuration instructions. ## Notifiers [Notifiers](./notifiers.mdx) are the endpoints where alerts are sent. Oodle supports several types of notifiers: - [Slack](./notifiers.mdx#slack) - [PagerDuty](./notifiers.mdx#pagerduty) - [Opsgenie](./notifiers.mdx#opsgenie) - Webhook - [Google Chat](./notifiers.mdx#google-chat) - [Microsoft Teams](./notifiers.mdx#microsoft-teams) See the [Notifiers documentation](./notifiers.mdx) for detailed configuration instructions for each notifier type. ## Notification Policies Notification policies determine how alerts are routed to different notifiers. Policies allow you to: - Define separate notifiers for `Warning` and `Critical` alerts - Create global policies that apply to all monitors in addition to the specific policies attached to the monitors. See the [Notification Policies documentation](./notification-policies.mdx) for detailed configuration instructions. ## Composite Monitors Composite monitors allow you to create monitors based on combinations of multiple monitors. Using the `ALERTS` metric, you can implement complex logic such as: - Alert only when multiple conditions are met simultaneously (AND logic) - Alert when any of several conditions are met (OR logic) - Alert when a threshold number of monitors are firing For example, you can create a composite monitor that triggers only when both a database monitor and an API service monitor are firing, helping you identify dependent service failures. See the [Composite Monitors documentation](./composite-monitors.mdx) for detailed configuration instructions and examples. --- ## Composite Monitors Composite monitors allow you to create monitors based on combinations of other monitors. This is useful when you need to alert on complex conditions that depend on multiple monitors. ## What are Composite Monitors? Composite monitors use the `ALERTS` metric, which is automatically generated by Oodle for all active alerts. By querying this metric, you can create monitors that trigger based on the state of other monitors. ## Creating a Composite Monitor To create a composite monitor, you'll use the `ALERTS` metric in your PromQL query. Each alert has several labels that you can filter on: - `_oodle_monitor_id`: The unique ID of the monitor that generated the alert - `alertstate`: The current state of the alert (`firing` or `pending`) - `alertname`: The name of the alert - `_oodle_severity`: The severity level (`critical`, `warn` or `no_data`) ### Basic Example: AND Logic To alert when **both** Monitor A and Monitor B are firing: ```promql ( count(ALERTS{ _oodle_monitor_id="", alertstate="firing" }) ) and ( count(ALERTS{ _oodle_monitor_id="", alertstate="firing" }) ) ``` This composite monitor will only trigger when both specified monitors are actively firing. Set the condition to trigger when the result is greater than 0, indicating both alerts are firing. ### OR Logic Example To alert when **either** Monitor A or Monitor B is firing: ```promql count(ALERTS{ _oodle_monitor_id=~"|", alertstate="firing" }) ``` ### Severity-Based Composite Monitor To alert when any critical alert from a specific set of monitors is firing: ```promql count(ALERTS{ _oodle_monitor_id=~"monitor-id-1|monitor-id-2", alertstate="firing", _oodle_severity="critical" }) ``` ## Finding Monitor IDs To create composite monitors, you'll need the monitor IDs of the monitors you want to combine. You can find a monitor's ID: 1. Navigate to the monitor in the Oodle UI 2. The monitor ID is displayed in the URL params 3. You can also use labels to identify monitors instead of IDs --- ## Migrations Oodle supports importing alerts from various platforms to make your migration seamless. Choose the platform you're migrating from: ## Alertmanager / Prometheus Import your Prometheus alert rules and Alertmanager notification routes into Oodle. **Methods available:** - Upload `prometheus.yaml` and `alertmanager.yaml` configuration files directly - Connect to running Prometheus/Alertmanager instances and scrape alerts 👉 [Import Alertmanager Alerts](/integrations/metrics/prometheus/alertmanager-alerts) ## Grafana Import your existing Grafana Dashboards, Alerts, and Datasources into Oodle automatically using a Service Account Token. **What gets imported:** - Dashboards with panels, queries, and folder structure - Alert rules configured in Grafana - Datasource configurations 👉 [Import from Grafana](/integrations/metrics/grafana) --- ## Monitors Monitors allow you to define metric queries that trigger alerts based on specific conditions. The system periodically evaluates these queries and generates alerts when the results violate the defined conditions. ## Key Components of a Monitor ### Alert Name A descriptive name that identifies the monitor. You can use template variables in the name using [Prometheus templates](https://prometheus.io/docs/prometheus/latest/configuration/template_reference/) syntax. ### Query A PromQL query that the system evaluates periodically. You can use any valid PromQL query syntax. ### Conditions Conditions specify the threshold values that trigger alerts. There are two severity levels: - `Critical` - `Warning` You must define at least one condition, and you can set different thresholds for each severity level. The following operators are available for conditions: - Equal to - Not equal to - Above - Above or Equal to - Below - Below or Equal to ### Message A customizable message that accompanies alert notifications. You can enhance messages using template variables with [Prometheus templates](https://prometheus.io/docs/prometheus/latest/configuration/template_reference/) syntax. For example, with a query like `max by (job) (up)`, you can access the job name in your message using the template variable `{{ $labels.job }}`. :::note Oodle notifications automatically include the following information, so you don't need to add them to your message: - Alert name - Severity - Threshold - Metric Value (the current value when the alert fires) - Labels and Annotations ::: ### Notification Policy Notification policies determine where alert notifications are sent. You can configure multiple notifiers as destinations for your alerts. Each rule specifies which notifiers to trigger and can optionally be scoped by label conditions and severity. #### How Routing Rules Work Notification rules are evaluated **top to bottom** on a first-match basis. The first rule whose conditions match the alert fires its notifiers; subsequent rules are skipped. The last rule without any label conditions acts as the **fallback** and catches everything that didn't match an earlier rule. Rules follow an **If / Else If / Else** pattern: | Position | Prefix | Description | |----------|--------|-------------| | First rule (with conditions) | **If** | Evaluated first | | Additional rules (with conditions) | **Else If** | Evaluated only when preceding rules don't match | | Last rule (no conditions) | **Always** / **Else** | Fallback — matches all remaining alerts | You can drag and drop rules to reorder them. #### Label-Based Routing Each rule can include one or more **label conditions** to match alerts. Conditions within a single rule are combined with AND logic. Available operators are: - `is` — exact match - `is not` — negated match - `matches regex` — regex match - `does not match regex` — negated regex match Label values are auto-suggested from the monitor's query results, so you can pick from labels that actually appear in your data. **Example — Team-based routing:** Route alerts to the owning team's channel based on a `team` label: | Rule | Condition | Notifier | |------|-----------|----------| | **If** | `team` is `backend` | Slack `#backend-alerts` | | **Else If** | `team` is `frontend` | Slack `#frontend-alerts` | | **Else** | *(none — fallback)* | Slack `#ops-alerts` | **Example — Environment-based routing:** Escalate production alerts to your incident management tool while keeping non-production alerts in Slack: | Rule | Condition | Notifier | |------|-----------|----------| | **If** | `environment` is `production` | OpsGenie | | **Else** | *(none — fallback)* | Slack `#dev-alerts` | **Example — Combined conditions:** Conditions within a rule are AND'd together, so you can be very specific: | Rule | Conditions | Notifier | |------|------------|----------| | **If** | `team` is `payments` AND `environment` is `production` | PagerDuty | | **Else If** | `environment` is `production` | OpsGenie | | **Else** | *(none — fallback)* | Slack `#alerts` | #### Severity-Based Routing Within each rule, you can filter by alert severity using the **for** dropdown. The options are: | Option | Matches | |--------|---------| | **all** | Critical, Warning, and No Data alerts | | **critical** | Only Critical alerts | | **warning** | Only Warning alerts | | **no data** | Only No Data alerts | When you select a specific severity, you can add additional severity rows within the same rule to send different severities to different notifiers. For example, within a single rule you can send `critical` alerts to PagerDuty and `warning` alerts to Slack. #### Notification Policies vs. Direct Notifiers Each rule supports two action types, selectable via the dropdown next to the rule: - **notify on** — Send directly to one or more notifiers (Slack, OpsGenie, PagerDuty, etc.) - **trigger policy** — Delegate to a reusable [Notification Policy](/alerts/notification-policies) that defines its own routing, grouping, and timing ### Grouping Grouping controls how notifications are consolidated. You can choose from three options: - By Monitor: Receive a single notification per monitor (default behavior) - By Labels: Receive a notification for each unique set of specified labels - Disabled: Receive a notification for each individual timeseries For example, consider a monitor with this query that alerts when the `up` metric for any `job` is 0: ``` max by (job, instance) (up{}) ``` Here's how different grouping options affect notifications: - **By Monitor:** You receive one notification that includes all affected jobs and instances - **By Labels** (with `job` specified): You receive one notification per affected job - **Disabled:** You receive one notification per affected timeseries (job-instance combination) ### Notification Timings These parameters control the timing of grouped notifications: #### Group Wait Group Wait defines how long to wait before sending the first notification for a new group of alerts. The default is 30 seconds. #### Group Interval Group Interval defines how long to wait before sending a notification for new alerts added to a group that has already sent notifications. The default is 5 minutes. #### Repeat Interval The repeat interval specifies how often notifications are resent for active alerts. For example, if you have an active alert for the timeseries `up` with `job=foo` and `instance=bar`, setting a repeat interval of 10 minutes means the notification will be sent every 10 minutes until the alert is resolved. :::info For example, with the `up` metric monitor grouped "By Monitor", consider a group wait of 30 seconds and a group interval of 5 minutes: If two jobs go down within the first 30 seconds, you'll receive one notification covering both jobs. If a third job goes down a minute later, its notification will be delayed by the 5-minute group interval. ::: ### Labels Labels add metadata to alerts. You can use [Prometheus templates](https://prometheus.io/docs/prometheus/latest/configuration/template_reference/) syntax to create dynamic labels. A common use case is tagging monitors with owner teams to identify responsible teams for each monitor. ### Annotations Annotations provide additional context for alerts, such as descriptions or runbook links. Like labels, annotations support [Prometheus templates](https://prometheus.io/docs/prometheus/latest/configuration/template_reference/) syntax. ## Creating a Monitor with AI The quickest way to create a monitor is to ask the AI Assistant. Open the sidebar assistant (Cmd + I) or [AI Canvas](/ai-canvas) and describe the alert you need: - `Create an alert to notify me when container memory usage is above 90%` - `Alert me when the error rate on api-server exceeds 5%` - `Create an alert for container restarts in the last 10 minutes` The AI will build the PromQL query, set the threshold, and save the monitor. You can review and adjust it from the alerts page afterwards. ## Creating a Monitor Manually 1. Navigate to **Alerts** (Bell icon in sidebar) 2. Click **New Alert** 3. Configure the following settings: - **Name:** Enter a descriptive name for your monitor - **Query:** Specify the PromQL query to evaluate - **Conditions:** Define the alerting conditions - **Message:** (Optional) Add a message for alert notifications - **Notification Policy:** (Optional) Select routing rules for alerts - **Grouping:** (Optional) Configure how you want to group alerts. Additionally, you can configure various timing parameters by clicking on the **Clock** icon. - **Labels:** (Optional) Add metadata labels to the alerts - **Annotations:** (Optional) Include additional contextual information 4. Click **Save** to create the monitor --- ## Muting ## Overview You can mute alerts to temporarily silence notifications for specific alerts without deleting or disabling them permanently. This is useful during maintenance windows, known incidents, or when you need to reduce noise. ## When to Use Mute alerts when: - Performing scheduled maintenance - Investigating a known issue - Testing changes in production - Dealing with expected high traffic events - Reducing alert fatigue during incident response ## How to Mute an Alert 1. Go to the Alerts page. 2. Find the alert you want to mute. 3. Click on alert's context menu [•••] and select 'Mute'. 4. Select the duration for muting (options: 1 hour, 6 hours, 12 hours, 1 day, 2 days, infinite, or custom). 5. Optionally add labels for muting. 6. Click 'Add Muting Rule'. 7. To unmute, click on the red trash can. ## How to Unmute an Alert 1. Navigate to the Alerts page. 2. Filter or find muted alerts using the Muted filter in the left sidebar, or sort the 'Muted left' column. 3. Click on a muted alert's Muted indicator in the Muted left column. 4. Click on the red trash can to permanently remove it. ## Muted Alert Behavior - Muted alerts do not trigger notifications via email, Slack, PagerDuty, or other channels - Muted alerts still evaluate conditions, and change state to "firing" - You can view muted alert firing history in the alert timeline - Muted alerts appear with a 'Muted' badge in the alerts list - The system automatically unmutes alerts when the mute period expires ## FAQ ### Can I mute multiple alerts at once? Yes, you can select multiple alerts from the alerts list and mute in-bulk using the bulk actions menu. ### What happens if an alert triggers while muted? The alert condition is still evaluated, the state of the alert is appropriately updated, but no notifications are sent. You can see these occurrences in the alert history. ### Can I schedule a mute in advance? Yes, when selecting 'Custom' duration, you can set a future start time for the mute period. ### Are there limits on mute duration? No, there are no maximum limits on mute duration. ## Related Features - [Alert Conditions](/alerts/monitors#conditions) - Modify alert thresholds to reduce false positives - [Notification Policies](/alerts/notification-policies) - Configure which channels receive alert notifications - [Notifiers](/alerts/notifiers) - Configure notification channels --- ## Notification Policies Notification policies let you route alerts to different notifiers based on severity levels. Each policy can be configured with specific notifiers for both `Warning` and `Critical` severity levels. With notification policies, you can: - Create team-specific policies to route team alerts to designated notifiers - Set up deployment-specific policies to route alerts from different deployments (e.g. dev, staging, production) to appropriate notifiers ## Create a Notification Policy 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notification Policies** 2. Click the **+** icon. 3. Fill in the following fields: - Name: A descriptive name for your notification policy - Critical: (Optional) Select notifiers to receive alerts with `Critical` severity - Warning: (Optional) Select notifiers to receive alerts with `Warning` severity - Global: Enable this toggle to apply the policy to all monitors in addition to their individual notification policies. This is particularly useful when migrating from another system and you want to verify Oodle alerts while maintaining your existing alert system. By default, this toggle is **OFF**. 4. Click **Save** to create the notification policy :::note You can also create notification policies directly from the monitor create/edit page by clicking the **Add Notification** button. ::: ## Use a Notification Policy After creating a notification policy, you can attach it to any monitor during creation or editing. Once attached, the monitor's alerts will be automatically routed to the notifiers specified in the policy. --- ## Notifiers Notifiers are endpoints that receive alert notifications when monitor conditions are triggered. This guide explains each supported notifier type and provides step-by-step configuration instructions. ## Supported Notifiers ### Slack To send notifications to Slack, you'll need an incoming webhook URL. There are two methods to create one: 1. Slack Apps 2. Incoming Webhooks (Legacy) #### Slack Apps Follow these steps to create an incoming webhook using Slack Apps. For more details, see the official [Slack documentation on incoming webhooks](https://api.slack.com/messaging/webhooks). 1. Create a Slack App: - Visit [https://api.slack.com/apps?new_app=1](https://api.slack.com/apps?new_app=1) - Choose a name for your app - Select your workspace - Click **Create App** 2. Enable incoming webhooks: - Open the [Apps management dashboard](https://api.slack.com/apps) - Select your app - Click on Incoming Webhooks - Toggle **Activate Incoming Webhooks** to ON 3. Create a webhook: - Click **Add New Webhook to Workspace** - Select the channel for receiving messages - Click **Authorize** You'll receive a webhook URL that looks like this: ``` https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX ``` :::note Webhooks created through Slack Apps can only post to their designated channel. You'll need separate webhooks for different channels. ::: #### Incoming WebHooks App (Legacy) The legacy Incoming WebHooks app allows you to create webhooks that can send messages to any channel in your workspace. 1. Install the [Incoming WebHooks](https://slack.com/marketplace/A0F7XDUAZ-incoming-webhooks) app to your workspace 2. Click **Add to Slack**, select a default channel, then click **Add Incoming WebHooks Integration** :::note Legacy webhooks can send messages to any channel in your workspace, unlike Slack Apps webhooks which are channel-specific. ::: #### Configuration To set up a Slack notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Slack** from the dropdown 3. Fill in the following: - Name: A descriptive name for your notifier - API URL: The URL from Slack - Channel (optional): Override the default channel. Note: This only works with legacy webhooks :::note For Slack Apps webhooks, messages will always go to the channel specified during webhook creation, regardless of the channel setting in Oodle. ::: 4. Click **Save** to create the notifier ### Pagerduty To send notifications to Pagerduty, you'll need an API Key. Oodle supports following Pagerduty Integrations: 1. Events API V2 (Recommended) 2. Prometheus ##### Events API V2 (Recommended) [Events API V2](https://developer.pagerduty.com/docs/events-api-v2-overview) is rich Events API in comparison to Prometheus integration. For example, it allows to set Pagerduty Alert's Severity, attach images etc. which are not possible in Prometheus integration type. To get an Events API v2 API Key, follow: 1. Log in to PagerDuty and navigate to **Services** in the top menu bar. 2. Click on the service you are currently using, or create a new service. 3. Click **Integrations** and then **Add another integration**. 4. Select **Events API V2** from the list of integrations. 5. After creating the integration, you will be able to see the API Key for this integration. ##### Prometheus Prometheus Integration is less richer in comparison to Events API V2 Integration. To get a Prometheus Integration API Key, follow: 1. Log in to PagerDuty and navigate to **Services** in the top menu bar. 2. Click on the service you are currently using, or create a new service. 3. Click **Integrations** and then **Add another integration**. 4. Select **Prometheus** from the list of integrations. 5. After creating the integration, you will be able to see the API Key for this integration. #### Configuration To set up a Pagerduty notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Pagerduty** from the dropdown 3. Fill in: - Name: A descriptive name for your notifier - Integration: Choose `Events API V2` or `Prometheus` based on the Integration you have configured on Pagerduty. - API Key: Your Pagerduty integration API key 4. Click **Save** to create the notifier When using `Events API V2`, Oodle sets [Pagerduty Alert's Severity](https://support.pagerduty.com/main/docs/dynamic-notifications#severity-and-urgency-mapping) based on the Condition Severity defined within the monitor: - Critical condition in Oodle is mapped to `critical` severity in Pagerduty. - Warning condition in Oodle is mapped to `warning` severity in Pagerduty. Follow [Configure Dynamic notifications](https://support.pagerduty.com/main/docs/dynamic-notifications#configure-dynamic-notifications) in Pagerduty to govern alert notifications based on Severity. ### Opsgenie To send notifications to Opsgenie, you'll need an API key. Here's how to get one: 1. Log in to Opsgenie and navigate to [Integrations](https://app.opsgenie.com/settings/integration/integration-list) 2. Click **Add Integration** and select **Prometheus** 3. Configure the integration: - Enter a name - Select the team to receive alerts - Click **Continue** 4. Click **Turn on Integration** to activate it 5. [Optional] In `Incoming | Rules for creating and processing alerts` section, Click on `Create Alert` and update `Message` field from `[Prometheus]: {{message}}` to `{{message}}`. #### Configuration To set up an Opsgenie notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Opsgenie** from the dropdown 3. Fill in: - Name: A descriptive name for your notifier - API Key: Your Opsgenie integration API key 4. Click **Save** to create the notifier Oodle sets [Opsgenie's Priority](https://support.atlassian.com/opsgenie/docs/what-is-the-priority-level-of-integration/) based on the Condition Severity defined within the monitor: - Critical condition in Oodle is mapped to `P1` priority in Opsgenie. - Warning condition in Oodle is mapped to `P2` priority in Opsgenie. ### Rootly To send notifications to Rootly, you'll need a Bearer Token Secret from a Rootly Alertmanager source. 1. In Rootly, navigate to [Alert Sources > New Alertmanager Source](https://rootly.com/account/alert-sources/new?alerts_source%5Bsourceable_type%5D=Alerts%3A%3AAlertmanagerSource) 2. Give the source a name (e.g. "Oodle Alerts") 3. Under **Urgency**, add a condition: `$.alerts[0].labels._oodle_severity` is `warn` sets urgency to **Medium**, otherwise **High** 4. Save and copy the **Bearer Token Secret** #### Configuration To set up a Rootly notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Rootly** from the dropdown 3. Fill in: - Name: A descriptive name for your notifier - Bearer Token: The Bearer Token Secret from Rootly 4. Click **Save** to create the notifier Oodle delivers alerts in Alertmanager webhook format. Rootly uses the `_oodle_severity` label to map alert urgency based on the conditions you configured above. For more details, see the [Rootly Alertmanager docs](https://docs.rootly.com/integrations/alertmanager). ### Google Chat To send notifications to Google Chat, you'll need a webhook URL. Get the Webhook URL by following [Create a webhook for the Google Space](https://developers.google.com/workspace/chat/quickstart/webhooks) for the Google Space you want to send notification to. When creating the webhook in Google Chat, we recommend: - **Name**: `Oodle Alerts` - **Avatar URL**: `https://www.oodle.ai/img/logo_square.png` This gives alert messages a recognizable name and Oodle logo in Google Chat. #### Configuration To set up a Google Chat notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Google Chat** from the dropdown 3. Fill in: - Name: A descriptive name for your notifier - Webhook URL: The Webhook URL you created above. 4. Click **Save** to create the notifier. ### Microsoft Teams To send notifications to Microsoft Teams, you'll need a webhook URL. Get the Webhook URL by following [Create an incoming Webhook with Workflows](https://support.microsoft.com/en-gb/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) for the Teams Channel you want to send notifications to. #### Configuration To set up a Microsoft Teams notifier in Oodle: 1. Navigate to **Alerts** (Bell icon in sidebar) → click the ellipsis (**...**) → select **Notifiers** 2. Click the **+** icon and choose **Microsoft Teams** from the dropdown 3. Fill in: - Name: A descriptive name for your notifier - Webhook URL: The Webhook URL you created above. 4. Click **Save** to create the notifier ## Use a Notifier Once you have a notifier created, you can use it in a notification policy to route alerts to the notifier. See the [Notification Policies documentation](./notification-policies.mdx) for more details. --- ## Anomaly Alerting Anomaly Alerting lets you configure notification policies for anomalies detected by Oodle's anomaly detection system. When anomalies are detected in your logs or metrics, alerts are sent to the notification channels you configure. ## Getting Started Navigate to **Anomalies → Anomaly Alerting** in the sidebar. The page shows the available anomaly detection modules and their status. ## Detectors Table The page displays a table of all anomaly detectors: | Column | Description | |--------|-------------| | **Name** | Detector name (underscores are displayed as spaces). | | **Description** | What the detector monitors. | | **Data Sets** | The data source the detector operates on (e.g., `logs`, `metrics`), shown as badges. | | **Status** | Toggle switch to enable or disable the detector. | Clicking an enabled detector row opens a drawer that shows the alerts generated by that detector, using the same interface as the [Alerts](/alerts/) page. ## Notification Policy At the top of the page, the **Notification Policy** label shows the currently assigned policy. Click it to open a drawer where you can create or select an existing notification policy. Notification policies define which channels (Slack, email, PagerDuty, etc.) receive anomaly alerts. ### Setting a Notification Policy 1. Click the **Notification Policy** value (or the "-" if none is set). 2. In the drawer, select an existing policy or create a new one. 3. Click **Save and Add Notification Policy**. You can remove the current policy by clicking the delete icon next to the policy name. ## Enabling / Disabling Detectors Toggle the **Status** switch for each detector: - **Enabled** (blue) — The detector actively monitors for anomalies and sends alerts through the configured notification policy. The row becomes clickable to view related alerts. - **Disabled** (grey) — The detector is paused and does not generate alerts. The row is not clickable. :::note Toggling a detector requires edit permissions. Users without edit access will see disabled switches. ::: ## How It Works 1. Oodle continuously analyzes incoming logs and metrics using machine learning models. 2. When a pattern deviates significantly from the learned baseline, an anomaly is detected. 3. If the corresponding module is enabled and a notification policy is configured, an alert is sent. 4. Detected anomalies are also visible in the [Anomaly Detection](/anomaly-detection/) dashboard. ## Best Practices - **Enable both modules** for comprehensive coverage across logs and metrics. - **Set up a dedicated notification channel** for anomaly alerts to avoid mixing them with other alert types. - **Review anomalies regularly** on the Anomaly Detection dashboard, even if alerts are configured — not all anomalies may be severe enough to trigger notifications. - **Tune notification policies** to avoid alert fatigue. Start broad and narrow down as you learn which anomalies are actionable. ## Related Pages - [Anomaly Detection](/anomaly-detection/) — View and investigate detected anomalies. - [Alerts](/alerts/) — Configure alert rules and notification policies. --- ## Anomaly Detection Oodle automatically identifies unusual patterns in your metrics and logs, helping you catch issues before they impact your users. ![Anomalies List](/img/anomalies-list.png) ## Overview Anomaly detection helps you: - **Detect Issues Early**: Automatically identify unusual patterns before they become critical problems - **Reduce Alert Noise**: Focus on genuine anomalies by blocking repetitive or irrelevant patterns - **Compare Historical Patterns**: Understand how current behavior differs from your historical baselines - **Prioritize Issues**: Anomalies are ranked by severity to help you focus on what matters most ## How It Works Oodle continuously monitors your infrastructure and applications, automatically detecting: - **Metric Anomalies**: Unusual behavior in your infrastructure and application metrics - **Log Anomalies**: New or unusual patterns in your error logs When anomalies are detected: - They appear on the Anomalies page for you to review - You can set up alerts to be notified immediately - You can block repetitive or expected patterns to reduce noise ## Types of Anomalies ### Metric Anomalies Oodle detects unusual patterns in your infrastructure and application metrics, including: - **CPU Throttle**: Services experiencing CPU throttling - **High Node CPU**: Nodes with unusually high CPU usage - **Service Communication Errors**: Communication issues between services - **API Errors**: High error rates in your APIs - **Container Restarts**: Containers restarting unexpectedly - **High Memory**: Services with excessive memory usage - **Pod Phase Issues**: Pods in problematic states (e.g., CrashLoopBackOff) ### Log Anomalies Oodle identifies unusual patterns in your logs by detecting: - **New Patterns**: Error messages that haven't appeared before - **Spikes**: Sudden increases in existing error patterns - **High Volume**: Recurring errors with unusually high occurrence rates Each anomaly shows whether it's a new issue or a recurring problem, helping you prioritize your response. ## Using the Anomalies Page The Anomalies page (`/anomalies`) provides three main tabs: ### Anomalies Tab View all detected anomalies with comprehensive filtering and details: - **Filters**: Filter by type (metrics/logs), subtype, cluster, namespace, or service - **Time Range**: Select the time window to view anomalies - **Details**: Click on any anomaly to see: - Trend charts showing the anomaly over time - Related logs (for log anomalies) - Metric queries (for metric anomalies) - Labels and metadata ![Anomalies List](/img/anomalies-list.png) ### Detectors Tab Control which types of anomalies to detect: - Toggle individual detector types on/off - Changes save automatically - Useful for temporarily disabling noisy or irrelevant detectors ![Anomaly Detectors](/img/anomalies-detectors.png) ### Spam Tab Manage blocked anomalies to reduce noise: - **Search**: Search through blocked anomalies to find specific items - **View Blocked Items**: See all anomalies you've blocked - **Unblock**: Remove items from the blocklist to allow them to appear again ![Spam Management](/img/anomalies-spam.png) When you block an anomaly, it won't trigger alerts or appear in your anomaly feed, keeping your view focused on what matters. ## FAQ ### How does Oodle detect log anomalies? Oodle doesn't evaluate individual log lines. Instead it works at the **pattern** level: 1. **Filter to errors** — only error-level logs are considered. Non-error logs are never evaluated. 2. **Group by pattern** — error logs are bucketed by **cluster**, **container**, and **fingerprint** (a pattern identifier for structurally similar messages). 3. **Compare against 24 hours ago** — the total count for each group in the last 1-hour window is compared against the same 1-hour window 24 hours ago. 4. **Apply thresholds** — a group is flagged when: - It has **more than 4 occurrences** in the current hour, **and** - It meets one of these conditions: - **New Pattern** — the group didn't exist in the baseline window - **Spike** — the count has at least doubled compared to 24 hours ago - **Volume** — a recurring error with notable count that hasn't doubled ![Log anomaly detection flow](/img/anomaly-log-detection.svg) ### How does Oodle detect metric anomalies? Oodle runs predefined queries against your infrastructure and application metrics every 5 minutes: 1. **Query current and historical values** — each query produces a single value for the last 1-hour window and a comparison value from the same window 24 hours ago. 2. **Compare values** — an anomaly is flagged when: - The current value shows a **significant percentage increase** over the historical value (used for CPU throttling, node CPU, memory usage, API error rates, and service communication errors), or - The current value is **non-zero** (used for container restarts and pod phase issues, where any occurrence is notable). ![Metric anomaly detection flow](/img/anomaly-metric-detection.svg) ### What time window is used to detect anomalies? Both log and metric anomaly detection compare two one-hour windows: - **Current window**: the last hour (`now − 1h` to `now`) - **Baseline window**: the same hour one day ago (`now − 25h` to `now − 24h`) Using the same time-of-day accounts for diurnal traffic patterns. The checks run every 5 minutes, so anomalies are re-evaluated with fresh data on each cycle. ### How long after a fix before an anomaly disappears? Anomalies are not stored as persistent records — they are re-computed from live data every 5 minutes. Once a fix is deployed: 1. The one-hour sliding window needs to clear of pre-fix data. In the worst case this takes **up to one hour** after the fix. 2. On the next 5-minute check after the window clears, no new data point is emitted for that anomaly. 3. The anomaly's trend chart in the UI drops to zero and it effectively disappears from the Anomalies page. This means it can take **up to 1 hour** after a fix is deployed for the anomaly to fully disappear. ![Anomaly resolution timeline](/img/anomaly-resolution.svg) ### Can anomaly detection be customized? Yes — Oodle's anomaly detection is customizable. Reach out to us to tailor detection rules based on your specific data patterns and operational needs. ## Best Practices 1. **Review Regularly**: Check the Anomalies page regularly to catch emerging issues early 2. **Block Noise**: Use the blocking feature to suppress known non-issues or expected patterns 3. **Create Targeted Alerts**: Set up alerts for specific anomaly types that are most critical to your operations 4. **Investigate Recurring Anomalies**: Pay attention to recurring anomalies, as they may indicate persistent underlying issues 5. **Use Filters**: Filter by cluster, namespace, and service to focus on specific areas of your infrastructure --- ## Dashboards Dashboards are a way to query, visualize, and organize your observability data to gain deeper insights at a glance. Oodle provides dashboards for both metrics and logs. ## Metrics Dashboards Build dashboards using PromQL to visualize your metrics data. Create panels with time series graphs, gauges, stat panels, and more. - **Query with PromQL** - Use the query builder or write queries directly - **Dashboard variables** - Create dynamic, reusable dashboards - **Infrastructure as code** - Manage dashboards via Terraform [Explore Metrics Dashboards →](/dashboards/metrics) ## Logs Dashboards Create dashboards from your log data to monitor application health, track errors, and analyze patterns. Build visualizations and combine them into comprehensive monitoring views. - **Multiple chart types** - Line, bar, area, pie, heat map, and more - **Aggregations** - Count, sum, average, and unique count metrics - **Flexible grouping** - Date histograms, terms, filters, and ranges [Explore Logs Dashboards →](/dashboards/logs/log-dashboards) ## Explore Further --- ## Dashboards(Logs) Dashboards combine multiple visualizations into a single view, giving you a comprehensive overview of your log data. Build dashboards to monitor application health, track errors, and analyze user behavior. :::tip Try it Live Explore a working example dashboard in the [Oodle Playground](https://play.oodle.ai/logs/app/dashboards) to see a multi-panel dashboard. ::: ## Terminology | Term | Description | |------|-------------| | **Dashboard** | A collection of panels arranged on a canvas that provides a unified view of your data | | **Panel** | A single visualization displayed on a dashboard (also called a visualization) | | **Visualization** | A chart, graph, metric, or table that represents your log data visually | | **Index Pattern** | The data source that defines which logs are available for visualizations | ## Creating a New Dashboard ![Create Dashboard](/img/osd-create-dashboard.png) 1. Navigate to the **Dashboards** section from the sidebar 2. Click **Create Dashboard** 3. You'll see an empty dashboard canvas ready for panels ## Adding Visualizations ### Add Existing Visualization 1. Click **Add** in the toolbar 2. Select **Add from library** 3. Browse or search for saved visualizations 4. Click on a visualization to add it to the dashboard ### Create New Visualization 1. Click **Add** in the toolbar 2. Select **Create new** 3. Choose a visualization type 4. Configure and save the visualization 5. It will automatically be added to your dashboard ## Dashboard Controls ### Time Filter The time picker in the top navigation controls the time range for all panels. You can: - Select predefined ranges (Last 15 minutes, Last 24 hours, etc.) - Set custom absolute time ranges - Use relative time expressions ### Query Bar Enter a query in the search bar to filter data across all panels: ``` container_name:frontend AND log_level:error ``` ### Filters Click **Add filter** to create field-based filters that apply to all panels. Filters appear as pills below the query bar and can be: - Toggled on/off - Pinned across dashboards - Inverted (exclude matching documents) ### Refresh - **Manual refresh**: Click the refresh button - **Auto-refresh**: Set an interval (e.g., every 30 seconds) for live monitoring ## Panel Options Click the gear icon on any panel to access: | Option | Description | |--------|-------------| | **Edit visualization** | Modify the underlying visualization | | **Clone panel** | Duplicate the panel on this dashboard | | **Create drilldown** | Link to another dashboard or URL | | **Full screen** | Expand the panel to full screen | | **Replace panel** | Swap with a different visualization | | **Delete from dashboard** | Remove the panel | ## Saving Dashboards 1. Click **Save** in the toolbar 2. Enter a dashboard title 3. Optionally add a description 4. Choose whether to save the current time filter with the dashboard 5. Click **Save** ### Save Options - **Save as new dashboard**: Creates a copy with a new name - **Store time with dashboard**: Opens dashboard with the saved time range instead of the default ## Next Steps - Learn about [creating visualizations](/dashboards/logs/visualizations) to build custom panels for your dashboards - Explore the [logs interface](/logs-visualization) for ad-hoc log analysis --- ## Visualizations Visualizations are the building blocks of [dashboards](/dashboards/logs/log-dashboards). They transform your log data into charts, graphs, and other visual formats to identify trends, track metrics, and analyze patterns. You can create visualizations directly from the **Visualize** section, or create them while building a dashboard by selecting **Create new** when adding a panel. :::tip Try it Live Explore working examples in the [Oodle Playground](https://play.oodle.ai/logs/app/visualize) to see visualizations in action. ::: ## Getting Started To create a new visualization: 1. Navigate to the **Visualize** section from the sidebar 2. Click **Create visualization** 3. Select a visualization type 4. Choose your index pattern (your log data source) 5. Configure metrics and buckets 6. Save your visualization ## Core Concepts ### Metrics Metrics determine **what** you're measuring. Common metric types include: | Metric | Description | Example Use Case | |--------|-------------|------------------| | **Count** | Number of documents matching the query | Total logs per minute | | **Sum** | Total of a numeric field | Total bytes transferred | | **Average** | Mean value of a numeric field | Average response time | | **Min/Max** | Minimum or maximum value | Peak memory usage | | **Unique Count** | Distinct values of a field | Number of unique users | ### Buckets Buckets determine **how** your data is grouped. Common bucket types include: | Bucket | Description | Example Use Case | |--------|-------------|------------------| | **Date Histogram** | Groups data by time intervals | Logs per hour | | **Terms** | Groups by field values | Logs per service | | **Filters** | Groups by custom queries | Error vs Success logs | | **Range** | Groups numeric values into ranges | Response time buckets | | **Histogram** | Groups by numeric intervals | Request size distribution | ## Visualization Editor The visualization editor lets you build charts by configuring filters, time ranges, and aggregations. ![Visualization Editor](/img/osd-visualization-explanation.png) | # | Component | Description | |---|-----------|-------------| | 1 | **Filter** | Filter logs to include only data relevant to your visualization. Add multiple filters to narrow down results. | | 2 | **Time Range** | Set the time window for your data. Use quick selections like "Last 1 day" or define custom ranges. | | 3 | **Metrics (Y-Axis)** | Choose the aggregation for your Y-axis values. Options include Count, Sum, Average, Min, Max, and more. Scroll to see all available aggregations. | | 4 | **Buckets (X-Axis)** | Define how data is grouped on the X-axis. Use Date Histogram for time-based charts or Terms for categorical grouping. | | 5 | **Split Series** | Add a sub-aggregation to break down data into multiple series. Select Terms to split by field values. | | 6 | **Sub-aggregation Field** | Choose the field to split your series by (e.g., `container_name`, `log_level`, `service`). | ## Visualization Types ### Line Chart Line charts display data points connected over time, ideal for tracking trends and identifying patterns in time-series data. ![Line Chart showing Successful Transactions](/img/osd-line-chart.png) **Best for:** - Tracking metrics over time (transactions, requests, errors) - Identifying trends and anomalies - Comparing before/after changes **Configuration:** 1. **Metric**: Choose Count (or Sum/Average for numeric fields) 2. **X-Axis**: Add a Date Histogram bucket on your timestamp field 3. **Split Series** (optional): Add a Terms or Filters bucket to compare multiple series **Example: Successful Transactions Over Time** - Metric: Count - X-Axis: Date Histogram on `timestamp` with auto interval - Split Series: Filters with query `message:Transaction complete.` → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/4ef9c1a0-edc0-11f0-b051-df37cf89c1fe) --- ### Bar Chart Bar charts compare categorical data across groups or time periods. Stacked bars show the composition of each group. ![Stacked Bar Chart showing Cart Operations](/img/osd-barchart.png) **Best for:** - Comparing volumes across categories - Showing composition (stacked bars) - Analyzing distribution by time with breakdowns **Configuration:** 1. **Metric**: Count or Sum 2. **X-Axis**: Date Histogram for time-series, or Terms for categories 3. **Split Series**: Terms bucket on a field (e.g., `container_name`, `log_level`) to stack by category **Example: Cart Operations Over Time** - Metric: Count - X-Axis: Date Histogram on `timestamp` (5-minute intervals) - Split Series: Terms on `fingerprint.keyword` to see operation types → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/900d7c27-e8f8-4d95-9b47-a1773733a924) --- ### Area Chart Area charts show magnitude over time with filled regions. Stacked area charts display how parts contribute to a whole. ![Stacked Area Chart showing Log Count by Container](/img/osd-area-chart.png) **Best for:** - Visualizing volume trends over time - Showing proportional contributions from different sources - Highlighting sudden changes in data volume **Configuration:** 1. **Metric**: Count 2. **X-Axis**: Date Histogram on `timestamp` 3. **Split Series**: Terms bucket on a categorical field 4. In **Metrics & axes**, set Chart type to "Area" and enable "Stacked" **Example: Log Volume by Container** - Metric: Count - X-Axis: Date Histogram on `timestamp` (5-minute intervals) - Split Series: Terms on `container_name.keyword` → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/4dbd9c50-edb9-11f0-b051-df37cf89c1fe) --- ### Pie Chart Pie and donut charts show proportional distribution of data across categories. ![Pie Chart showing Transactions by Loyalty Level](/img/osd-pie-chart.png) **Best for:** - Showing relative proportions - Distribution across a small number of categories (≤10) - Understanding composition at a glance **Configuration:** 1. **Metric**: Count (or Sum for numeric values) 2. **Split Slices**: Terms bucket on the field to segment by 3. Optionally enable "Donut" mode in Options **Example: Transactions by Customer Loyalty Level** - Metric: Count - Split Slices: Filters bucket with queries for each level: - `message:Platinum` → label "Platinum" - `message:Gold` → label "Gold" - `message:Silver` → label "Silver" - `message:Bronze` → label "Bronze" → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/23a5fa40-edbc-11f0-b901-edc57b66473a) --- ### Metric Metric visualizations display a single prominent number, ideal for KPIs and summary statistics. ![Metric showing Total Error Count by Service](/img/osd-metric-type.png) **Best for:** - Displaying key performance indicators - Showing totals or averages at a glance - Dashboard summary panels **Configuration:** 1. **Metric**: Count, Sum, Average, or other aggregation 2. **Split Group** (optional): Terms bucket to show multiple metrics side by side **Example: Error Count by Service** - Metric: Count - Query filter: `log_level:error OR level:ERROR` - Split Group: Terms on `container_name.keyword` (top 3-5) → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/8c3e6f40-edb9-11f0-b051-df37cf89c1fe) --- ### Data Table Data tables display aggregated data in tabular format, perfect for detailed breakdowns and exports. ![Data Table showing Top Log Fingerprints](/img/osd-data-table.png) **Best for:** - Detailed data exploration - Exporting aggregated data - Showing exact counts and values **Configuration:** 1. **Metric**: Count (and additional metrics as needed) 2. **Split Rows**: Terms bucket on the field to list 3. Configure sort order (descending for "top N" lists) **Example: Top Log Fingerprints** - Metric: Count - Split Rows: Terms on `fingerprint.keyword` with size 10 - Sort by Count descending → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/7b780660-2b10-11f0-8d5a-b38158070b23) --- ### Heat Map Heat maps use color intensity to represent value magnitude across two dimensions, perfect for spotting patterns across time and categories. ![Heat Map showing Log Activity by Container and Hour](/img/osd-head-map.png) **Best for:** - Identifying activity patterns across time and categories - Spotting outliers in multi-dimensional data - Understanding when and where activity occurs **Configuration:** 1. **Metric**: Count (determines color intensity) 2. **X-Axis**: Date Histogram on `timestamp` 3. **Y-Axis**: Terms bucket on the categorical field **Example: Log Activity by Container Over Time** - Metric: Count - X-Axis: Date Histogram on `timestamp` (1-hour intervals) - Y-Axis: Terms on `container_name.keyword` → [View example in Playground](https://play.oodle.ai/logs/app/visualize#/edit/0d169890-edba-11f0-b051-df37cf89c1fe) --- ## Filtering Data You can filter visualization data in several ways: ### Query Bar Enter Lucene or KQL queries to filter documents: ``` log_level:error AND container_name:frontend ``` ### Filters Aggregation Use Filters as a bucket type to create labeled segments based on queries: ``` # Filter 1 Query: message:success Label: Successful # Filter 2 Query: message:failed OR message:error Label: Failed ``` ### Time Filter Use the time picker in the top navigation to adjust the time range for all visualizations. ## Saving Visualizations 1. Click **Save** in the top menu 2. Enter a descriptive title 3. Optionally add to an existing dashboard 4. Click **Save** Saved visualizations can be: - Added to multiple dashboards - Shared with team members - Used as a template for similar visualizations ## Next Steps - Combine your visualizations into [dashboards](/dashboards/logs/log-dashboards) for comprehensive monitoring views - Explore the [logs interface](/logs-visualization) for ad-hoc log analysis --- ## Create a Dashboard This guide walks you through creating a new dashboard in Oodle. ## Create a New Dashboard 1. Click **Dashboards** in the left sidebar. 2. Click the **New** button near the top right of the page. 3. Choose **New dashboard** from the dropdown menu. Now you should have a new blank dashboard. ## Add a New Panel 1. Click **Add visualization** on the empty dashboard screen to add a new panel. 2. Click **oodle** on the "Select data source" dialog. ## Configure the Panel Panels come with extensive configuration options. Let's take a look at the configuration options for [Cluster CPU Utilization panel from Oodle Playground]( https://play.oodle.ai/grafana/d/k8s_views_global/kubernetes-views-global?editPanel=72): 1. See how the panel will look like on the dashboard in the **visualization preview**. 2. Choose the visualization type from the **visualization dropdown**. 3. Write **PromQL query** to fetch the data for the panel, 4. Or use the **query builder** UI to build the query. 5. **Customize the panel** both in appearance and functionality using the provided options. --- ## Dashboard Variables Dashboard variables allow you to create interactive, dynamic dashboards. They can be used in queries and panel titles to create dashboards that can be easily modified to show different data. ## Create a Variable To add a variable to your dashboard: 1. Click the **Edit** button in the top-right corner of the dashboard. 2. Click the **Settings** button that just appeared. 3. The **Settings** page shows up. 1. Choose the **Variables** tab. 2. This page has link to the detailed documentation for variables if needed. 3. Click **Add variable** button. In the next section we'll take a deeper look at some of the most useful and commonly used variable types. ## Types of Variables Oodle supports all the variable types [supported by Grafana]( https://grafana.com/docs/grafana/latest/dashboards/variables/). Among those, two most useful and commonly used are **Query**, and **Ad hoc filters**. While the [Grafana docs]( https://grafana.com/docs/grafana/latest/dashboards/variables/ ) cover the technical details, here we'll focus on how to configure them, and how to use them in dashboards. ### Query Variable Query variables are variables that show up as a dropdown in the dashboard, where the values shown in the dropdown are determined by running a PromQL query. Let's look at the `namespace` variable in the [Kubernetes / Views / Namespaces]( https://play.oodle.ai/grafana/d/k8s_views_ns/kubernetes-views-namespaces ) dashboard on Oodle Playground. #### Appearance This variable appears as a dropdown in the dashboard, where you can select the kubernetes namespace. #### Configuration Here's how the [configuration page for `namespace`]( https://play.oodle.ai/grafana/d/k8s_views_ns/kubernetes-views-namespaces?editview=variables&editIndex=2 ) looks like: 1. **Name** specifies the name that will be used in the dashboard to reference the variable. 2. **Query** is used to fetch the variable values, which will be displayed in the dropdown. In this example, it means: 1. Query the metric `kube_pod_info` 2. Only consider the timeseries where `cluster` label equals the value of `cluster` variable. Note how configuration of `namespace` variable makes use of another variable, `cluster`. Also note how `$cluster` is used to reference the `cluster` variable. 3. From the resulting timeseries, take the value of `namespace` label. 4. Take the distinct set of values from the `namespace` label, and populate the dropdown. 3. **Multi-value** allows to select multiple values from the dropdown. 4. **Include All option** allows to include the special "All" value in the dropdown. 5. **Preview of values** shows all the values that will be present in the dropdown. #### Usage The [Kubernetes Resource Count panel]( https://play.oodle.ai/grafana/d/k8s_views_ns/kubernetes-views-namespaces?editPanel=32 ) uses the `namespace` variable by adding `$namespace` appropriately in all the queries of the panel that need to filter by namespace: ### Ad-hoc Filters #### Appearance Ad-hoc filters show up as double-dropdowns in the dashboard, where you can select: 1. The PromQL label to filter by 2. The value of the label to filter by #### Configuration Ad-hoc filters are configured by choosing **Ad-hoc filters** in "Select variable type" dropdown. #### Usage Unlike query variables, which need to be referenced in the query using `$variable_name`, ad-hoc filters don't need to be referenced in the query at all. Instead, they are automatically applied to all the queries in the dashboard. --- ## Terraform for dashboards Configure all your Grafana dashboards and folders through terraform. ## Advantages of managing dashboards as code * They are version-controlled, making changes and rollbacks more transparent and trackable. * This approach allows you to standardize dashboard configurations across environments, eliminating manual errors and drift between staging and production. You can apply the same dashboard configuration across all your deployments / environments. * Terraform's declarative nature also makes it easy to spin up or tear down dashboards alongside your infrastructure, ensuring your monitoring visualizations evolve with your stack. * Integrating dashboard management into your CI/CD pipeline means faster, more reliable deployments, as your dashboards will always align with the infrastructure they monitor. ## How :::info Use oodle's [Terraform Provider]( https://registry.terraform.io/providers/oodle-ai/oodle/latest/docs/resources/grafana_dashboard) ::: ### Prerequisites To setup Oodle Dashboards via Terraform files, you'll need the following values: - `OODLE_INSTANCE`: Your Oodle instance ID. Go to `Settings` icon -> `API Keys` page in your Oodle UI to find out. (Oodle UI links: ) - `OODLE_API_KEY`: Your Oodle API key for authentication. Go to `Settings` icon -> `API Keys` in your Oodle UI to choose an appropriate key. (Oodle UI links: ) ### Example usage ```hcl terraform { required_providers { oodle = { source = "registry.terraform.io/oodle-ai/oodle" } } } # These can also be set as environment variables: # export OODLE_DEPLOYMENT=https://us1.oodle.ai/ # export OODLE_INSTANCE="my-instance" # export OODLE_API_KEY="my-api-key" provider "oodle" { deployment_url = "https://us1.oodle.ai/" instance = "my-instance" api_key = "my-api-key" } # Example of creating a Grafana folder and dashboard together # The dashboard references the folder's UID resource "oodle_grafana_folder" "my_folder" { title = "Test Folder" } resource "oodle_grafana_dashboard" "test_folder" { folder = oodle_grafana_folder.my_folder.uid config_json = jsonencode({ "title" : "My Dashboard Title", "uid" : "my-dashboard-uid", "schemaVersion" : 39, "timezone" : "browser", "panels" : [ { "id" : 1, "type" : "stat", "title" : "Request Rate", "gridPos" : { "h" : 8, "w" : 12, "x" : 0, "y" : 0 }, "targets" : [ { "expr" : "sum(rate(http_requests_total[5m]))", "refId" : "A" } ] }, { "id" : 2, "type" : "timeseries", "title" : "Error Rate", "gridPos" : { "h" : 8, "w" : 12, "x" : 12, "y" : 0 }, "targets" : [ { "expr" : "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m]))", "refId" : "A" } ] } ] }) } # Example of a dashboard with a commit message for version history resource "oodle_grafana_dashboard" "service_dashboard" { folder = oodle_grafana_folder.my_folder.uid message = "Initial dashboard creation" overwrite = true config_json = jsonencode({ "title" : "Service Health", "uid" : "service-health-dashboard", "schemaVersion" : 39, "time" : { "from" : "now-6h", "to" : "now" }, "panels" : [ { "id" : 1, "type" : "gauge", "title" : "Uptime", "gridPos" : { "h" : 8, "w" : 8, "x" : 0, "y" : 0 }, "targets" : [ { "expr" : "avg(up{job=\"my-service\"})", "refId" : "A" } ] } ] }) } # Example without a folder (created at root level) resource "oodle_grafana_dashboard" "root_dashboard" { config_json = jsonencode({ "title" : "Root Level Dashboard", "uid" : "root-level-dashboard", "schemaVersion" : 39, "panels" : [] }) } ``` Apply terraform configuration ```bash terraform init terraform apply ``` --- ## Metrics Dashboards Oodle dashboards are a way to query, visualize, and organize metrics to gain deeper insights into your data at a glance. ## Overview Let's take a look at the [Kubernetes Overview dashboard on Oodle Playground]( https://play.oodle.ai/grafana/d/k8s_views_global/kubernetes-views-global): 1. **Dashboards button** on the left sidebar provides quick access to Oodle dashboards. 2. **Dashboard path** shows your current location in the dashboard hierarchy, including folder structure and dashboard name. You can navigate back through folders using this breadcrumb trail. 3. **Variables** allow you to filter and refine your view. 4. **Time controls** let you set the time range for dashboard data. 5. **Visualization panels** show the data using a variety of visualizations. ## Explore Further export const FilteredDocCardList = () => { const category = useCurrentSidebarCategory(); const filteredItems = category.items.filter( item => item.label !== 'Metrics Dashboards' ); return ; }; --- ## Database Hosts The Hosts tab provides a host-level view of your monitored database instances, showing key performance and resource utilization metrics. ## Hosts Table | Column | Description | |--------|-------------| | **Database Host** | Instance hostname or endpoint | | **Queries/s** | Average queries per second | | **Total Duration** | Cumulative query execution time | | **Avg Duration** | Mean query execution time | | **Max Duration** | Longest single query execution | | **Rows/s** | Rows processed per second | | **Avg CPU Utilization** | Average CPU usage percentage | | **Max CPU Utilization** | Peak CPU usage percentage | ## Filters The left-hand panel provides: - **Database Type** — Filter by engine (PostgreSQL, MySQL). Each type is shown with its logo icon. - **Version** — Filter by database version. - **Database Identifier** — Search by DB instance identifier. ## Click to Drill Down Click a host row to open a detail drawer with: - **Overview metrics** — CPU utilization, active connections, queries per second, and replication lag over time. - **Databases table** — Lists each database on the host with its size, active connections, commits, rollbacks, and conflict count. - **Replication metrics** — Write lag, flush lag, replay lag, and replication slot delay (when applicable). ## Use Cases - **Capacity planning** — Identify hosts approaching CPU or connection limits. - **Performance debugging** — Correlate host-level resource spikes with query duration increases. - **Fleet management** — Compare performance across database instances to find outliers. ## Related Pages - [Database Queries](/database-monitoring/queries) — Aggregated query performance metrics. - [Database Samples](/database-monitoring/samples) — View individual query execution events. --- ## Database Monitoring Oodle Database Monitoring gives you deep visibility into your database performance. Track query execution times, identify slow queries, monitor host resource utilization, and analyze query samples — all from a single interface. ## Getting Started Navigate to **Database Monitoring** in the sidebar. The default view opens on the **Queries** tab, which shows an aggregated view of all database queries. ## Navigation Database Monitoring is organized into three tabs: | Tab | Description | |-----|-------------| | **[Queries](/database-monitoring/queries)** | Aggregated query performance metrics | | **[Samples](/database-monitoring/samples)** | Individual query execution samples | | **[Hosts](/database-monitoring/hosts)** | Database host health and resource usage | ## Filters Each tab provides a filter sidebar on the left. The **Queries** and **Samples** tabs share these filters: - **Host** — Filter by database host. - **Database** — Filter by database name. - **User** — Filter by the database user that executed the query. - **Tables** — Filter by referenced table names. - **Commands** — Filter by SQL command type (e.g., SELECT, INSERT). The **Hosts** tab has its own filters: - **Database Type** — Filter by engine (PostgreSQL, MySQL). - **Version** — Filter by database version. - **Database Identifier** — Search by DB instance identifier. All tabs include a **time picker** to adjust the observation window. ## Supported Databases Oodle Database Monitoring supports: - **PostgreSQL** (including Aurora PostgreSQL) - **MySQL** (including Aurora MySQL) ## Best Practices - **Start with the Queries tab** to identify the most expensive queries by total duration. - **Drill into Samples** when you need to see individual execution plans or parameter values. - **Monitor Hosts** to detect resource bottlenecks (CPU, connections) that affect query performance. - **Set alerts** on key database metrics (query duration, error rate) to catch regressions early. ## Related Pages - [Kubernetes](/kubernetes/) — Monitor Kubernetes clusters, workloads, and pods. - [Serverless](/serverless/) — Monitor AWS Lambda functions. --- ## Database Queries The Queries tab shows aggregated performance metrics for every normalized database query. Use it to identify slow queries, high- volume queries, and queries that consume the most total database time. ## Overview Chart At the top of the page, a time-series chart visualizes the selected metric over time. Use the dropdown to switch between: - **Total Duration** — Cumulative execution time across all executions. - **Avg Duration** — Mean execution time per query call. - **Count** — Number of executions. ## Queries Table Below the chart, the table lists every normalized query: | Column | Description | |--------|-------------| | **Query** | Normalized SQL statement (parameters replaced with placeholders) | | **Count** | Number of times the query ran in the selected window | | **Total Duration** | Cumulative execution time with a visual bar | | **Avg Duration** | Average execution time | | **Avg Rows** | Average number of rows returned or affected | | **Cache Hit Ratio** | Percentage of executions served from cache | | **% Duration** | Share of total database time consumed by this query | ### Sorting Click any column header to sort. Sorting by **Total Duration** (descending) is the most effective way to find the queries that matter most. ### Search Use the search bar to filter queries by SQL text. ### Column Selector Click the column-selector button in the toolbar to toggle additional columns on or off. Hidden by default are block-level I/O columns such as Avg Shared Blocks Hit/Read, Avg Local Blocks Hit/Read, and Avg Temp Blocks Read/Written. These are useful for diagnosing I/O-heavy queries. ### Show Full Query Toggle **Show Full Query** in the toolbar to expand truncated SQL statements in the table so you can read the complete query text without clicking into the detail drawer. ## Filters The left-hand panel provides: - **Host** — Filter by database host. - **Database** — Filter by database name. - **User** — Filter by the database user that executed the query. - **Tables** — Filter by referenced table names. - **Commands** — Filter by SQL command type (e.g., SELECT, INSERT). ## Click to Drill Down Click any query row to see: - A detailed execution timeline. - Per-host breakdown of execution statistics. - Link to related [Samples](/database-monitoring/samples) for that query. ## Related Pages - [Database Samples](/database-monitoring/samples) — View individual query execution events. - [Database Hosts](/database-monitoring/hosts) — Monitor host-level performance. --- ## Database Samples The Samples tab displays individual query execution events captured from your databases. Unlike the aggregated Queries view, Samples shows each execution with its exact timestamp, full SQL statement, and the database instance it ran on. ## Samples Table | Column | Description | |--------|-------------| | **Time** | Exact timestamp of the execution. | | **Statement** | Normalized SQL statement (displayed in monospace font). | | **Database Instance** | The host the query ran on. | | **User** | The database user that executed the query. | | **Wait Event Group** | The wait-event category — color-coded badges for CPU, IO, Network, and Lock. | | **Database** | The database name. | | **Duration** | Execution duration, color-coded by severity. | | **Application** | The client application name (if reported). | | **State** | Current state of the query (e.g., active, idle). | :::tip The table shows the latest 1,000 samples. If the limit is reached, a note appears above the table. Narrow the time range to load more samples. ::: ## Filters The left-hand panel provides: - **Host** — Filter by database host. - **Database** — Filter by database name. - **User** — Filter by the database user. - **Tables** — Filter by referenced table names. - **Commands** — Filter by SQL command type (e.g., SELECT, INSERT). ### Search Use the search bar to filter samples by SQL text, host, database, or user. ## Click to Drill Down Click any sample row to open a detail drawer showing the full query text, execution plan (when available), and contextual details like client application name and user. ## Use Cases - **Debug slow queries** — Find specific executions that were abnormally slow and inspect their details. - **Audit activity** — Review which queries ran at a specific time against a specific database. - **Investigate deadlocks** — Examine the exact statements involved in a deadlock event. - **Validate deployments** — After a schema migration, verify that new query patterns appear as expected. ## Related Pages - [Database Queries](/database-monitoring/queries) — Aggregated query performance metrics. - [Database Hosts](/database-monitoring/hosts) — Monitor host-level performance. --- ## Amazon ECS Oodle provides a seamless integration with [Elastic Container Service](https://aws.amazon.com/ecs/) and [AWS Fargate](https://aws.amazon.com/fargate/) to ingest **metrics, logs, and traces** from your ECS cluster. The quickest path for all three signals is the **Datadog Agent**, which ships metrics, logs, and traces together; see [Datadog Agent](#datadog-agent) below. If you only need logs, the [log routers](#configuration) (FireLens, OpenTelemetry, CloudWatch) cover that on their own. ## Datadog Agent If the [Datadog Agent](https://docs.datadoghq.com/integrations/ecs_fargate/) runs in your ECS tasks (injected as a sidecar on **Fargate**, or as a daemon task on **EC2**), point it at Oodle by adding environment variables to the **Agent container**, with no application changes required. This carries **metrics, logs, and traces** together, unlike the log-only routers below. **Dual-write** (Datadog + Oodle) adds Oodle as additional endpoints; keep your existing `DD_API_KEY` / `DD_SITE`: ```json "environment": [ { "name": "DD_ADDITIONAL_ENDPOINTS", "value": "{\"\": [\"\"]}" }, { "name": "DD_USE_V3_API_SERIES_ENABLED", "value": "false" }, { "name": "DD_LOGS_CONFIG_ADDITIONAL_ENDPOINTS", "value": "[{\"api_key\": \"\", \"Host\": \"\", \"Port\": 443, \"is_reliable\": false}]" }, { "name": "DD_LOGS_CONFIG_FORCE_USE_HTTP", "value": "true" }, { "name": "DD_APM_ADDITIONAL_ENDPOINTS", "value": "{\"\": [\"\"]}" } ] ``` **Single-write** (Oodle only) overrides the Agent's primary endpoints and authenticates with your Oodle API key: ```json "environment": [ { "name": "DD_API_KEY", "value": "" }, { "name": "DD_DD_URL", "value": "" }, { "name": "DD_USE_V3_API_SERIES_ENABLED", "value": "false" }, { "name": "DD_APM_DD_URL", "value": "" }, { "name": "DD_LOGS_CONFIG_LOGS_DD_URL", "value": ":443" }, { "name": "DD_LOGS_CONFIG_FORCE_USE_HTTP", "value": "true" } ] ``` :::note On the **EC2** launch type, also set `DD_ORCHESTRATOR_EXPLORER_ENABLED=false`. Orchestrator Explorer ships to Datadog's site-based process intake, which `DD_DD_URL` cannot redirect to Oodle. ::: For the full reference (Helm/Operator/YAML elsewhere, plus a runnable `terraform-aws-ecs-datadog` example for Fargate and EC2), see the [Datadog on ECS guide](/integrations/metrics/datadog#amazon-ecs). ## Configuration To configure Oodle to ingest logs from your ECS cluster, you'll need the following values: - `OODLE_INSTANCE`: Your Oodle instance ID. Go to `Settings` icon -> `API Keys` page in your Oodle UI to find out. (Oodle UI links: ) - `OODLE_API_KEY`: Your Oodle API key for authentication. Go to `Settings` icon -> `API Keys` in your Oodle UI to choose an appropriate key. (Oodle UI links: ) - `OODLE_LOGS_HOST`: The Oodle Host for log ingestion. Go to `Settings` -> `Logs` -> `Fluent Bit` in your Oodle UI to find out. - `OODLE_METRICS_HOST`: The Oodle Host for metrics ingestion. Go to `Settings` -> `Metrics` -> `Prometheus` in your Oodle UI to find out. ### Using AWS FireLens If you are using [awslogs](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html) log driver to send logs to CloudWatch, [AWS FireLens](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html) is recommended for routing logs to external destinations. Oodle uses Fluent Bit with FireLens for routing logs. #### Customer-Hosted Fluent Bit Configs It is recommended to host Fluent Bit configs in the customer's S3 buckets as that allows maximum flexibility in terms of being able to version-control the configs, and modifying the config for specific needs. It also simplifies ECS task definitions as config files can be directly pulled from customer's S3 bucket. 1. Store your fluent bit config in a S3 bucket. Following is an example config which configures dual-write of logs to CloudWatch and Oodle, and also send Fluent Bit internal metrics to Oodle. It also adds following filters: - [multiline](https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/filter-multiline-partial-message-mode) to concatenate partial/split container logs due to container runtime split. ```conf [INPUT] name fluentbit_metrics tag internal_metrics scrape_interval 2 [FILTER] name multiline match * multiline.key_content log mode partial_message [OUTPUT] Name cloudwatch_logs Match * region ${AWS_REGION} log_group_name ${CLOUDWATCH_LOG_GROUP} log_stream_name ${CLOUDWATCH_LOG_STREAM}/${ECS_TASK_ID} auto_create_group true Retry_Limit 3 [OUTPUT] Name http Match * Host ${OODLE_LOGS_HOST} Port 443 URI /ingest/v1/logs Header X-OODLE-INSTANCE ${OODLE_INSTANCE} Header X-API-KEY ${OODLE_API_KEY} Format json Compress gzip Json_date_key timestamp Json_date_format iso8601 Tls On Retry_Limit 3 [OUTPUT] Name prometheus_remote_write Match internal_metrics Host ${OODLE_METRICS_HOST} Port 443 Uri /v1/prometheus/${OODLE_INSTANCE}/write Header X-API-KEY ${OODLE_API_KEY} Log_response_payload True Tls On # These environment variables are added by aws-for-fluent-bit init container add_label ecs_cluster ${ECS_CLUSTER} add_label ecs_task_arn ${ECS_TASK_ARN} add_label ecs_task_definition ${ECS_TASK_DEFINITION} add_label ecs_task_id ${ECS_TASK_ID} Retry_Limit 3 ``` :::note Depending on your needs, you can update the fluent bit config, e.g. to only send logs to Oodle, you can remove `cloudwatch_logs` Output section in the config. ::: 2. In the ECS task definition, update the value of `logDriver` from `awslogs` to `awsfirelens` for the application container. ```json { "logConfiguration": { "logDriver": "awsfirelens" } } ``` 3. In the ECS task definition, add a Fluent Bit sidecar container to handle log routing. Provide relevant environment variables referenced in the fluent bit config, and provide fluent bit config via `aws_fluent_bit_init_s3_1` environment variable. ```json { "name": "fluent-bit", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.32.5.20250305", "essential": false, "memory": 200, "environment": [ { "name": "OODLE_INSTANCE", "value": "" }, { "name": "OODLE_API_KEY", "value": "" }, { "name": "OODLE_LOGS_HOST", "value": "" }, { "name": "OODLE_METRICS_HOST", "value": "" }, { "name": "CLOUDWATCH_LOG_GROUP", "value": "" }, { "name": "CLOUDWATCH_LOG_STREAM", "value": "" }, { "name": "aws_fluent_bit_init_s3_1", "value": "/fluent-bit.conf>" } ], "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } } } ``` 4. Ensure your ECS [task role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) has permissions to download from S3 bucket. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetBucketLocation" ], "Resource": "*" } ] } ``` :::note If you want to restrict the permissions to the specific bucket where your Fluent Bit configs are hosted, then you can update `Resource` to `arn:aws:s3:::/*` ::: 5. If you are writing logs from Fluent Bit to CloudWatch as well, then ensure your ECS task role has relevant CloudWatch permissions as well. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:::log-group::*" } ] } ``` #### Oodle-Hosted Fluent Bit Configs If you prefer to use Oodle-Hosted Fluent Bit configs, then you can follow steps in this section. ##### Send to CloudWatch and Oodle In this configuration, ECS application container logs are sent to both CloudWatch and Oodle. Sidecar containers logs are sent to CloudWatch. 1. In the application container definition, update the value of `logDriver` from `awslogs` to `awsfirelens`. ```json { "logConfiguration": { "logDriver": "awsfirelens" } } ``` 2. Add an ephemeral volume in the ECS task definition. This volume will be used to store Fluent Bit configuration file as configured in Step #3. ```json "volumes": [ { "name": "config", "host": {} } ] ``` 3. Add two containers to the ECS task definition: - An init container to download Fluent Bit configuration file - A Fluent Bit sidecar container to handle log routing to both CloudWatch and Oodle. ```json { "name": "config-init", "image": "public.ecr.aws/docker/library/alpine:3.21.3", "essential": false, "memoryReservation": 64, "command": [ "sh", "-c", "set -e && apk add --no-cache ca-certificates wget && wget -O /oodle/fluent-bit.conf https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-cloudwatch-v3.conf || exit 1" ], "mountPoints": [ { "sourceVolume": "config", "containerPath": "/oodle", "readOnly": false } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } } }, { "name": "fluent-bit", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.32.5.20250305", "essential": false, "memory": 200, "environment": [ { "name": "OODLE_INSTANCE", "value": "" }, { "name": "OODLE_API_KEY", "value": "" }, { "name": "OODLE_LOGS_HOST", "value": "" }, { "name": "OODLE_METRICS_HOST", "value": "" }, { "name": "CLOUDWATCH_LOG_GROUP", "value": "" }, { "name": "CLOUDWATCH_LOG_STREAM", "value": "" } ], "mountPoints": [ { "sourceVolume": "config", "containerPath": "/oodle", "readOnly": true } ], "dependsOn": [ { "containerName": "config-init", "condition": "COMPLETE" } ], "firelensConfiguration": { "type": "fluentbit", "options": { "config-file-type": "file", "config-file-value": "/oodle/fluent-bit.conf" } }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } } } ``` 4. Ensure your ECS [task role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)) has relevant CloudWatch permissions. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:::log-group::*" } ] } ``` ##### Send to Oodle Only In this configuration, ECS application container logs are sent to Oodle only. Sidecar containers logs are sent to CloudWatch. 1. Configure the application container and volume (Steps 1-2 above). 2. Update the `config-init` container definition. ```json { "name": "config-init", "image": "public.ecr.aws/docker/library/alpine:3.21.3", "essential": false, "memoryReservation": 64, "command": [ "sh", "-c", "set -e && apk add --no-cache ca-certificates wget && wget -O /oodle/fluent-bit.conf https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-v3.conf || exit 1" ], "mountPoints": [ { "sourceVolume": "config", "containerPath": "/oodle", "readOnly": false } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } } } ``` The `config-init` container downloads a simplified configuration file from [fluent-bit-v3.conf](https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-v3.conf) that routes logs exclusively to Oodle. In addition, environment variables `CLOUDWATCH_LOG_GROUP` and `CLOUDWATCH_LOG_STREAM` can be removed from the `fluent-bit` container. #### Optional Configurations This section documents configurations which can be used if you have the specified use-cases. ##### JSON Logs If your applications emit logs in json format, then you can add a fluent parser to parse the json logs before routing them to destinations. It makes use of [multi-config-support](https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/multi-config-support) and [parse-json](https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/parse-json) capability in AWS for Fluent Bit image. To add `parse-json` parser in your `fluent-bit` configuration, you can add following environment variable to `fluent-bit` container: ```json { "name": "aws_fluent_bit_init_file_1", "value": "/fluent-bit/configs/parse-json.conf" } ``` The default parser used in AWS for Fluent Bit image uses `Time_Format %d/%b/%Y:%H:%M:%S %z` to parse the timestamp from the logs. If your application logs have a different timestamp format, you can override the json parser. As an example, if your timestamp format is ISO8601 (`%Y-%m-%dT%H:%M:%S.%LZ`), then you can follow below steps: 1. Update `config-init` container command to download custom json parser related configuration files, add following `wget` commands in the command used in `config-init` container: ``` wget -O /oodle/parser.conf https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-parser-v1.conf && wget -O /oodle/parse-json.conf https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-filter-parse-json-iso8601-v1.conf ``` 2. Add following environment variables in `fluent-bit` container: ``` { "name": "aws_fluent_bit_init_file_1", "value": "/oodle/parser.conf" }, { "name": "aws_fluent_bit_init_file_2", "value": "/oodle/parse-json.conf" } ``` :::note Parser definitions needs to be provided as separate file and cannot be merged with Filter definitions in a single file. You can choose to store these config files in your S3 bucket as well, and use `aws_fluent_bit_init_s3_` environment variables to refer to them. ::: ##### Concatenate Partial/Split Container Logs Container runtime splits log lines larger than 16KB into multiple log lines. Fluent Bit can concatenate these split log lines using `multiline` filter. To use it, add following filter in your fluent bit configuration: ```conf [FILTER] name multiline match * multiline.key_content log mode partial_message ``` :::note This needs to be first filter in the pipeline so that all remaining filters operate on concatenated log lines. ::: ##### Enrich log lines with additional key-value pair Say, you want to ingest your dev and prod cluster logs to Oodle. You can enrich each log line with a `environment: dev` or `environment: prod` field by making following changes: 1. Update `config-init` container command to download custom modify filter configuration file, add following `wget` command in the command used in `config-init` container: ``` wget -O /oodle/modify-add-field.conf https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/fluent-bit/fluent-bit-modify-add-field-v1.conf ``` 2. Add following environment variables in `fluent-bit` container: ``` { "name": "FIELD_NAME", "value": "environment" }, { "name": "FIELD_VALUE", "value": "dev" }, { "name": "aws_fluent_bit_init_file_1", "value": "/oodle/modify-add-field.conf" }, ``` :::note The numbering in `aws_fluent_bit_init_file_*` can be used to add multiple init files, e.g. if you need both json parsing and enrich field, then you can add all files needed with increasing counter. ::: ### Using OpenTelemetry Collector Users can run [OpenTelemetry (OTel) Collector contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib) image to send ECS logs to Oodle. :::note 1. [AWS Distro for OpenTelemetry](https://aws-otel.github.io) image is not supported as it doesn't contain the [Fluent Forward Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/fluentforwardreceiver) required by this configuration. 1. [Fluent Forward Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/fluentforwardreceiver) is currenlty in **beta** status. ::: #### Send to CloudWatch and Oodle In this configuration, ECS application container logs are sent to both CloudWatch and Oodle. Sidecar containers logs are sent to CloudWatch. 1. In the application container definition, update the value of `logDriver` from `awslogs` to `awsfirelens`. ```json { "logConfiguration": { "logDriver": "awsfirelens" } } ``` Fluent Forward Receiver in OTel collector is used in this configuration. Therefore, the value of `logDriver` is set to `awsfirelens` and the `firelensConfiguration` type in next step is set to `fluentbit`. 2. Add an OTel collector sidecar container in the ECS task definiton. ```json { "name": "otel-collector", "image": "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.121.0", "essential": false, "memory": 200, "environment": [ { "name": "OODLE_INSTANCE", "value": "" }, { "name": "OODLE_API_KEY", "value": "" }, { "name": "OODLE_LOGS_HOST", "value": "" }, { "name": "CLOUDWATCH_LOG_GROUP", "value": "" }, { "name": "CLOUDWATCH_LOG_STREAM", "value": "" } ], "command": [ "--config", "https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/otel/otel-config-cloudwatch-v2.yaml" ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } }, "firelensConfiguration": { "type": "fluentbit" } } ``` 3. Ensure your ECS [task role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)) has relevant CloudWatch permissions. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:::log-group::*" } ] } ``` OTeL collector downloads an [otel-config-cloudwatch-v2.yaml](https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/otel/otel-config-cloudwatch-v2.yaml) from a public S3 repository owned by Oodle. The config files in this repository are versioned and immutable. #### Send to Oodle Only In this configuration, ECS application container logs are sent to Oodle only. Sidecar containers logs are sent to CloudWatch. 1. Configure the application container (Step 1 above). 2. Update the `otel-collector` container definition. ```json { "name": "otel-collector", "image": "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.121.0", "essential": false, "memory": 200, "environment": [ { "name": "OODLE_INSTANCE", "value": "" }, { "name": "OODLE_API_KEY", "value": "" }, { "name": "OODLE_LOGS_HOST", "value": "" } ], "command": [ "--config", "https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/otel/otel-config-v2.yaml" ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "", "awslogs-group": "", "awslogs-stream-prefix": "" } }, "firelensConfiguration": { "type": "fluentbit" } } ``` The `otel-collector` container downloads a simplified configuration file from [otel-config-v2.yaml](https://oodle-configs.s3.us-west-2.amazonaws.com/logs/ecs/otel/otel-config-v2.yaml) that routes logs exclusively to Oodle. In addition, environment variables `CLOUDWATCH_LOG_GROUP` and `CLOUDWATCH_LOG_STREAM` are also removed from the `otel-collector` container. ## CloudWatch Logs Integration If you are already sending your ECS logs to CloudWatch and prefer to push logs from CloudWatch to Oodle instead of modifying your ECS task definitions, you can use our CloudWatch integration. This approach allows you to keep your existing logging setup while still getting your logs into Oodle. For detailed instructions on how to set up the CloudWatch to Oodle integration, please refer to our [CloudWatch integration guide](/integrations/logs/cloudwatch). --- ## Evaluator Dependencies Evaluator dependencies let you chain evaluation rules into a directed acyclic graph (DAG). A dependent rule only runs on spans where **all** of its dependencies scored > 0, saving LLM costs by skipping spans that fail earlier, cheaper checks. ## How it works 1. The eval-worker builds a DAG from each rule's `dependsOnRuleIds` field. 2. Rules are sorted into topological levels — level 0 has no dependencies, level 1 depends only on level 0, and so on. 3. After each rule runs, the eval-worker records which span IDs had any score greater than zero. 4. Before running a dependent rule, the worker filters candidates to only spans that passed **all** of that rule's dependencies (AND logic). 5. Each rule still applies its own dedup, sampling, and rate limits on the filtered set. ### What counts as "passing" A span passes a dependency if **any** numeric score returned by that rule is greater than zero. Categorical scores are ignored. ## Example: code filter → LLM deep analysis ``` Code eval: has_output (level 0) ↓ LLM eval: Correctness (level 1) ↓ LLM eval: Deep analysis (level 2) ``` 1. `has_output` is a code evaluator that returns 1 if the span has non-empty output, 0 otherwise. It runs on all matching spans. 2. `Correctness` depends on `has_output`. It only runs the LLM judge on spans where the code eval scored > 0 (i.e., spans that actually have output). 3. `Deep analysis` depends on `Correctness`. It only runs on spans that passed the correctness check. This saves LLM costs because the cheap code evaluator filters out irrelevant spans before the expensive LLM calls happen. ## Multi-dependency example ``` Code eval: has_output (level 0) Code eval: is_english (level 0) ↓ ↓ LLM eval: Correctness (level 1) ``` `Correctness` depends on both `has_output` and `is_english`. A span must pass **both** code evaluators to be evaluated for correctness. ## Setting up dependencies 1. Go to **GenAI → Evaluators → New Evaluator** (or edit an existing rule). 2. In the **Depends on** section, select one or more existing rules. 3. The API validates that the new dependency does not create a cycle. If it does, the save is rejected with an error. ## Cycle prevention The system blocks saving any rule that would create a dependency cycle. For example, if rule A depends on rule B, you cannot make rule B depend on rule A. Cycle detection runs both server-side (at save time) and is enforced by the API, which returns a 400 error if a cycle is detected. ## Deleting a dependency target If you try to delete a rule that other rules depend on, the API returns a 409 Conflict error listing the dependent rule names. You must first remove the dependency from those rules before deleting the target. ## Visualizing dependencies ### Dependencies tab The **Dependencies** tab on the Evaluators page shows the full DAG across all rules. Each node represents a rule with its name, type (Code or LLM), and status (Active or Paused). Edges show the dependency direction. Click any node to open the evaluator details drawer. ### Drawer subtree view Inside the evaluator details drawer, the **Dependencies** tab shows the subtree for the focused rule — both its upstream dependencies and downstream dependents. The focused node is highlighted. ## Best practices - **Same filters recommended:** dependent rules should share the same span filters for best results. The eval-worker groups rules by filter set, so cross-group dependencies work but span overlap may be limited. - **Use code evals as first-pass filters:** code evaluators are fast and cheap. Use them to filter out irrelevant spans before running LLM judges. - **Keep the DAG shallow:** deeply nested dependencies add latency to each eval cycle. Two to three levels is usually sufficient. --- ## Evaluators Evaluators automatically score your LLM spans to track quality, accuracy, and compliance across your GenAI application. Navigate to **Agent Observability → Evaluators** (). ## Types of Evaluators | Type | Best For | Execution | Guide | |------|----------|-----------|-------| | **[LLM-as-Judge](./llm-as-judge)** | Nuanced quality assessment, relevance, tone | Calls an LLM to evaluate each span | [Setup guide →](./llm-as-judge) | | **Code** | Deterministic checks: regex, JSON schema, exact match, keyword detection | Runs Python code in an isolated microVM — fast and free | Coming soon | ## How It Works 1. **Create a template** — define what to evaluate (LLM prompt or Python code) 2. **Create a rule** — configure which spans to evaluate, sampling rate, and filters 3. **Scores appear** on the Traces page, attached to each evaluated span ## When to Use Each Type **Use LLM-as-Judge when:** - You need subjective assessment (relevance, helpfulness, tone) - The evaluation criteria are hard to express as code - You want natural-language reasoning with each score - You want to use one of the 8 [built-in templates](./llm-as-judge#built-in-templates) (hallucination, relevance, correctness, etc.) **Use Code Evaluators when:** - You need deterministic, repeatable checks - Speed and cost matter (no LLM API call needed) - You're validating structure (JSON schema, required fields, format) - You want exact match, regex, or keyword detection ## Evaluators Page The evaluators page has three tabs: ### Evaluators Tab Shows all active evaluation rules. Each row displays the rule name, status (Active / Paused), 24-hour evaluation cost, and timestamps. Click a rule to open a detail drawer with a score-over-time chart, execution logs, and the full configuration. Use the sidebar to filter by status (Active / Paused) and evaluator name. ### Library Tab Browse all available templates — both the 8 built-in managed templates and any custom templates your team has created. Click a template to see its full prompt or code, variables, and the rules using it. The built-in managed templates cover common evaluation criteria: - Hallucination - Helpfulness - Relevance - Toxicity - Correctness - Conciseness - Context Relevance (RAG) - Faithfulness (RAG) See the [LLM-as-Judge guide](./llm-as-judge) for details on each template. ### Scores Tab View all scores produced by evaluators. Filter by time range, score name, value range, and span labels. Click any row to open the corresponding trace detail. ## Prerequisites - LLM-as-Judge evaluators require an [LLM Connection](/agent-observability/llm-connections) - Code evaluators have no external dependencies - Creating evaluators requires the **Editor** or **Admin** role --- ## LLM-as-Judge Evaluators LLM-as-Judge evaluators use a language model to score your GenAI spans. They are ideal for subjective assessments like relevance, helpfulness, and tone that are hard to express as deterministic code. ## Built-in Templates Oodle includes eight managed evaluator templates ready to use out of the box. Each returns a score from 0 to 1 with reasoning. | Template | Variables | What it measures | |----------|-----------|------------------| | **Hallucination** | query, generation, context | Whether the output is grounded in the query and context | | **Helpfulness** | query, generation | How helpful and complete the answer is | | **Relevance** | query, generation | How relevant the output is to the query | | **Toxicity** | generation | Presence of toxic content, hate speech, or profanity | | **Correctness** | query, generation, ground_truth | Factual correctness against a known answer | | **Conciseness** | query, generation | Brevity without losing completeness | | **Context Relevance** | query, context | Whether retrieved context is relevant to the query (RAG) | | **Faithfulness** | generation, context | Whether the output stays faithful to the context (RAG) | Managed templates cannot be edited or deleted. ## Creating a Custom Template 1. Go to **GenAI → Evaluators** () 2. Click **Create Evaluator** → **New Template** 3. Select **LLM as a Judge** 4. Enter a name and write your evaluation prompt using `{{variable}}` placeholders 5. Click **Save** The template automatically appends return instructions that ask the model to respond with a JSON score (0–1) and reasoning. ### Writing Effective Prompts Use `{{variable}}` syntax to reference span data. Common variables: - `{{query}}` — the user's input - `{{generation}}` — the model's output - `{{context}}` — system instructions or retrieved context Example: ```text You are an expert evaluator. Score the following output on a scale of 0 to 1 based on whether it directly answers the question. User query: {{query}} LLM output: {{generation}} ``` ## Creating an Evaluation Rule After selecting a template (built-in or custom), configure how it runs: 1. **Name** — the score name that appears on traces 2. **LLM Connection** — which provider to use for evaluation ([configure here](/agent-observability/llm-connections)) 3. **Model** — which model runs the evaluation 4. **Variable mapping** — map template variables to span fields 5. **Sampling rate** — percentage of matching spans to evaluate (1–100%, default 100%) 6. **Max invocations/hour** — rate cap to control cost (default 100, set to 0 for no limit) 7. **Filter rules** — optional span label filters to restrict which spans are evaluated Click **Create** to activate the rule. ## Variable Mapping Each `{{variable}}` in the template prompt maps to a span field. Oodle auto-assigns sensible defaults: | Variable | Default field | |----------|---------------| | `query`, `input` | `gen_ai.input.messages` | | `generation`, `output` | `gen_ai.output.messages` | | `context`, `system` | `gen_ai.system_instructions` | | `ground_truth` | `expected_output` | You can override any mapping by selecting a different span tag from the dropdown. Add an optional **JsonPath** expression (e.g. `$[0].content`) to extract a nested value from JSON fields. Use the **Preview** toggle with a sample span to verify that variables resolve correctly before creating the rule. ## Managing Evaluators ### Evaluators Tab The main tab shows all active evaluation rules. Each row displays the rule name, status (Active / Paused), 24-hour cost, and timestamps. Click a rule to open a detail drawer with: - **Score over time** chart (1h / 6h / 24h / 7d / 30d) - **Execution logs** with per-span score, reasoning, and trace links - **Configuration** summary (connection, model, template, mappings) ### Library Tab Browse all templates — managed and custom. Click any template to see its full prompt text, variables, and the list of rules using it. Use **Use this evaluator** to create a new rule from the template. ### Scores Tab View all scores produced by evaluators across a configurable time range. Filter by score name, value range, and span labels. Click any row to open the trace detail. --- ## GPU Monitoring Oodle's GPU Monitoring gives you full visibility into your GPU fleet — utilization, memory allocation, temperature, power draw, and per-process resource consumption. Works with NVIDIA GPUs using either the `nvidia_gpu_exporter` (nvidia-smi based) or `dcgm-exporter`. ## Overview Tab The Overview tab provides a high-level health summary of your entire GPU fleet. ### Summary Cards | Card | Description | |------|-------------| | **Total GPUs** | Number of monitored GPU devices | | **Active GPUs** | GPUs with utilization > 5% | | **Total Hosts** | Number of hosts with GPUs | | **Avg Utilization** | Fleet-wide average GPU utilization | ### Alerts & Health | Alert | Trigger | |-------|---------| | **Idle GPUs** | Devices with < 5% utilization | | **Thermal Throttling** | Devices with active thermal throttle or temp ≥ 85°C | | **ECC / XID Errors** | Devices reporting memory or XID errors | | **PCIe Degradation** | Active devices with zero PCIe throughput | ## Hosts Tab The Hosts tab lists every host that has at least one GPU installed. | Column | Description | |--------|-------------| | **Host** | Hostname (instance label) | | **Device** | GPU model name | | **Health** | Healthy, Throttled, or Error state | | **Active** | Number of active / total GPUs | | **CPU** | Host CPU utilization % | | **Memory** | Host system memory utilization % | | **GPU** | Average GPU compute utilization % | | **GPU Mem** | Average GPU VRAM allocation % | | **ECC** | ECC error count | | **XID** | XID error count | Click any host row to open a detail drawer with devices, processes, and embedded Grafana dashboards. ## Devices Tab The Devices tab lists every individual GPU device across all hosts. | Column | Description | |--------|-------------| | **UUID** | GPU device UUID | | **Model** | GPU model name | | **Host** | Host the GPU is installed in | | **Health** | Health status | | **GPU Util** | Compute utilization % | | **GPU Mem** | VRAM allocation % | | **Temp** | Current temperature in °C | | **Power** | Current power draw in watts | | **ECC** | ECC error count | Click any device row to open a detail drawer with GPU-specific Grafana dashboards and process information. ## Process Monitor The process monitor shows per-process resource usage on GPU hosts using `process_exporter` metrics. | Metric | Description | |--------|-------------| | **CPU Rate** | CPU cores consumed | | **Resident Memory** | Physical memory usage | | **Read / Write Bytes** | Disk I/O rate | | **Context Switches** | Rate of context switches | | **FD Ratio** | File descriptor usage as % of limit | ## Setup Navigate to **Integrations → GPU Monitoring** to access the setup wizard, which guides you through: 1. **Choose your exporter** — `nvidia_gpu_exporter` or `dcgm-exporter` 2. **Install the exporter** — Docker, systemd, or binary commands provided 3. **Install process exporter** — for per-process visibility 4. **Configure scraping** — Prometheus or VMAgent configuration with correct instance labeling 5. **Verify data** — confirm metrics are flowing ### Supported Exporters | Exporter | Metrics Prefix | Use Case | |----------|----------------|----------| | **nvidia_gpu_exporter** | `nvidia_smi_*` | Simple setup, covers most use cases | | **dcgm-exporter** | `DCGM_FI_*` | Advanced profiling metrics | ### Instance Label The GPU page identifies hosts by the `instance` label. The setup wizard configures relabeling to strip the port from the target address, so you get clean hostnames (e.g. `gpu-train-01` instead of `gpu-train-01:9835`). ## Grafana Dashboards Four built-in dashboards are embedded in the UI, accessible from host and device detail drawers: | Dashboard | Used In | |-----------|---------| | **GPU Host Overview** | Host drawer | | **NVIDIA GPU Metrics** | Device drawer (nvidia_smi) | | **DCGM GPU Metrics** | Device drawer (DCGM) | | **GPU Process Monitor** | Process tab | The NVIDIA GPU Metrics dashboard provides a deep dive into a single GPU — real-time utilization, clock speeds, memory allocation, power draw, fan speed, and throttle reasons. It automatically filters to the selected device when opened from the Devices tab. ## Best Practices - **Use consistent instance labels** — strip ports so the same host appears as one entity across all metrics. - **Monitor VRAM allocation** — high usage (>90%) leads to OOM kills. - **Watch thermal throttling** — sustained temps above 80°C reduce performance. - **Track idle GPUs** — idle GPUs waste expensive compute; use the alert to right-size your fleet. - **Enable process_exporter** — without it, you lose visibility into which processes consume resources. --- ## Oodle Oodle is a fast, fully managed, drop-in replacement for Prometheus and ELK stack at scale.