> ## Documentation Index
> Fetch the complete documentation index at: https://www.activepieces.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Structured Logging

> Field-naming conventions for evlog wide events across the Activepieces server

All structured logging on the server goes through [evlog](https://www.evlog.dev) — `logger.{info,warn,error,debug}({ fields }, msg)` and `wideEvent.set/error/timed` from `@activepieces/server-utils`.

The **field keys** (not the message string) are the real schema — they are what dashboards, alerts, and the OTLP drain query against. If the same thing is logged under different keys (`runId` here, `flowRunId` there, a bare `id` elsewhere), every query becomes a guessing game and correlation silently breaks.

One rule above all: **one concept = one field, everywhere.**

## Group fields by entity

Following [evlog's guidance](https://www.evlog.dev/learn/wide-events), group related fields under the entity they belong to instead of flat, prefixed keys. An entity's own id is just `id` inside its group.

```ts theme={null}
// ✅ Grouped — clear, and flattens to flowRun.id / flow.id
logger.info({
  flowRun: { id: run.id, status: run.status },
  flow: { id: run.flowId },
  project: { id: run.projectId },
}, 'Flow run started')

// ❌ Flat prefixes, alias, and a bare id
logger.info({ runId: run.id, flowRunId: run.id, id: run.id }, 'Flow run started')
```

The group name is the camelCase singular entity from the domain model. The id lives at `entity.id`, and the entity's attributes sit beside it in the same group.

## Rules

<Steps>
  <Step title="The id is `id`, inside its entity group">
    Never a top-level `flowRunId`, `runId`, or bare `id`. A flow run is `flowRun: { id }`, a flow is `flow: { id }`. This gives exactly one queryable path per entity.
  </Step>

  <Step title="Attributes live beside the id, merged into one group">
    `{ jobId, jobType }` becomes `job: { id, type }`; `{ pieceName, pieceVersion }` becomes `piece: { name, version }`. Never a bare `name` / `version` / `status` / `type` at the top level — they're meaningless without their entity.
  </Step>

  <Step title="Errors use `error`">
    Pass the error straight to evlog — `logger.error(err)` or `logger.error({ err }, 'msg')` — and it lands under the canonical `error` key. Descriptive error fields like `migrationError` are fine.
  </Step>

  <Step title="Units go in the suffix">
    Durations end in `Ms` (`durationMs`, `timings.{op}Ms`), bytes in `Bytes`, counts in `Count` or a plural.
  </Step>

  <Step title="Keep it shallow, and leave reserved fields flat">
    One level of grouping (two max). `requestId`, `service`, `version`, `level`, `msg`, `timestamp`, `error`, `method`, `path` are added automatically — never set them, and never fold `requestId` into a group.
  </Step>
</Steps>

## Groups

| Group          | Fields                                                   | Entity                 |
| -------------- | -------------------------------------------------------- | ---------------------- |
| `flowRun`      | `id`, `status`, `environment`                            | A flow execution       |
| `flow`         | `id`, `version`                                          | A flow definition      |
| `flowVersion`  | `id`                                                     | A flow version         |
| `project`      | `id`                                                     | A project              |
| `platform`     | `id`                                                     | A platform / workspace |
| `user`         | `id`                                                     | A user                 |
| `job`          | `id`, `type`                                             | A queue job            |
| `piece`        | `name`, `version`                                        | An integration piece   |
| `connection`   | `id`                                                     | An app connection      |
| `sandbox`      | `id`                                                     | An execution sandbox   |
| `worker`       | `id`                                                     | A worker process       |
| `webhook`      | `id`, `requestId`, `mode`, `flowFound`, `responseStatus` | A webhook request      |
| `conversation` | `id`                                                     | A chat conversation    |
| `waitpoint`    | `id`                                                     | A pause / resume point |
| `step`         | `name`                                                   | A flow step            |
| `trigger`      | `name`                                                   | A flow trigger         |
| `migration`    | `name`                                                   | A database migration   |

<Warning>
  This convention governs the keys in **logging calls** only, not entity or DTO field names. Data-model fields such as `JobData.runId`, DB query arguments, and service-call arguments stay as-is — the value is simply logged under `flowRun: { id }`.
</Warning>
