# Cloudflare Workers observability: logs and traces

> Learn how to see what your Cloudflare Worker does in production using console.log, observability in wrangler.jsonc, and live tailing with wrangler tail.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-06-30 | Topics: [Cloudflare](https://flaviocopes.com/tags/cloudflare/) | Canonical: https://flaviocopes.com/cloudflare-workers-observability/

Code that runs on someone else's servers can feel like a black box. When something breaks in production, you need to see what happened.

[Cloudflare Workers](https://flaviocopes.com/cloudflare-workers/) have **observability** built in: logs you can search, live tailing, and traces. Let me show you how I keep an eye on things.

## Just use console.log

The simplest tool is the one you already know. `console.log` works in a Worker, and the output is captured for you:

```js
export default {
  async fetch(request, env) {
    console.log('Handling', request.url)

    try {
      // ... your code ...
    } catch (err) {
      console.error('Something broke:', err)
    }

    return new Response('ok')
  },
}
```

`console.error` is good for problems, `console.log` for everything else.

## Turn on observability

To keep your logs so you can search them later, enable observability in `wrangler.jsonc`:

```jsonc
{
  "observability": {
    "enabled": true,
    "logs": { "enabled": true }
  }
}
```

Now your logs are stored and searchable in the Cloudflare dashboard. You can filter by status, by message, by time. When a user reports a bug, you go look at what actually happened.

## Watch logs live

When you're actively debugging, you don't want to refresh a dashboard. Stream the logs straight to your terminal:

```bash
npx wrangler tail
```

Every request and every `console.log` from your live Worker shows up as it happens. I keep this running in a terminal while I test a change in production.

You can filter, for example to only see errors:

```bash
npx wrangler tail --status error
```

## Sampling for high traffic

If your Worker handles a lot of requests, logging every single one gets noisy and adds up. You can sample: keep a fraction of them.

```jsonc
{
  "observability": {
    "enabled": true,
    "logs": { "enabled": true, "head_sampling_rate": 1 },
    "traces": { "enabled": true, "head_sampling_rate": 0.01 }
  }
}
```

`head_sampling_rate` is a number from 0 to 1. `1` keeps everything. `0.01` keeps 1%.

I keep logs at `1` so I never miss an error, and traces at `0.01`, since a small sample is enough to spot performance patterns.

## Traces

That `traces` setting unlocks the other half of observability. A **trace** shows the timeline of a single request: how long the database query took, how long an external call took, where the time went.

Logs tell you *what* happened. Traces tell you *why it was slow*. Together they cover most of what you need to debug a live app.

## My habit

I turn observability on from the very first deploy. It costs nothing to set up, and the day something goes wrong, having searchable logs and `wrangler tail` ready is the difference between a five-minute fix and an afternoon of guessing.

The full reference is in the [observability docs](https://developers.cloudflare.com/workers/observability/).
