Metadata-Version: 2.4
Name: cf-logs
Version: 0.2.0
Summary: CLI for querying Cloudflare Workers Observability logs
Project-URL: Repository, https://github.com/asyschikov/cf-logs
Project-URL: Issues, https://github.com/asyschikov/cf-logs/issues
Author: Andrey Syschikov
License-Expression: MIT
License-File: LICENSE
Keywords: cli,cloudflare,logs,observability,workers
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Logging
Requires-Python: >=3.13
Requires-Dist: click>=8.3.1
Requires-Dist: httpx>=0.28.1
Requires-Dist: pydantic>=2.12.5
Requires-Dist: python-dotenv>=1.2.2
Description-Content-Type: text/markdown

# cf-logs

CLI for querying Cloudflare Workers Observability logs.

## Install

```bash
pip install cf-logs
```

Or run directly without installing:

```bash
uvx cf-logs events -s 1h
```

## Setup

Set your Cloudflare credentials (or create a `.env` file):

```bash
export CF_ACCOUNT_ID="your-account-id"
export CF_API_TOKEN="your-api-token"
```

The API token needs the **Workers Observability Read** permission (and **Write** for managing destinations).

## Usage

```
cf-logs COMMAND [OPTIONS]
```

Run `cf-logs COMMAND --help` for detailed usage and examples.

### Commands

| Command | Description |
|---------|-------------|
| `events` | Fetch individual log events |
| `query` | Run aggregate queries (counts, percentiles, etc.) |
| `invocations` | Fetch logs grouped by request |
| `tail` | Live-tail logs (like `tail -f`) |
| `keys` | List available field keys |
| `values` | List unique values for a field |
| `destinations` | Manage OpenTelemetry export destinations |

## Preset filters

The `events` command has built-in presets for common debugging scenarios via `-p`:

| Preset | What it filters |
|--------|----------------|
| `errors` | Log level = error |
| `exceptions` | Unhandled exceptions (outcome = exception) |
| `5xx` | HTTP 5xx responses |
| `4xx` | HTTP 4xx responses |
| `slow` | Slow requests (wall time >= 1s) |
| `cold` | Cold start requests |
| `cpu` | Exceeded CPU or memory limits |

```bash
cf-logs events -p errors                  # all errors in last hour
cf-logs events -p 5xx -w my-api -s 24h    # 5xx for a worker, last 24h
cf-logs events -p slow -j                 # slow requests as JSON
cf-logs events -p exceptions -n "db"      # exceptions mentioning "db"
```

Presets combine with other filters, so `-p 5xx -w my-api -n "timeout"` all work together.

## Common scenarios

### Something is broken — what's happening?

```bash
# Quick look at errors
cf-logs events -p errors -w my-api

# Are there unhandled exceptions?
cf-logs events -p exceptions -w my-api -s 30m

# What's the error rate?
cf-logs query -c COUNT -g '$workers.outcome' -w my-api -s 1h
```

### Investigating slow responses

```bash
# Find slow requests
cf-logs events -p slow -w my-api

# P99 and P50 latency
cf-logs query -c P99:'$workers.wallTimeMs' -c P50:'$workers.wallTimeMs' -w my-api -s 6h

# Latency by endpoint
cf-logs query -c P99:'$workers.wallTimeMs' -g '$workers.event.request.url' -w my-api
```

### Monitoring a deploy

```bash
# Tail logs in real time
cf-logs tail -w my-api

# Tail only errors
cf-logs tail -w my-api -f '$metadata.level=error'

# Watch for 5xx
cf-logs tail -w my-api -f '$workers.event.response.status>=500' -i 2
```

### Understanding traffic patterns

```bash
# Requests per worker
cf-logs query -c COUNT -g '$workers.scriptName' -s 24h

# Status code distribution
cf-logs query -c COUNT -g '$workers.event.response.status' -w my-api

# Traffic over time (5-min buckets)
cf-logs query -c COUNT --granularity 300 -w my-api -s 6h -j
```

### Tracing a specific request

```bash
# View all logs for recent invocations
cf-logs invocations -w my-api -s 15m

# Find failed invocations
cf-logs invocations -f '$workers.outcome=exception' -s 1h
```

## Examples

### Fetch recent events

