Curated Skills
by lstudlo

cloudflare

references/tail-workers/patterns.md

.md 181 lines
Content
# 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.