# Cloudflare Logs

Stream Worker invocation traces into Gonzo with a `jq` normalizer. Cloudflare's `wrangler tail` streams pretty-printed JSON envelopes over WebSocket — one per invocation, containing all `console.log` calls and exceptions. The normalizer flattens these into per-line JSONL that Gonzo can parse.

#### Quick Start

```bash
wrangler tail --format json \
  | jq --unbuffered -c '
    . as $inv |
    (.logs[] | {
      timestamp: .timestamp,
      level: .level,
      message: (.message | map(if type == "string" then . else tostring end) | join(" ")),
      script: $inv.scriptName,
      outcome: $inv.outcome,
      url: $inv.event.request.url,
      method: $inv.event.request.method,
      status: ($inv.event.response.status // null),
      colo: $inv.event.request.cf.colo
    }),
    (.exceptions[]? | {
      timestamp: .timestamp,
      level: "error",
      message: (.name + ": " + .message),
      stack: .stack,
      script: $inv.scriptName,
      outcome: $inv.outcome,
      url: $inv.event.request.url,
      method: $inv.event.request.method,
      status: ($inv.event.response.status // null)
    })
  ' \
  | gonzo
```

The `--unbuffered` flag on `jq` is required — without it, output buffers and logs stall in the pipe.

#### Prerequisites

* Gonzo installed
* [Wrangler](https://developers.cloudflare.com/workers/wrangler/) installed (`npm install -g wrangler`)
* `jq` installed (`brew install jq`)
* A Cloudflare account with at least one deployed Worker

#### What's Visible

| Component                     | In tail? | Notes                                     |
| ----------------------------- | -------- | ----------------------------------------- |
| `console.log/warn/error`      | ✅ Yes    | All levels: log, warn, error, debug, info |
| Uncaught exceptions           | ✅ Yes    | With stack traces                         |
| Request/response metadata     | ✅ Yes    | URL, method, status, colo                 |
| Cron trigger invocations      | ✅ Yes    | `url`/`method`/`status` will be null      |
| D1 / KV / R2 / AI / Vectorize | ❌ No     | Instrument manually with `console.log`    |

#### Usage Patterns

**Real-time streaming** (WebSocket, default):

```bash
wrangler tail --format json | jq --unbuffered -c '<normalizer>' | gonzo
```

**Tail a specific Worker by name:**

```bash
wrangler tail my-worker --format json | jq --unbuffered -c '<normalizer>' | gonzo
```

**Filter by status:**

```bash
wrangler tail --format json --status error | jq --unbuffered -c '<normalizer>' | gonzo
```

**Filter by HTTP method:**

```bash
wrangler tail --format json --method GET | jq --unbuffered -c '<normalizer>' | gonzo
```

**Filter by search string:**

```bash
wrangler tail --format json --search "TypeError" | jq --unbuffered -c '<normalizer>' | gonzo
```

**Sample high-traffic Workers:**

```bash
wrangler tail --format json --sampling-rate 0.1 | jq --unbuffered -c '<normalizer>' | gonzo
```

**Capture to file for replay:**

```bash
wrangler tail --format json | jq --unbuffered -c '<normalizer>' > /tmp/cf-logs.jsonl &
gonzo -f /tmp/cf-logs.jsonl --follow
```

**With local AI (logs never leave your machine):**

```bash
export OPENAI_API_KEY="ollama"
export OPENAI_API_BASE="http://localhost:11434"
wrangler tail --format json | jq --unbuffered -c '<normalizer>' | gonzo
```

#### Pages Functions

Pages Functions use the same Worker runtime and produce identical tail output. The normalizer works without modification.

```bash
# Tail latest production deployment
wrangler pages deployment tail --project-name my-site --environment production --format json \
  | jq --unbuffered -c '<normalizer>' \
  | gonzo
```

When piping (non-interactive mode), the `--environment` flag or a deployment ID is required. Static asset requests don't trigger Functions and won't appear in the tail.

#### Rate Limits and Sampling

**Streaming** (`wrangler tail`) uses WebSocket. For high-traffic Workers, Cloudflare may auto-sample, dropping some invocation traces. Use `--sampling-rate` (0.0–1.0) to proactively reduce volume.

**Concurrent sessions:** Maximum 10 tail sessions per Worker.

There is no fetch mode with `--lines` or `--since`. `wrangler tail` is streaming only. For historical queries, use [Workers Logs](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) in the Cloudflare dashboard.

#### Structured Logging Tips

Emit structured JSON via `console.log` for the best Gonzo experience. Objects passed directly also work — the normalizer stringifies them.

```javascript
// Explicit stringify — cleanest message field
console.log(JSON.stringify({
  message: "User signup completed",
  level: "info",
  userId: 123,
  duration_ms: 45
}));

// Direct object — faster to write
console.log({ event: "db_write", table: "users", rows: 1 });
```

#### Troubleshooting

| Symptom                              | Cause & fix                                                                                                 |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Logs not appearing                   | Missing `--unbuffered` on `jq`. Without it, jq buffers output and logs stall.                               |
| "Could not create tail"              | Hit the 10 concurrent tail session limit. Close other `wrangler tail` sessions or dashboard live log views. |
| Cron logs show null fields           | Expected. Cron triggers have no HTTP request — `url`, `method`, `status`, `colo` are null.                  |
| D1/KV operations missing             | Binding operations don't produce tail events. Add `console.log` around binding calls.                       |
| Pretty-printed JSON instead of JSONL | Don't pipe raw `wrangler tail` output to Gonzo. The `jq` normalizer compacts it.                            |
| Out-of-order logs under concurrency  | Workers execute at edge locations globally. Concurrent requests may arrive out of timestamp order.          |

***

**Time to complete:** 5 minutes **Prerequisites:** Wrangler, jq, Gonzo installed **Full guide:** [`guides/CLOUDFLARE_USAGE_GUIDE.md`](https://github.com/control-theory/gonzo/blob/main/guides/CLOUDFLARE_USAGE_GUIDE.md)
