Logstreem
Cloudflare Workers

Cloudflare Workers

Tail Workers for real-time logs, or Logpush for zone-wide HTTP data.

Two options. A Tail Worker gets you a Worker's own console output within a second; Logpush gets you every HTTP request across a zone in batches.

Tail Worker

export default {
  async tail(events: TraceItem[], env: Env) {
    const rows = events.flatMap((e) =>
      e.logs.map((log) => ({
        _time: log.timestamp,
        level: log.level,
        script: e.scriptName,
        outcome: e.outcome,
        message: log.message.join(" "),
        ...(e.event && "request" in e.event
          ? { url: e.event.request.url, method: e.event.request.method }
          : {}),
      })),
    );

    if (!rows.length) return;

    await fetch("https://api.logstreem.com/v1/ingest/cloudflare-workers", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.LOGSTREEM_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(rows),
    });
  },
};

Logpush

cURL
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/logpush/jobs" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "logstreem-http",
    "dataset": "http_requests",
    "destination_conf": "https://api.logstreem.com/v1/ingest/cloudflare-http?header_Authorization=Bearer%20'"$LOGSTREEM_TOKEN"'",
    "output_options": {
      "field_names": ["EdgeStartTimestamp","ClientIP","ClientRequestHost","ClientRequestMethod","ClientRequestURI","EdgeResponseStatus","EdgeTimeToFirstByteMs","RayID"],
      "timestamp_format": "rfc3339"
    },
    "enabled": true
  }'

Different latency, different job

Tail Workers are near-real-time and Worker-scoped — right for debugging. Logpush batches every few minutes and is zone-wide — right for traffic analysis. Most teams run both, into different datasets.