---
slug: 63-observability
title: Observability
description: Logging with log, decorative trace/measure/checkpoint markers, crash-resume via progress, and rich opt-in error diagnostics with --explain.
example_ids: [observability]
---

# Observability

```synsema
-- Doc example: observability. `log` is real; `trace`/`measure`/`checkpoint` are
-- decorative markers — they RUN their body but don't persist timing/snapshots.
intent: "doc example: observability"

let answer be 0
measure "demo"
    set answer to 41 + 1
print("measure ran → answer = " + text(answer))

test "measure runs its body (the timing instrumentation is decorative)"
    let x be 0
    measure "compute"
        set x to 41 + 1
    assert_eq(x, 42)
```

## Logging (real)

`log` takes a full expression (it's also an expression). Under `serve`, `log`/`print` reach the terminal with a `[serve]` prefix.

```synsema
log "Processing order " + order_id
```

> **`log` is a statement, not a function.** `log "msg"` works; `log("msg")` parses with `check` but crashes at runtime.

### Logs under `serve` (request logging)

`serve` does **not** write an access log for you — by default the terminal is quiet, which makes development hard. **Add a `log` in your handlers** to see traffic live:

```synsema
route "GET /:lang/:version/:slug"
    log "GET " + (path of request)
    give render_page(...)
```

Each request then prints in the server's terminal as `[serve] [LOG] GET /…`. `print` works there too (same `[serve]` prefix). (This docs site logs its own requests this way.)

## Markers — `trace` / `measure` / `checkpoint` (decorative)

These **run their body** but the timing/snapshot instrumentation is currently a stub — they do **not** persist anything. `trace`/`measure` take a literal name; `checkpoint` takes an expression.

```synsema
measure "db_query"
    run_query(sql)
```

For real **crash-resume / step tracking**, use the **progress** builtins, not `checkpoint` — see **[Memory & state](/en/0.6.x/61-memory)**.

## Capability audit (`--audit json`)

Every capability check the runtime makes — granted or denied — can be streamed as JSON, one line per
check, on `run`/`test`/`conform`/`serve` (engine v0.6.14+):

```sh
synsema run --audit json        program.syn   # to stderr
synsema run --audit ./audit.jsonl program.syn # to a file
synsema run --audit fd:3        program.syn   # to a file descriptor (Unix only)
```

Each line is `{ts, context, capability, granted, source, reason, origin, file, line}`:

- **`context`** — which CapabilitySet (`program`, `agent`, `request`, `worker`, a `sandbox:`/`tool:` frame).
- **`origin`** — `"program"` (a `require` or a call the code made) or `"runtime"` (an ambient grant: `stdout`/`time`/`llm`, or `serve` from `--port`).
- **`reason`** — `Granted by <grant>`, `No matching grant found`, `above host ceiling (--sandbox/--cap-set)`, `Explicitly denied by …`, `auto-granted by the runtime` (an ambient grant that succeeded now leaves a trace), or `bundled asset (part of the program)` (a `synsema build` bundle read).
- **`source`** — the builtin that triggered the check (`read_file()`, `run()`, …), or `ceiling`/`ambient`/`secret-builtin`.
- **`file`/`line`** — where in the program (or `null`).

A final line summarizes: `{"summary": {"granted": N, "denied": M, "exit": code}}`. **Secret values
never appear** — capability scopes are names (`secret("STRIPE_KEY")`), never the value. This is the
same audit the WebAssembly `run()` returns as `r.audit` (fields on the [WASM](72-wasm) page); the
native stream just adds `ts`/`context`/`file`/`line`. `run_program` returns its child's audit as
`r["audit"]` — a value, not a log to parse.

## Error diagnostics (opt-in)

Plain `run` prints a stable one-liner (`Runtime error: file:line:col: msg`). For a rich report — source context, call stack, visible variables, classification, suggestions — opt in:

```sh
synsema run --explain program.syn                # human-readable, on stderr
synsema run --explain --format json program.syn  # structured, for tools/agents
```

The exit code is unchanged (1 on failure, 0 on success) either way.
