---
title: "Deeper Telemetry"
description: "Add tool execution timing and total wall time with createEvlogIntegration. Compose with other middleware like supermemory or guardrails."
canonical_url: "https://www.evlog.dev/use-cases/ai-sdk/telemetry"
---
# Deeper Telemetry

> Add tool execution timing and total wall time with createEvlogIntegration. Compose with other middleware like supermemory or guardrails.

`createAILogger` covers tokens, model info, and streaming metrics. For deeper observability (per-tool execution timing, success/failure tracking, and total generation wall time), add `createEvlogIntegration()` on top. It implements the AI SDK telemetry interface (v6 `TelemetryIntegration`, v7 `Telemetry`) and captures data middleware alone cannot see.

On AI SDK v7, the integration also auto-captures embeddings via `onEmbedEnd`, records stream aborts via `onAbort`, and surfaces unrecoverable errors via `onError`.

## Combined with middleware (recommended)

When passed an `AILogger`, the integration shares its accumulator. Both paths write to the same `ai.*` field:

<framework-tabs>

```typescript [Nuxt]server/api/agent.post.ts
import { useLogger } from 'evlog'
import { generateText } from 'ai'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { getWeather, searchDB },
    telemetry: {
      integrations: [createEvlogIntegration(ai)],
    },
  })

  return { text: result.text }
})
```

```typescript [Nitro]routes/api/agent.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
import { generateText } from 'ai'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { getWeather, searchDB },
    telemetry: {
      integrations: [createEvlogIntegration(ai)],
    },
  })

  return { text: result.text }
})
```

```typescript [Next.js]app/api/agent/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'
import { generateText } from 'ai'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'

export const POST = withEvlog(async () => {
  const log = useLogger()

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { getWeather, searchDB },
    telemetry: {
      integrations: [createEvlogIntegration(ai)],
    },
  })

  return Response.json({ text: result.text })
})
```

```typescript [Hono]src/index.ts
import { generateText } from 'ai'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'

app.post('/api/agent', async (c) => {
  const log = c.get('log')

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { getWeather, searchDB },
    telemetry: {
      integrations: [createEvlogIntegration(ai)],
    },
  })

  return c.json({ text: result.text })
})
```

```typescript [Express]src/index.ts
import { generateText } from 'ai'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'

app.post('/api/agent', async (req, res) => {
  const log = req.log

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { getWeather, searchDB },
    telemetry: {
      integrations: [createEvlogIntegration(ai)],
    },
  })

  return res.json({ text: result.text })
})
```

</framework-tabs>

Your wide event now includes per-tool timing:

```json [Wide Event]
{
  "ai": {
    "calls": 2,
    "steps": 2,
    "model": "claude-sonnet-4.6",
    "provider": "anthropic",
    "inputTokens": 3500,
    "outputTokens": 800,
    "totalTokens": 4300,
    "toolCalls": ["getWeather", "searchDB"],
    "tools": [
      { "name": "getWeather", "durationMs": 150, "success": true },
      { "name": "searchDB", "durationMs": 45, "success": true }
    ],
    "totalDurationMs": 2340,
    "msToFirstChunk": 180,
    "msToFinish": 2100,
    "tokensPerSecond": 380
  }
}
```

## Standalone (without middleware)

If your model is already wrapped (e.g. by another middleware), pass the request logger directly:

```typescript [server/api/chat.post.ts]
import { createEvlogIntegration } from 'evlog/ai'

const integration = createEvlogIntegration(log)

const result = await generateText({
  model: somePreWrappedModel,
  telemetry: {
    integrations: [integration],
  },
})
```

## What lands on the wide event

<table>
<thead>
  <tr>
    <th>
      Data
    </th>
    
    <th>
      Source
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        ai.tools[]
      </code>
    </td>
    
    <td>
      <code>
        onToolExecutionEnd
      </code>
      
       (v7) / <code>
        onToolCallFinish
      </code>
      
       (v6)
    </td>
    
    <td>
      Per-tool <code>
        name
      </code>
      
      , <code>
        durationMs
      </code>
      
      , <code>
        success
      </code>
      
      , and <code>
        error
      </code>
      
       (if failed)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ai.totalDurationMs
      </code>
    </td>
    
    <td>
      <code>
        onStart
      </code>
      
       → <code>
        onEnd
      </code>
      
       (v7) / <code>
        onFinish
      </code>
      
       (v6)
    </td>
    
    <td>
      Total wall time from generation start to completion
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ai.embedding
      </code>
    </td>
    
    <td>
      <code>
        onEmbedEnd
      </code>
      
       (v7)
    </td>
    
    <td>
      Auto-captured from <code>
        embed()
      </code>
      
       / <code>
        embedMany()
      </code>
      
       when telemetry is enabled
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ai.finishReason: 'abort'
      </code>
    </td>
    
    <td>
      <code>
        onAbort
      </code>
      
       (v7)
    </td>
    
    <td>
      Set when a streaming generation is aborted
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ai.error
      </code>
    </td>
    
    <td>
      <code>
        onAbort
      </code>
      
       / <code>
        onError
      </code>
      
       (v7)
    </td>
    
    <td>
      Abort reason or unrecoverable generation error
    </td>
  </tr>
</tbody>
</table>

The middleware captures tokens, model info, and streaming metrics. The integration captures tool execution timing. Together, they give you complete AI observability.

## Compose it with the rest

`ai.wrap()` works with models that are already wrapped by other tools. If you use supermemory, guardrails middleware, or any other model wrapper, pass the wrapped model to `ai.wrap()`:

```typescript [server/api/chat.post.ts]
import { createAILogger } from 'evlog/ai'
import { withSupermemory } from '@supermemory/tools/ai-sdk'
import { createGateway } from 'ai'

const gateway = createGateway({ ... })
const ai = createAILogger(log)
const base = gateway('anthropic/claude-sonnet-4.6')
const model = ai.wrap(withSupermemory(base, 'your-org-id', { mode: 'full' }))
```

For explicit middleware composition, use `createAIMiddleware` to get the raw middleware and compose it yourself via `wrapLanguageModel`:

```typescript [server/api/chat.post.ts]
import { createAIMiddleware } from 'evlog/ai'
import { wrapLanguageModel } from 'ai'

const model = wrapLanguageModel({
  model: base,
  middleware: [createAIMiddleware(log, { toolInputs: true }), otherMiddleware],
})
```

`createAIMiddleware` returns the same middleware that `createAILogger` uses internally. The difference: `createAIMiddleware` does not include `captureEmbed` (embedding models don't use middleware). Use `createAILogger` for the full API, `createAIMiddleware` when you need explicit middleware ordering.

---

- [Overview](https://www.evlog.dev/use-cases/ai-sdk/overview)
- [Usage Patterns](https://www.evlog.dev/use-cases/ai-sdk/usage)


## Sitemap

See the full [sitemap](https://www.evlog.dev/sitemap.md) for all pages.
