---
slug: 63-observability
title: Observabilidad
description: Logging con log, marcadores decorativos trace/measure/checkpoint, crash-resume vía progress, y diagnósticos de error ricos opt-in con --explain.
example_ids: [observability]
---

# Observabilidad

```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` toma una expresión completa (también es una expresión). Bajo `serve`, `log`/`print` llegan a la terminal con prefijo `[serve]`.

```synsema
log "Procesando orden " + order_id
```

> **`log` es una sentencia, no una función.** `log "msg"` funciona; `log("msg")` parsea con `check` pero revienta en runtime.

### Logs bajo `serve` (logging de requests)

`serve` **no** escribe un access log por vos — por defecto la terminal está callada, lo que dificulta el desarrollo. **Agregá un `log` en tus handlers** para ver el tráfico en vivo:

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

Cada request entonces aparece en la terminal del server como `[serve] [LOG] GET /…`. `print` también funciona ahí (mismo prefijo `[serve]`). (Este sitio de docs loguea sus propios requests así.)

## Marcadores — `trace` / `measure` / `checkpoint` (decorativos)

Estos **corren su cuerpo** pero la instrumentación de timing/snapshot hoy es un stub — **no** persisten nada. `trace`/`measure` toman un nombre literal; `checkpoint` toma una expresión.

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

Para **crash-resume / tracking de pasos** real, usá los builtins de **progress**, no `checkpoint` — ver **[Memoria y estado](/es/0.6.x/61-memory)**.

## Audit de capabilities (`--audit json`)

Cada chequeo de capability que hace el runtime — concedido o denegado — se puede streamear como JSON,
una línea por chequeo, en `run`/`test`/`conform`/`serve` (engine v0.6.14+):

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

Cada línea es `{ts, context, capability, granted, source, reason, origin, file, line}`:

- **`context`** — qué CapabilitySet (`program`, `agent`, `request`, `worker`, un frame `sandbox:`/`tool:`).
- **`origin`** — `"program"` (un `require` o una llamada que hizo el código) o `"runtime"` (un grant ambiente: `stdout`/`time`/`llm`, o `serve` desde `--port`).
- **`reason`** — `Granted by <grant>`, `No matching grant found`, `above host ceiling (--sandbox/--cap-set)`, `Explicitly denied by …`, `auto-granted by the runtime` (un grant ambiente que tuvo éxito ahora deja rastro), o `bundled asset (part of the program)` (una lectura desde un bundle de `synsema build`).
- **`source`** — el builtin que disparó el chequeo (`read_file()`, `run()`, …), o `ceiling`/`ambient`/`secret-builtin`.
- **`file`/`line`** — dónde en el programa (o `null`).

Una línea final resume: `{"summary": {"granted": N, "denied": M, "exit": code}}`. **Los valores de
secretos nunca aparecen** — los scopes de capability son nombres (`secret("STRIPE_KEY")`), nunca el
valor. Este es el mismo audit que el `run()` de WebAssembly devuelve como `r.audit` (campos en la
página [WASM](/es/0.6.x/72-wasm)); el stream nativo solo agrega `ts`/`context`/`file`/`line`.
`run_program` devuelve el audit de su hijo como `r["audit"]` — un valor, no un log para parsear.

## Diagnósticos de error (opt-in)

El `run` plano imprime una línea estable (`Runtime error: file:line:col: msg`). Para un reporte rico — contexto de fuente, call stack, variables visibles, clasificación, sugerencias — optá por:

```sh
synsema run --explain program.syn                # legible, en stderr
synsema run --explain --format json program.syn  # estructurado, para tools/agentes
```

El exit code no cambia (1 al fallar, 0 al tener éxito) en cualquier caso.
