Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/frameworks/deno.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Configure Deno OpenTelemetry export to Logfire and use the logfire

# Deno

Deno has built-in OpenTelemetry support. Configure Deno's OTLP exporter to point at Logfire, then optionally use the `logfire` package for manual spans and logs.
Since v2.2, Deno has built-in [OpenTelemetry support](https://docs.deno.com/runtime/fundamentals/open_telemetry/). Configure Deno's OTLP exporter to point at Logfire, then optionally use the `logfire` package for manual spans and logs.

## Runtime Export

Expand Down
2 changes: 2 additions & 0 deletions docs/frameworks/express.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ description: Instrument an Express application with @pydantic/logfire-node.

Express applications should configure Logfire before Express is imported, so OpenTelemetry can patch the framework and HTTP modules.

The underlying `@opentelemetry/instrumentation-express` package supports Express `>=4.0.0 <5`.

## Install

```bash
Expand Down
35 changes: 34 additions & 1 deletion docs/frameworks/nextjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export default async function Page() {
}
```

Vercel production deployments can cache build and runtime configuration. If spans do not appear after changing tracing environment variables, clear the Vercel data cache for the project and redeploy.

## Client-Side Tracing

Install the browser package:
Expand All @@ -57,7 +59,36 @@ Install the browser package:
npm install @pydantic/logfire-browser @opentelemetry/auto-instrumentations-web
```

Create a proxy route or middleware that forwards browser OTLP requests to Logfire with the `Authorization` header. Then configure the browser package in a client-only component:
Create a proxy route or middleware that forwards browser OTLP requests to Logfire with the `Authorization` header. Browser requests should go to this same-origin proxy, not directly to the Logfire API.

For Next.js 16 and later, place this code in `proxy.ts` in the project root, or in `src/proxy.ts` if your app uses `src`.

Store the write token in a server-only environment variable such as `LOGFIRE_TOKEN`. Do not use a `NEXT_PUBLIC_` variable for the token.

```ts title="proxy.ts"
import { NextRequest, NextResponse } from 'next/server'

export default function proxy(request: NextRequest) {
const url = request.nextUrl.clone()

if (url.pathname === '/logfire-proxy/v1/traces') {
Comment thread
petyosi marked this conversation as resolved.
const requestHeaders = new Headers(request.headers)
requestHeaders.set('Authorization', process.env.LOGFIRE_TOKEN!)

return NextResponse.rewrite(new URL(process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ?? 'https://logfire-api.pydantic.dev/v1/traces'), {
request: {
headers: requestHeaders,
},
})
}
}

export const config = {
matcher: '/logfire-proxy/:path*',
}
```

Then configure the browser package in a client-only component:

```tsx
'use client'
Expand All @@ -83,4 +114,6 @@ export function ClientInstrumentation() {
}
```

If you import the component from a server-rendered page, load it with `next/dynamic` and `ssr: false` so browser instrumentation only runs in the browser.

See `examples/nextjs` and `examples/nextjs-client-side-instrumentation` for working projects.
71 changes: 70 additions & 1 deletion docs/frameworks/vercel-ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The Vercel AI SDK can emit OpenTelemetry spans for model calls, tools, token usa
npm install @pydantic/logfire-node ai @ai-sdk/openai
```

Replace `@ai-sdk/openai` with the provider package you use, such as `@ai-sdk/anthropic` or `@ai-sdk/google`.

Configure Logfire before importing the AI SDK:

```ts title="instrumentation.ts"
Expand Down Expand Up @@ -41,7 +43,72 @@ console.log(result.text)

## Next.js

In Next.js, configure `@vercel/otel` as shown in [Next.js](nextjs.md), then enable the same `experimental_telemetry` option on AI SDK calls.
In Next.js, configure `@vercel/otel` as shown in [Next.js](nextjs.md), then enable the same `experimental_telemetry` option on AI SDK calls. The `instrumentation.ts` file must live in the project root, or in `src` if your Next.js app uses `src`.

```bash
npm install @vercel/otel @opentelemetry/api ai @ai-sdk/openai
```

## Enabling Telemetry

The Vercel AI SDK emits OpenTelemetry spans when `experimental_telemetry.isEnabled` is set:

```ts
const result = await generateText({
model,
prompt: 'Write a short haiku about traces.',
experimental_telemetry: { isEnabled: true },
})
```

This works with the AI SDK core functions that emit telemetry, including:

- `generateText` and `streamText`
- `generateObject` and `streamObject`
- `embed` and `embedMany`

## Example: Text Generation With Tools

```ts
import { openai } from '@ai-sdk/openai'
import { generateText, tool } from 'ai'
import { z } from 'zod'

const result = await generateText({
model: openai('gpt-4.1-mini'),
experimental_telemetry: { isEnabled: true },
tools: {
weather: tool({
description: 'Get the weather in a location',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
}),
}),
},
prompt: 'What is the weather in San Francisco?',
})

console.log(result.text)
```

For Node.js scripts, import your Logfire instrumentation file before importing or calling the AI SDK.

## What You Will See

When telemetry is enabled, Logfire captures a trace for each AI operation. Depending on the AI SDK provider and call type, traces can include:

- parent spans such as `ai.generateText`
- provider call spans for the model request
- tool call spans
- model and provider details
- input and output token usage
- timing information
- tool call arguments and results
- prompts and responses when the AI SDK emits them

## Metadata

Expand All @@ -60,3 +127,5 @@ await generateText({
},
})
```

`functionId` appears in span names and helps distinguish use cases. `metadata` attaches custom key-value pairs to the emitted telemetry spans.
92 changes: 90 additions & 2 deletions docs/packages/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Configure @pydantic/logfire-browser for browser tracing with an aut

