---
title: "Observability"
description: "Trace Ask AI and MCP runs with span IDs, timing, status, previews, errors, and callbacks"
canonical_url: "https://docs.farming-labs.dev/docs/customization/observability"
markdown_url: "https://docs.farming-labs.dev/docs/customization/observability.md"
last_updated: "2018-10-20"
---

# Observability
URL: /docs/customization/observability
LLM index: /llms.txt
Description: Trace Ask AI and MCP runs with span IDs, timing, status, previews, errors, and callbacks

# Observability

Use this page when the user asks about this topic: Trace Ask AI and MCP runs with span IDs, timing, status, previews, errors, and callbacks.
Keep answers grounded in the exact options, routes, commands, and examples documented here.
If the request moves beyond this page, point to the closest related docs instead of inventing config.

Use `observability` when you need step-level visibility into agent runtime behavior. It emits
span-like trace events for Ask AI and MCP tools: run start/end, prompt building, retrieval, model
calls, model responses, streaming handoff, tool calls, tool results, errors, retries, and timeouts.

Observability is separate from [Analytics](/docs/customization/analytics). It does not receive
`page_view`, `search_query`, `api_ai_request`, or `mcp_tool`; those usage events belong to
`analytics.onEvent`.

## Quick Start

```ts title="docs.config.ts"
export default defineDocs({
  observability: true,
});
```

`observability: true` logs trace events to the console with the
`[@farming-labs/docs:observability]` prefix.

## Live Local Logs

Use console observability when you want to watch Ask AI and MCP work as it happens:

```ts title="docs.config.ts"
export default defineDocs({
  observability: {
    console: "debug",
  },
});
```

Every trace event prints to the dev-server terminal with its `traceId`, `spanId`, `name`, `status`,
`durationMs`, previews, and metadata.

## Callback

Use `onEvent` when you want to forward traces to a log drain, APM, OpenTelemetry bridge, database,
or your own debugging dashboard:

```ts title="docs.config.ts"
export default defineDocs({
  observability: {
    console: "debug",
    async onEvent(event) {
      await fetch("https://observability.example.com/events", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(event),
      });
    },
  },
});
```

Set `console: false` if you only want the callback and do not want terminal logs.

For metrics, aggregate by `event.type`, `event.name`, `event.status`, and `event.durationMs`. For
traces, group by `event.traceId` and order by `startedAt` or `timestamp`.

## Event Shape

`onEvent` receives a normalized `DocsObservabilityEvent`:

```ts
interface DocsObservabilityEvent {
  type: string;
  timestamp: string;
  source: "client" | "server" | "mcp";
  traceId?: string;
  spanId?: string;
  parentSpanId?: string;
  name?: string;
  startedAt?: string;
  endedAt?: string;
  durationMs?: number;
  status?: "started" | "success" | "error" | "retry" | "timeout";
  inputPreview?: Record<string, unknown>;
  outputPreview?: Record<string, unknown>;
  url?: string;
  path?: string;
  referrer?: string;
  locale?: string;
  input?: {
    query?: string;
    question?: string;
    feedbackComment?: string;
    content?: string;
  };
  metadata?: Record<string, unknown>;
  properties?: Record<string, unknown>;
}
```

Example tool event:

```ts
{
  traceId: "run_123",
  spanId: "span_456",
  parentSpanId: "run_123",
  type: "tool.call",
  name: "read_page",
  startedAt: "2026-01-01T00:00:00.000Z",
  endedAt: "2026-01-01T00:00:00.082Z",
  durationMs: 82,
  status: "success",
  inputPreview: { page: "/docs/mcp" },
  outputPreview: { chars: 4210 },
  metadata: {
    attempt: 1,
    model: "gpt-5.2",
  },
}
```

## Trace Events

Observability events describe runtime steps inside an agent run. They are span-like: use
`traceId` to group a run, `spanId` to identify a step, and `parentSpanId` to connect child steps to
their parent.

### Ask AI Events

| Event              | Meaning                                                               |
| ------------------ | --------------------------------------------------------------------- |
| `run.start`        | A new Ask AI run started. This is the root span for the request.       |
| `user.input`       | The latest user message was accepted and summarized into safe counts. |
| `retrieval.query`  | Docs retrieval started for the user question.                         |
| `retrieval.result` | Docs retrieval completed and produced measurable result counts.       |
| `retrieval.error`  | Docs retrieval failed before the model call could use the context.    |
| `prompt.build`     | The system prompt and retrieved docs context were assembled.          |
| `model.call`       | The outbound model request started.                                   |
| `model.response`   | The model provider returned successful response headers.              |
| `model.stream`     | A streaming model response was handed back to the client.             |
| `model.error`      | The model request failed, threw, or returned an error status.         |
| `agent.final`      | The final response handoff was reached after a successful model call. |
| `run.error`        | The run ended unsuccessfully.                                         |
| `run.end`          | The run ended, either successfully or after an error.                 |

### MCP Tool Events

| Event         | Meaning                                                           |
| ------------- | ----------------------------------------------------------------- |
| `tool.call`   | An MCP tool call started, such as `search_docs` or `read_page`.   |
| `tool.result` | An MCP tool completed successfully and returned a result.         |
| `tool.error`  | An MCP tool returned an error result or threw during execution.   |

### Generic Runtime Events

| Event     | Meaning                                                               |
| --------- | --------------------------------------------------------------------- |
| `retry`   | A retry attempt was recorded for a failed or unstable step.           |
| `timeout` | A step timed out before it could complete.                            |
| `error`   | A generic run-level error was recorded outside a more specific event. |

Built-in trace events avoid raw user-authored text. They use lengths, counts, route names, model
IDs, status codes, and content sizes so they are safer to aggregate.

## Using Analytics Together

Configure both hooks when you want product analytics and runtime traces:

```ts title="docs.config.ts"
export default defineDocs({
  analytics: {
    async onEvent(event) {
      await sendUsageEvent(event);
    },
  },
  observability: {
    console: "debug",
    async onEvent(event) {
      await sendTraceEvent(event);
    },
  },
});
```

In that setup:

- `analytics.onEvent` receives events like `page_view`, `search_query`, `api_ai_request`, and
  `mcp_tool`
- `observability.onEvent` receives events like `run.start`, `prompt.build`, `model.call`, and
  `tool.result`

## Options

| Option          | Type                                      | Default                      |
| --------------- | ----------------------------------------- | ---------------------------- |
| `enabled`       | `boolean`                                 | `true` when object is passed |
| `console`       | `boolean \| "log" \| "info" \| "debug"` | `true` for `observability: true` |
| `includeInputs` | `boolean`                                 | `false`                      |
| `onEvent`       | `(event) => void \| Promise<void>`        | `undefined`                  |

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
