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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.controltheory.com/controltheory-documentation/gonzo-docs/integration-examples/cloudflare-logs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
