# Logs

The fields of a GoRunner log line, what each level means, and what is scrubbed first.

Each container writes its lines to its output (`docker compose logs`), and to your OTLP endpoint when one is set. `LOG_LEVEL` sets how much; `LOG_FORMAT` sets the shape.

## The shape

`text`, the default, is `key=value`. `json` is one object per line, for a log collector:

`LOG_FORMAT=json`

```
{"time":"2026-10-03T10:02:14.311Z","level":"WARN","msg":"step failed","step":"notify_sales","piece":"slack","error":"channel_not_found","run_id":"0b7c…","workflow_id":"19a4…","org_id":"7f3e…","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7"}
```

Over OTLP, each line is a log record with its level as the severity, its fields as attributes, and its trace and span ids set.

## The fields

Every line has `time`, `level` and `msg`. Then, where they apply:

- `request_id`: The API request, also returned as `X-Request-ID`, and carried into the queue and the worker.
- `org_id`, `workspace_id`: The organization and workspace.
- `workflow_id`, `run_id`: On every line a run writes.
- `step`, `piece`: The step’s name and its app, on every line a step writes.
- `trace_id`, `span_id`: While tracing is on.
- `component`: The part that wrote it, when not obvious: `telemetry`, `opentelemetry`, `browser`.
- `error`: What went wrong, scrubbed.

The API writes one `http request` line per request, with `method`, `route` (the pattern), `status` and `duration_ms`, plus `client_request_id` when the caller sent its own `X-Request-ID`. A browser error from the dashboard is a `WARN` with `component=browser`. Lines don’t name the process; Docker prefixes each with its container.

## Levels

- `ERROR`: GoRunner itself is failing, and someone must act: a refused write, a 5xx, a crash. Also sent to Sentry.
- `WARN`: Something slowed or retried, or a workflow failed: its author fixes that, not you.
- `INFO`: A process started; a run started and finished.
- `DEBUG`: Detail for an investigation, with `LOG_LEVEL=debug`.

The level changes on the [Monitoring page](https://docs.gorunner.io/self-host/monitoring-page) without a restart, so you can turn on `debug` while you look.

## What is scrubbed

Before a line is written anywhere, these become `[redacted]`: bearer and Basic credentials, GoRunner’s tokens and signing secrets, provider API keys, private keys, the password in any address, every query string, and any field named like a secret (`password`, `token`, `cookie`, `api_key`…). A webhook token in a path is cut to 8 characters. Email addresses stay: they say who did something, and grant nothing.

A monitoring connection that keeps failing says so at most once a minute, on the container output only: never through the connection that fails.

Related: [Metrics](https://docs.gorunner.io/self-host/metrics) · [Traces](https://docs.gorunner.io/self-host/traces) · [Logs only](https://docs.gorunner.io/self-host/monitoring-tools#logs)

Source: https://docs.gorunner.io/self-host/logs