```bash
# Last hour of events (default)
cf-logs events

# Last 24 hours for a specific worker
cf-logs events -s 24h -w my-api

# Events with 5xx status codes
cf-logs events -f '$workers.event.response.status>=500'

# Search for "timeout" in log messages
cf-logs events -n "timeout"

# Exceptions or errors (OR filter)
cf-logs events -f '$workers.outcome=exception' -f '$metadata.level=error' --or

# Raw JSON output for piping to jq
cf-logs events -w my-api -j | jq '.events.events[].["$metadata"].message'
```

### Aggregate queries

```bash
# Total event count (default)
cf-logs query

# Count events per worker
cf-logs query -c COUNT -g '$workers.scriptName'

# P99 latency over the last 24 hours
cf-logs query -c P99:'$workers.wallTimeMs' -s 24h

# Average CPU time per worker, sorted descending
cf-logs query -c AVG:'$workers.cpuTimeMs' -g '$workers.scriptName' -o AVG:desc

# Error rate by status code
cf-logs query -c COUNT -g '$workers.event.response.status' -w my-api

# Multiple calculations at once
cf-logs query -c COUNT -c P50:'$workers.wallTimeMs' -c P99:'$workers.wallTimeMs'

# Time-series with 5-minute buckets
cf-logs query -c COUNT -g '$workers.outcome' --granularity 300 -j

# Only show groups with more than 100 events
cf-logs query -c COUNT -g '$workers.scriptName' -H 'COUNT>100'
```

### Live tail

```bash
# Tail all logs
cf-logs tail

# Tail a specific worker
cf-logs tail -w my-api

# Tail errors only, poll every 2 seconds
cf-logs tail -f '$metadata.level=error' -i 2

# Tail as JSON for piping
cf-logs tail -w my-api -j | jq '.["$metadata"].message'
```

### Discover fields and values

```bash
# List all available field keys
cf-logs keys

# Search for keys related to "status"
cf-logs keys -S status

# List all worker names
cf-logs values '$workers.scriptName'

# List all outcomes for a worker
cf-logs values '$workers.outcome' -w my-api

# List HTTP status codes (numeric field)
cf-logs values '$workers.event.response.status' -t number
```

### Manage export destinations

```bash
# List configured destinations
cf-logs destinations list

# Create an OTLP destination
cf-logs destinations create \
  --name my-collector \
  --type otlp \
  --endpoint https://otel.example.com/v1/traces \
  -H 'Authorization=Bearer token123'

# Disable a destination
cf-logs destinations update my-collector-slug --disabled

# Delete a destination
cf-logs destinations delete my-collector-slug -y
```

## Filter syntax

Filters are passed with `-f` and support these operators:

| Syntax | Operator |
|--------|----------|
| `key=value` | equals |
| `key!=value` | not equals |
| `key>value` | greater than |
| `key>=value` | greater than or equal |
| `key<value` | less than |
| `key<=value` | less than or equal |
| `key~=pattern` | regex match |
| `key::value` | contains substring |
| `key!:value` | does not contain |
| `key^=value` | starts with |
| `key@=a,b,c` | value in list |
| `key@!=a,b,c` | value not in list |
| `key?` | field exists |
| `key!?` | field is null |

Multiple `-f` flags are combined with AND by default. Use `--or` to combine with OR.

## Common fields

| Field | Description |
|-------|-------------|
| `$workers.scriptName` | Worker script name |
| `$workers.outcome` | `ok`, `exception`, `exceededCpu`, `exceededMemory`, `canceled` |
| `$workers.wallTimeMs` | Wall clock time in ms |
| `$workers.cpuTimeMs` | CPU time in ms |
| `$workers.eventType` | `fetch`, `scheduled`, `cron`, `queue`, `alarm`, `email`, `rpc` |
| `$workers.executionModel` | `stateless` or `durableObject` |
| `$workers.event.response.status` | HTTP response status code |
| `$metadata.level` | `log`, `debug`, `info`, `warn`, `error` |
| `$metadata.message` | Log message text |
| `$metadata.requestId` | Request ID |
| `$metadata.traceId` | Trace ID |

## Calculation operators

Used with `-c` in the `query` command:

`COUNT`, `UNIQ`, `AVG`, `SUM`, `MIN`, `MAX`, `MEDIAN`, `STDDEV`, `VARIANCE`, `P01`, `P05`, `P10`, `P25`, `P75`, `P90`, `P95`, `P99`, `P999`

Format: `OPERATOR` (for count) or `OPERATOR:field` (for field aggregations).

## Time formats

The `--since` and `--until` options accept:

- **Durations**: `30s`, `5m`, `1h`, `7d`, `2w`, `1h30m`
- **ISO datetimes**: `2024-01-15T10:00:00`
