mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 06:11:27 -07:00
181 lines
3.9 KiB
Markdown
181 lines
3.9 KiB
Markdown
# Tail Workers Common Patterns
|
|
|
|
## Community Libraries
|
|
|
|
While most tail Worker implementations are custom, these libraries may help:
|
|
|
|
**Logging/Observability:**
|
|
- **Axiom** - `axiom-cloudflare-workers` (npm) - Direct Axiom integration
|
|
- **Baselime** - SDK for Baselime observability platform
|
|
- **LogFlare** - Structured log aggregation
|
|
|
|
**Type Definitions:**
|
|
- **@cloudflare/workers-types** - Official TypeScript types (use `TraceItem`)
|
|
|
|
**Note:** Most integrations require custom tail handler implementation. See integration examples below.
|
|
|
|
## Basic Patterns
|
|
|
|
### HTTP Endpoint Logging
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
const payload = events.map(event => ({
|
|
script: event.scriptName,
|
|
timestamp: event.eventTimestamp,
|
|
outcome: event.outcome,
|
|
url: event.event?.request?.url,
|
|
status: event.event?.response?.status,
|
|
logs: event.logs,
|
|
exceptions: event.exceptions,
|
|
}));
|
|
|
|
ctx.waitUntil(
|
|
fetch(env.LOG_ENDPOINT, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
})
|
|
);
|
|
}
|
|
};
|
|
```
|
|
|
|
### Error Tracking Only
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
const errors = events.filter(e =>
|
|
e.outcome === 'exception' || e.exceptions.length > 0
|
|
);
|
|
|
|
if (errors.length === 0) return;
|
|
|
|
ctx.waitUntil(
|
|
fetch(env.ERROR_ENDPOINT, {
|
|
method: "POST",
|
|
body: JSON.stringify(errors),
|
|
})
|
|
);
|
|
}
|
|
};
|
|
```
|
|
|
|
## Storage Integration
|
|
|
|
### KV Storage with TTL
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
ctx.waitUntil(
|
|
Promise.all(events.map(event =>
|
|
env.LOGS_KV.put(
|
|
`log:${event.scriptName}:${event.eventTimestamp}`,
|
|
JSON.stringify(event),
|
|
{ expirationTtl: 86400 } // 24 hours
|
|
)
|
|
))
|
|
);
|
|
}
|
|
};
|
|
```
|
|
|
|
### Analytics Engine Metrics
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
ctx.waitUntil(
|
|
Promise.all(events.map(event =>
|
|
env.ANALYTICS.writeDataPoint({
|
|
blobs: [event.scriptName, event.outcome],
|
|
doubles: [1, event.event?.response?.status ?? 0],
|
|
indexes: [event.event?.request?.cf?.colo ?? 'unknown'],
|
|
})
|
|
))
|
|
);
|
|
}
|
|
};
|
|
```
|
|
|
|
## Filtering & Routing
|
|
|
|
Filter by route, outcome, or other criteria:
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
// Route filtering
|
|
const apiEvents = events.filter(e =>
|
|
e.event?.request?.url?.includes('/api/')
|
|
);
|
|
|
|
// Multi-destination routing
|
|
const errors = events.filter(e => e.outcome === 'exception');
|
|
const success = events.filter(e => e.outcome === 'ok');
|
|
|
|
const tasks = [];
|
|
if (errors.length > 0) {
|
|
tasks.push(fetch(env.ERROR_ENDPOINT, {
|
|
method: "POST",
|
|
body: JSON.stringify(errors),
|
|
}));
|
|
}
|
|
if (success.length > 0) {
|
|
tasks.push(fetch(env.SUCCESS_ENDPOINT, {
|
|
method: "POST",
|
|
body: JSON.stringify(success),
|
|
}));
|
|
}
|
|
|
|
ctx.waitUntil(Promise.all(tasks));
|
|
}
|
|
};
|
|
```
|
|
|
|
## Sampling
|
|
|
|
Reduce costs by processing only a percentage of events:
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
if (Math.random() > 0.1) return; // 10% sample rate
|
|
ctx.waitUntil(fetch(env.LOG_ENDPOINT, {
|
|
method: "POST",
|
|
body: JSON.stringify(events),
|
|
}));
|
|
}
|
|
};
|
|
```
|
|
|
|
## Advanced Patterns
|
|
|
|
### Batching with Durable Objects
|
|
|
|
Accumulate events before sending:
|
|
|
|
```typescript
|
|
export default {
|
|
async tail(events, env, ctx) {
|
|
const batch = env.BATCH_DO.get(env.BATCH_DO.idFromName("batch"));
|
|
ctx.waitUntil(batch.fetch("https://batch/add", {
|
|
method: "POST",
|
|
body: JSON.stringify(events),
|
|
}));
|
|
}
|
|
};
|
|
```
|
|
|
|
See durable-objects skill for full implementation.
|
|
|
|
### Workers for Platforms
|
|
|
|
Dynamic dispatch sends TWO events per request. Filter by `scriptName` to distinguish dispatch vs user Worker events.
|
|
|
|
### Error Handling
|
|
|
|
Always wrap external calls. See gotchas.md for fallback storage pattern.
|