`@pydantic/logfire-browser` configures OpenTelemetry browser tracing and re-exports the manual `logfire` API for client-side spans and logs.

Browser telemetry must be sent through your own backend proxy. Do not put a Logfire write token in browser code.
Browser telemetry must be sent through your own backend proxy. Do not put a Logfire write token in browser code, and do not configure browser code to send directly to `https://logfire-api.pydantic.dev/v1/traces`. Requests from arbitrary browser origins are blocked by CORS, and adding an `Authorization` header in client code would expose the write token.

## Install

Expand All @@ -21,8 +21,10 @@ npm install @pydantic/logfire-browser @opentelemetry/auto-instrumentations-web
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'
import * as logfire from '@pydantic/logfire-browser'

const url = new URL('/logfire-proxy/v1/traces', window.location.origin)

logfire.configure({
traceUrl: '/logfire-proxy/v1/traces',
traceUrl: url.toString(),
serviceName: 'web-app',
serviceVersion: '1.0.0',
instrumentations: [getWebAutoInstrumentations()],
Expand All @@ -31,6 +33,21 @@ logfire.configure({

`traceUrl` should point to a server-side endpoint that forwards OTLP trace requests to Logfire and adds the `Authorization` header on the server.

Use `diagLogLevel` while troubleshooting local browser instrumentation:

```ts
logfire.configure({
traceUrl: '/logfire-proxy/v1/traces',
serviceName: 'web-app',
instrumentations: [getWebAutoInstrumentations()],
diagLogLevel: logfire.DiagLogLevel.ALL,
})
```

Only enable verbose diagnostic logging in development.

`@pydantic/logfire-browser` is published as an ESM package for modern browsers and frameworks. If your app uses SSR or SSG, run `configure()` only in browser runtime code.

## Manual Client Events

```ts
Expand Down Expand Up @@ -60,6 +77,77 @@ A browser proxy should:

For Next.js, see [Next.js](../frameworks/nextjs.md). For a standalone browser example, see the `examples/browser` project in this repository.

## Python Backend Proxy

Python backends can use the experimental `logfire.experimental.forwarding` helpers to forward browser OTLP requests without exposing the write token.

For FastAPI, mount `logfire_proxy` on a path that captures the OTLP suffix:

```py title="main.py" skip-run="true" skip-reason="server-start"
from fastapi import Depends, FastAPI, Request

import logfire
from logfire.experimental.forwarding import logfire_proxy

logfire.configure()
app = FastAPI()


async def verify_user_session():
# Add authentication, rate limiting, or origin checks here.
pass


@app.post('/logfire-proxy/{path:path}', dependencies=[Depends(verify_user_session)])
async def proxy_browser_telemetry(request: Request):
return await logfire_proxy(request)
```

The `{path:path}` route parameter is required so `/logfire-proxy/v1/traces` forwards the `/v1/traces` OTLP path. The helper rejects paths other than `/v1/traces`, `/v1/logs`, and `/v1/metrics`, strips incoming credentials, adds the configured Logfire token, and limits request bodies to 50 MB by default.

For Starlette, mount the same handler as a route:

```py title="main.py" skip-run="true" skip-reason="server-start"
from starlette.applications import Starlette
from starlette.routing import Route

import logfire
from logfire.experimental.forwarding import logfire_proxy

logfire.configure()

app = Starlette(
routes=[
Route('/logfire-proxy/{path:path}', logfire_proxy, methods=['POST']),
],
)
```

For Django, Flask, Litestar, or a custom HTTP server, use `forward_export_request` directly:

```py title="main.py" skip-run="true" skip-reason="server-start"
import logfire
from logfire.experimental.forwarding import forward_export_request

logfire.configure()


def my_custom_proxy_route(request):
response = forward_export_request(
path=request.path.removeprefix('/logfire-proxy'),
headers=request.headers,
body=request.read(),
)
# Replace CustomFrameworkResponse with your framework's response class.
return CustomFrameworkResponse(
content=response.content,
status_code=response.status_code,
headers=response.headers,
)
```

Protect this endpoint in production. Any client that can reach it can send telemetry to your Logfire project.

## Shutdown

`configure()` returns an async shutdown function:
Expand Down
38 changes: 37 additions & 1 deletion docs/packages/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,24 @@ Enable Node.js compatibility in Wrangler:
}
```

For `wrangler.toml`, use:

```toml title="wrangler.toml"
compatibility_flags = ["nodejs_compat"]
```

For local development, set a write token in `.dev.vars`:

```bash title=".dev.vars"
LOGFIRE_TOKEN=your-write-token
LOGFIRE_ENVIRONMENT=development
```

For production, store `LOGFIRE_TOKEN` as a Worker secret.
`LOGFIRE_ENVIRONMENT` is optional and names the environment on emitted telemetry. For production, store `LOGFIRE_TOKEN` as a Worker secret:

```bash
npx wrangler secret put LOGFIRE_TOKEN
```

## In-Process Instrumentation

Expand Down Expand Up @@ -63,6 +73,32 @@ The package also supports Cloudflare Tail Worker flows:

See `examples/cf-producer-worker` and `examples/cf-tail-worker` in this repository for the full wiring.

## Vitest

If you test Workers with Vitest, ensure the Workers test pool can optimize the package for module loading:

```ts title="vitest.config.mts"
Comment thread
petyosi marked this conversation as resolved.
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'

export default defineWorkersConfig({
test: {
deps: {
optimizer: {
ssr: {
enabled: true,
include: ['@pydantic/logfire-cf-workers'],
},
},
},
poolOptions: {
workers: {
// Your Workers test configuration.
},
},
},
})
```

## Scrubbing

Pass `scrubbing` to control sensitive data scrubbing before telemetry is sent:
Expand Down
Loading