> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Filter and search logs

> Narrow down to exactly the traces you need using metadata filters, SQL queries, or the API. Pull out specific production data for debugging, analysis, or dataset creation.

Narrow down to specific traces by applying a filter via the UI, CLI, or API. Paste ready-made patterns from [Common filters](#common-filters), or save a useful combination as a [shared table view](#save-and-share-filters). When you don't have an exact filter in mind, ask [Loop](#ask-questions-with-loop).

## Apply a filter

<Tabs>
  <Tab title="UI" icon="mouse-pointer-2">
    Select <Icon icon="list-filter" /> **Filter** to open the filter menu:

    * **Basic**: Point-and-click filtering for common fields.
    * **SQL**: Write precise [SQL queries](/reference/sql) with operators, functions, and full-text search. Use the <Icon icon="blend" /> **Generate** button to create queries from natural-language descriptions. See [SQL best practices](/reference/sql/best-practices) performance tips.

    When viewing traces (set in <Icon icon="settings-2" /> **Display** > <Icon icon="rows-3" /> **Row type**), Basic filter conditions [match across spans](#match-traces-across-spans) automatically.

    <Note>
      SQL queries on project logs enforce your plan's data retention limit. Use a relative interval to stay within the window. See [Plans and limits](/plans-and-limits) for retention details.
    </Note>
  </Tab>

  <Tab title="CLI" icon="terminal">
    Use [`bt sql`](/reference/cli/sql) to run filtered SQL queries against your logs from the terminal. Pass a query inline or pipe one in from a file.

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    bt sql "SELECT * FROM project_logs('my-project') WHERE scores.Factuality > 0.8 LIMIT 100"
    ```

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    cat analysis.sql | bt sql
    ```

    For automated workflows, pass `--non-interactive` to skip the interactive interface and `--json` to get machine-readable output.

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    bt sql --non-interactive "SELECT count(*) FROM project_logs('my-project') WHERE tags MATCH 'error'" --json
    ```
  </Tab>

  <Tab title="API" icon="code">
    Query logs programmatically using the Braintrust API for automation, integrations, and custom tooling.

    **Basic filtering**: Use the [project logs endpoint](/api-reference/logs/fetch-project-logs-get-form) for simple filters and programmatic access:

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    curl -X GET "https://api.braintrust.dev/v1/project/<PROJECT_ID>/logs" \
      -H "Authorization: Bearer <YOUR_API_KEY>" \
      -H "Content-Type: application/json"
    ```

    **SQL queries**: For complex queries, POST a SQL query to the [`/btql` endpoint](/api-reference#query-logs-and-experiments):

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    curl -X POST https://api.braintrust.dev/btql \
      -H "Authorization: Bearer <YOUR_API_KEY>" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "SELECT * FROM project_logs('"'<PROJECT_ID>'"') WHERE scores.Factuality > 0.8 LIMIT 100"
      }'
    ```

    The API accepts these parameters:

    * `query` (required): Your SQL query string.
    * `fmt`: Response format (`json` or `parquet`, defaults to `json`).
    * `tz_offset`: Timezone offset in minutes for correct day boundaries.
    * `audit_log`: Include audit log data.
  </Tab>
</Tabs>

## Identify spans and traces

Every span carries three ID fields, each with a distinct role:

| Field          | Identifies                                                             | Use it to                                                          |
| -------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `id`           | An individual span (one row in logs or an experiment).                 | Filter to or link to a single span. This is the span's identifier. |
| `root_span_id` | The entire trace the span belongs to.                                  | Filter to or link to a full trace.                                 |
| `span_id`      | An internal field used with `span_parents` to construct the span tree. | Nothing. Treat it as an implementation detail.                     |

To fetch a known span or trace, filter on `id` or `root_span_id`. Both map directly to the index, so they are the fastest predicates. Avoid filtering on `span_id`. See [Fetch known traces or spans by ID](/reference/sql/best-practices#fetch-known-traces-or-spans-by-id) for details.

When you link to a trace in the Braintrust UI, the `r=` URL parameter is the `root_span_id`, not the `id`. Using the `root_span_id` is the efficient way to open a trace. See [Link to a trace or span](/observe/examine-traces#link-to-a-trace-or-span).

<Note>
  Some ingestion paths, such as OpenTelemetry collectors, set `span_id` to the same value as `id`, which is why the two are often conflated. Regardless, `id` is the field that identifies a span.
</Note>

## Common filters

Ready-to-use filter expressions for the most common log queries. Paste them directly into the **SQL** filter tab in the UI, or use them as the WHERE clause in [`bt sql`](/reference/cli/sql) or [`/btql` API queries](/api-reference#query-logs-and-experiments).

<AccordionGroup>
  <Accordion title="Filter by score">
    <CodeGroup>
      ```sql Above threshold theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      scores.Factuality > 0.8
      ```

      ```sql Including missing theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      scores.Factuality IS NULL OR scores.Factuality < 0.5
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Filter by tag">
    <CodeGroup>
      ```sql Has tag theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      tags MATCH "error"
      ```

      ```sql Excludes tag theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      NOT tags MATCH "internal"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Filter errored runs">
    ```sql theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    error IS NOT NULL
    ```
  </Accordion>

  <Accordion title="Filter by metadata">
    <CodeGroup>
      ```sql Single field theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      metadata.user_id = "user-123"
      ```

      ```sql Combined theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      metadata.environment = "production" AND metadata.model = "gpt-5-mini"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Search text fields">
    Use `search()` to match across all text fields, or `MATCH` to target a single field. See [Full-text search](/reference/sql#full-text-search) for details.

    <CodeGroup>
      ```sql All fields theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      search('timeout')
      ```

      ```sql Specific field theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      output MATCH 'timeout'
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Filter by time">
    Use a relative interval to stay within your plan's [data retention window](/plans-and-limits).

    <CodeGroup>
      ```sql Relative theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      created > now() - interval 1 day
      ```

      ```sql Range theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      created >= '2024-01-01' AND created < '2024-02-01'
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Match traces across spans">
    When filtering traces, you often want to match against any span in the trace, not just the root. Wrap conditions in `ANY_SPAN()`. For example, to find traces with both an LLM call and an error somewhere in the tree:

    ```sql theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    ANY_SPAN(span_attributes.type = 'llm') AND ANY_SPAN(error IS NOT NULL)
    ```

    See [Single span filters](/reference/sql#single-span-filters) for nesting rules and the `is_root` restriction.

    <Note>
      Braintrust applies `ANY_SPAN()` automatically in:

      * The **Basic** filter tab when [**<Icon icon="settings-2" /> Display** > **<Icon icon="rows-3" /> Row type**](/observe/view-logs#browse-logs) is set to **Traces**.
      * [Topics automation filters](/observe/topics/manage#adjust-automation).
      * [Trace-scoped online scoring rule filters](/evaluate/score-online#rule-parameters).
    </Note>
  </Accordion>
</AccordionGroup>

## Save and share filters

Save a frequently-used combination of filters, display settings, and columns as a [custom table view](/observe/view-logs#create-custom-table-views). Saved views appear in the <Icon icon="layers-2" /> menu at the top of the table and are visible to all project members.

If you've built a useful view in one project, you can [duplicate it to another project](/observe/view-logs#duplicate-table-views-across-projects) via the API.

## Ask questions with Loop

Select <Icon icon="blend" /> **Loop** on the <Icon icon="activity" /> **Logs** page to ask natural language questions about your traces. Loop understands your data structure and can answer questions, identify patterns, and surface specific traces without writing any queries.

Example questions:

* "Show me traces where the user was confused"
* "Find requests that took longer than usual"
* "What are the most common error patterns?"

You can also select one or more rows and use <Icon icon="blend" /> **Find similar traces**. Loop identifies common traits across the selected traces and returns similar ones.

Loop is also available when viewing an individual trace. Ask questions like "Summarize this trace" or "Why did this request fail?" See [Analyze logs](/loop#analyze-logs) and [Analyze individual traces](/loop#analyze-individual-traces) for more details.

## Speed up log filtering

If you frequently filter on the same custom fields, you can index them to reduce query latency. Braintrust offers two options: A full-text index for broad search and subfield indexes for specific fields you filter on most.

1. Go to **<Icon icon="settings-2" /> Settings** > [**<Icon icon="ellipsis" /> Advanced**](https://www.braintrust.dev/app/~/configuration/advanced).
2. Under **Log search optimization**, enable the toggle to build a full-text index that speeds up text-based filter queries.
3. Under **Shingled search optimization**, enable the toggle to also index multi-word phrases, which speeds up phrase and multi-word `search()` queries.
4. Under **Subfield indexing**, click **+ Add subfield index** for each field you filter on frequently.

   Braintrust auto-discovers candidate fields from your data (e.g., `metadata.session_id`). If a field doesn't appear, you can type it in directly. Subfield paths must start with `input`, `output`, `expected`, `metadata`, or `span_attributes`.
5. Click **Save and index**.
6. Enter how many days back to backfill (default: 3) and click **Save and backfill**.

The **Index status** section shows backfill progress as indexing runs in the background.

<Tip>
  Use [`search()`](/reference/sql#full-text-search) in SQL filters to query all text fields at once. It gets automatic bloom filter acceleration when log search optimization is on.
</Tip>

## Next steps

* [Score online](/evaluate/score-online) to evaluate filtered traces
* [Create dashboards](/observe/dashboards) with filtered metrics
* Read the [SQL reference](/reference/sql) for complete query syntax
