# 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.