Skip to main content
How logs are captured, how severity is detected, which logging libraries work out of the box, how long logs are kept, and the known limitations. For metrics and the wider observability overview, see Observability.

How logging works

No SDK or extra library is required:
  • Anything your container writes to stdout or stderr is captured.
  • Each line is parsed, assigned a severity, and appears in the logs explorer within seconds.
  • One line in your container is one entry in the explorer. Multi-line output is split into separate entries (see Limitations).
Find your logs in the Logs tab of the service properties panel. Select a service on the canvas to open it.
The Logs tab open in the service properties panel next to the service node on the canvas
Select Open in Logs explorer for the full view: a histogram of volume by level over time, level facets for filtering, a per-replica toggle, a time-range picker, and a detail panel for individual entries.
Logs explorer showing mixed-level rows with level chips, a volume histogram broken down by level, level facets, and the replica toggle

Structured logging

Plain text gives you logs, but structured logging makes them queryable and correctly leveled. Each line is parsed as JSON. When parsing succeeds, every key becomes a queryable field on the entry, visible in the detail panel. When it fails, the line is kept as a plain-text body.

How severity is detected

Severity is read from the parsed JSON in priority order. The first matching field wins:
Emit only one severity field per line. If more than one is present, the result is undefined. Pick one logger and let it own the field.

Worked examples

Each emits a JSON line that gets parsed and leveled correctly.

Library compatibility

If you use one of these libraries with JSON output enabled, severity is detected out of the box: For any library not listed, emit JSON with a top-level level (string or numeric) or severity_number (1 to 24) and it will be detected.

Retention

How long logs stay in the explorer depends on your plan: Custom retention beyond 7 days is available on Enterprise. See the observability row in the plan comparison for details. Logs older than your retention window are removed from the explorer entirely. There is no archive to recover them from.

Limitations and gotchas

console.warn and console.error are not auto-detected. Plain text only becomes WARN or ERROR when the body contains a word-bounded match for one of: warn, warning, error, err, fatal, panic, exception, critical, crit, debug. So console.warn("user not found") shows up as info. The word boundaries keep phrases like “no errors” from being misclassified. Use a structured logger for reliable levels. A Datadog-style status field is ignored. Many apps log {"status": 500} for an HTTP status or {"status": "error"} for business state, so status is left alone. Use level, severity, levelname, or severity_number instead. Multi-line output is split into one entry per line. A bare console.error(err) in Node prints Error: ... followed by each stack frame on its own line, and every line becomes a separate entry. Log the error through a structured logger so the stack stays inside one JSON line as a field (for example logger.error({ err }, "request failed") with pino). Emit one severity field per line. When more than one is present, the result is undefined.