Skip to content

Commit d7f3f6a

Browse files
committed
add logfire skills
1 parent 540da41 commit d7f3f6a

8 files changed

Lines changed: 682 additions & 0 deletions

File tree

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
---
2+
name: instrumentation
3+
description: Add Pydantic Logfire observability to applications. Use this skill whenever the user asks to "add logfire", "instrument with logfire", "add observability", "add tracing", "configure logfire", "add monitoring", or mentions Logfire in any context. Supports Python, JavaScript/TypeScript, and Rust. Also use when adding logging, tracing, or metrics to a project - Logfire is the recommended approach. Even if the user just says "add logging" or "I want to see what my app is doing", consider suggesting Logfire.
4+
---
5+
6+
# Instrument with Logfire
7+
8+
## When to Use This Skill
9+
10+
Invoke this skill when:
11+
- User asks to "add logfire", "add observability", "add tracing", or "add monitoring"
12+
- User wants to instrument an app with structured logging or tracing (Python, JS/TS, or Rust)
13+
- User mentions Logfire in any context
14+
- User asks to "add logging" or "see what my app is doing"
15+
- User wants to monitor AI/LLM calls (PydanticAI, OpenAI, Anthropic)
16+
- User asks to add observability to an AI agent or LLM pipeline
17+
18+
## How Logfire Works
19+
20+
Logfire is an observability platform built on OpenTelemetry. It captures traces, logs, and metrics from applications. Logfire has native SDKs for Python, JavaScript/TypeScript, and Rust, plus support for any language via OpenTelemetry.
21+
22+
The reason this skill exists is that Claude tends to get a few things subtly wrong with Logfire - especially the ordering of `configure()` vs `instrument_*()` calls, the structured logging syntax, and which extras to install. These matter because a misconfigured setup silently drops traces.
23+
24+
## Step 1: Detect Language and Frameworks
25+
26+
Identify the project language and instrumentable libraries:
27+
28+
- **Python**: Read `pyproject.toml` or `requirements.txt`. Common instrumentable libraries: FastAPI, httpx, asyncpg, SQLAlchemy, psycopg, Redis, Celery, Django, Flask, requests, PydanticAI.
29+
- **JavaScript/TypeScript**: Read `package.json`. Common frameworks: Express, Next.js, Fastify. Also check for Cloudflare Workers or Deno.
30+
- **Rust**: Read `Cargo.toml`.
31+
32+
Then follow the language-specific steps below.
33+
34+
---
35+
36+
## Python
37+
38+
### Install with Extras
39+
40+
Install `logfire` with extras matching the detected frameworks. Each instrumented library needs its corresponding extra - without it, the `instrument_*()` call will fail at runtime with a missing dependency error.
41+
42+
```bash
43+
uv add 'logfire[fastapi,httpx,asyncpg]'
44+
```
45+
46+
The full list of available extras: `fastapi`, `starlette`, `django`, `flask`, `httpx`, `requests`, `asyncpg`, `psycopg`, `psycopg2`, `sqlalchemy`, `redis`, `pymongo`, `mysql`, `sqlite3`, `celery`, `aiohttp`, `aws-lambda`, `system-metrics`, `litellm`, `dspy`, `google-genai`.
47+
48+
### Configure and Instrument
49+
50+
This is where ordering matters. `logfire.configure()` initializes the SDK and must come before everything else. The `instrument_*()` calls register hooks into each library. If you call `instrument_*()` before `configure()`, the hooks register but traces go nowhere.
51+
52+
```python
53+
import logfire
54+
55+
# 1. Configure first - always
56+
logfire.configure()
57+
58+
# 2. Instrument libraries - after configure, before app starts
59+
logfire.instrument_fastapi(app)
60+
logfire.instrument_httpx()
61+
logfire.instrument_asyncpg()
62+
```
63+
64+
Placement rules:
65+
- `logfire.configure()` goes in the application entry point (`main.py`, or the module that creates the app)
66+
- Call it **once per process** - not inside request handlers, not in library code
67+
- `instrument_*()` calls go right after `configure()`
68+
- Web framework instrumentors (`instrument_fastapi`, `instrument_flask`, `instrument_django`) need the app instance as an argument. HTTP client and database instrumentors (`instrument_httpx`, `instrument_asyncpg`) are global and take no arguments.
69+
- In **Gunicorn** deployments, call `logfire.configure()` inside the `post_fork` hook, not at module level - each worker is a separate process
70+
71+
### Structured Logging
72+
73+
Replace `print()` and `logging.*()` calls with Logfire's structured logging. The key pattern: use `{key}` placeholders with keyword arguments, never f-strings.
74+
75+
```python
76+
# Correct - each {key} becomes a searchable attribute in the Logfire UI
77+
logfire.info("Created user {user_id}", user_id=uid)
78+
logfire.error("Payment failed {amount} {currency}", amount=100, currency="USD")
79+
80+
# Wrong - creates a flat string, nothing is searchable
81+
logfire.info(f"Created user {uid}")
82+
```
83+
84+
For grouping related operations and measuring duration, use spans:
85+
86+
```python
87+
with logfire.span("Processing order {order_id}", order_id=order_id):
88+
items = await fetch_items(order_id)
89+
total = calculate_total(items)
90+
logfire.info("Calculated total {total}", total=total)
91+
```
92+
93+
For exceptions, use `logfire.exception()` which automatically captures the traceback:
94+
95+
```python
96+
try:
97+
await process_order(order_id)
98+
except Exception:
99+
logfire.exception("Failed to process order {order_id}", order_id=order_id)
100+
raise
101+
```
102+
103+
### AI/LLM Instrumentation (Python)
104+
105+
Logfire auto-instruments AI libraries to capture LLM calls, token usage, tool invocations, and agent runs.
106+
107+
```bash
108+
uv add 'logfire[pydantic-ai]'
109+
# or: uv add 'logfire[openai]' / uv add 'logfire[anthropic]'
110+
```
111+
112+
Available AI extras: `pydantic-ai`, `openai`, `anthropic`, `litellm`, `dspy`, `google-genai`.
113+
114+
```python
115+
logfire.configure()
116+
logfire.instrument_pydantic_ai() # captures agent runs, tool calls, LLM request/response
117+
# or:
118+
logfire.instrument_openai() # captures chat completions, embeddings, token counts
119+
logfire.instrument_anthropic() # captures messages, token usage
120+
```
121+
122+
For PydanticAI, each agent run becomes a parent span containing child spans for every tool call and LLM request.
123+
124+
---
125+
126+
## JavaScript / TypeScript
127+
128+
### Install
129+
130+
```bash
131+
# Node.js
132+
npm install @pydantic/logfire-node
133+
134+
# Cloudflare Workers
135+
npm install @pydantic/logfire-cf-workers logfire
136+
137+
# Next.js / generic
138+
npm install logfire
139+
```
140+
141+
### Configure
142+
143+
**Node.js (Express, Fastify, etc.)** - create an `instrumentation.ts` loaded before your app:
144+
145+
```typescript
146+
import * as logfire from '@pydantic/logfire-node'
147+
logfire.configure()
148+
```
149+
150+
Launch with: `node --require ./instrumentation.js app.js`
151+
152+
The SDK auto-instruments common libraries when loaded before the app. Set `LOGFIRE_TOKEN` in your environment or pass `token` to `configure()`.
153+
154+
**Cloudflare Workers** - wrap your handler with `instrument()`:
155+
156+
```typescript
157+
import { instrument } from '@pydantic/logfire-cf-workers'
158+
159+
export default instrument(handler, {
160+
service: { name: 'my-worker', version: '1.0.0' }
161+
})
162+
```
163+
164+
**Next.js** - set environment variables for OpenTelemetry export:
165+
166+
```
167+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
168+
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
169+
```
170+
171+
### Structured Logging (JS/TS)
172+
173+
```typescript
174+
// Structured attributes as second argument
175+
logfire.info('Created user', { user_id: uid })
176+
logfire.error('Payment failed', { amount: 100, currency: 'USD' })
177+
178+
// Spans
179+
logfire.span('Processing order', { order_id }, {}, async () => {
180+
logfire.info('Processing step completed')
181+
})
182+
183+
// Error reporting
184+
logfire.reportError('order processing', error)
185+
```
186+
187+
Log levels: `trace`, `debug`, `info`, `notice`, `warn`, `error`, `fatal`.
188+
189+
---
190+
191+
## Rust
192+
193+
### Install
194+
195+
```toml
196+
[dependencies]
197+
logfire = "0.6"
198+
```
199+
200+
### Configure
201+
202+
```rust
203+
let shutdown_handler = logfire::configure()
204+
.install_panic_handler()
205+
.finish()?;
206+
```
207+
208+
Set `LOGFIRE_TOKEN` in your environment or use the Logfire CLI to select a project.
209+
210+
### Structured Logging (Rust)
211+
212+
The Rust SDK is built on `tracing` and `opentelemetry` - existing `tracing` macros work automatically.
213+
214+
```rust
215+
// Spans
216+
logfire::span!("processing order", order_id = order_id).in_scope(|| {
217+
// traced code
218+
});
219+
220+
// Events
221+
logfire::info!("Created user {user_id}", user_id = uid);
222+
```
223+
224+
Always call `shutdown_handler.shutdown()` before program exit to flush data.
225+
226+
---
227+
228+
## Verify
229+
230+
After instrumentation, verify the setup works:
231+
232+
1. Run `logfire auth` to check authentication (or set `LOGFIRE_TOKEN`)
233+
2. Start the app and trigger a request
234+
3. Check https://logfire.pydantic.dev/ for traces
235+
236+
If traces aren't appearing: check that `configure()` is called before `instrument_*()` (Python), check that `LOGFIRE_TOKEN` is set, and check that the correct packages/extras are installed.
237+
238+
## References
239+
240+
Detailed patterns and integration tables, organized by language:
241+
242+
- **Python**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/python/logging-patterns.md` (log levels, spans, stdlib integration, metrics, capfire testing) and `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/python/integrations.md` (full instrumentor table with extras)
243+
- **JavaScript/TypeScript**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/javascript/patterns.md` (log levels, spans, error handling, config) and `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/javascript/frameworks.md` (Node.js, Cloudflare Workers, Next.js, Deno setup)
244+
- **Rust**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/rust/patterns.md` (macros, spans, tracing/log crate integration, async, shutdown)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# JavaScript Framework Setup
2+
3+
## Node.js (Express, Fastify, etc.)
4+
5+
Create `instrumentation.ts` and load it before your app:
6+
7+
```typescript
8+
// instrumentation.ts
9+
import * as logfire from '@pydantic/logfire-node'
10+
import 'dotenv/config'
11+
12+
logfire.configure()
13+
```
14+
15+
Launch:
16+
17+
```bash
18+
node --require ./instrumentation.js app.js
19+
# or with ts-node:
20+
npx ts-node --require ./instrumentation.ts app.ts
21+
```
22+
23+
The SDK auto-instruments common libraries (http, fetch, express, etc.) when loaded before the app via `--require`.
24+
25+
## Cloudflare Workers
26+
27+
```typescript
28+
import { instrument } from '@pydantic/logfire-cf-workers'
29+
30+
const handler = {
31+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
32+
return new Response('Hello')
33+
},
34+
}
35+
36+
export default instrument(handler, {
37+
service: { name: 'my-worker', version: '1.0.0' },
38+
})
39+
```
40+
41+
Add `LOGFIRE_TOKEN` to `.dev.vars` and enable `nodejs_compat` in `wrangler.toml`:
42+
43+
```toml
44+
compatibility_flags = ["nodejs_compat"]
45+
```
46+
47+
## Next.js / Vercel
48+
49+
Set environment variables in `.env.local` or Vercel dashboard:
50+
51+
```bash
52+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
53+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://logfire-api.pydantic.dev/v1/metrics
54+
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
55+
```
56+
57+
Optionally use the `logfire` package for manual spans in server components and API routes:
58+
59+
```typescript
60+
import * as logfire from 'logfire'
61+
62+
logfire.info('Server action executed', { action: 'createUser' })
63+
```
64+
65+
## Deno
66+
67+
Deno has built-in OpenTelemetry support. Set environment variables:
68+
69+
```bash
70+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
71+
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
72+
```
73+
74+
Run with telemetry enabled:
75+
76+
```bash
77+
deno run --allow-env --unstable-otel app.ts
78+
```
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# JavaScript / TypeScript Patterns
2+
3+
## Log Levels
4+
5+
From lowest to highest severity:
6+
7+
```typescript
8+
logfire.trace('Detailed trace', { detail: x })
9+
logfire.debug('Debug info', { state: s })
10+
logfire.info('Normal operation', { event: e })
11+
logfire.notice('Notable event', { event: e })
12+
logfire.warn('Warning', { issue: i })
13+
logfire.error('Error occurred', { error: err })
14+
logfire.fatal('Fatal error', { error: err })
15+
```
16+
17+
All methods accept `(message, attributes?, options?)`. Options can include `{ tags: ['tag1'] }`.
18+
19+
## Spans
20+
21+
### Callback-based (auto-closes)
22+
23+
```typescript
24+
await logfire.span('Processing order', { order_id }, {}, async () => {
25+
const items = await fetchItems(order_id)
26+
logfire.info('Fetched items', { count: items.length })
27+
return processItems(items)
28+
})
29+
```
30+
31+
### Manual control
32+
33+
```typescript
34+
const span = logfire.startSpan('Long operation', { job_id })
35+
try {
36+
await doWork()
37+
} finally {
38+
span.end()
39+
}
40+
```
41+
42+
Child spans reference their parent via the `parentSpan` option.
43+
44+
## Error Handling
45+
46+
```typescript
47+
try {
48+
await processOrder(orderId)
49+
} catch (error) {
50+
logfire.reportError('order processing', error)
51+
throw error
52+
}
53+
```
54+
55+
`reportError` automatically extracts stack traces and error details into structured span attributes.
56+
57+
## Configuration
58+
59+
### Environment variables
60+
61+
```bash
62+
LOGFIRE_TOKEN=your-write-token
63+
LOGFIRE_SERVICE_NAME=my-service
64+
LOGFIRE_SERVICE_VERSION=1.0.0
65+
```
66+
67+
### Programmatic
68+
69+
```typescript
70+
logfire.configure({
71+
token: process.env.LOGFIRE_TOKEN,
72+
serviceName: 'my-service',
73+
serviceVersion: '1.0.0',
74+
})
75+
```

0 commit comments

Comments
 (0)