One row shape for errors, events and measurements, and the conventions we suggest.
An error, an event and a measurement are the same row. They differ in the severity they carry and the attributes they fill in, never in where they go.
| Field | What it is |
|---|---|
| time | When it happened. Stamped by the client, and what every query buckets on. |
| name | What happened. Any string up to 128 characters. |
| severity | 1 to 24. A number, not a word. See the ladder below. |
| distinct_id | The anonymous id that client generated for itself. Required. |
| attributes | Everything else, as JSON. Up to 64 keys, 4 levels deep. |
Names follow ^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$. : and > are reserved, because internal query keys are delimited with them.
| Field | What it is |
|---|---|
| project_id | Resolved from your source key. Never sent, never claimed. |
| firstrun.source.id | Which source the event arrived through. |
| ingested_at | Arrival time. Debugging only: nothing sorts or buckets on it. |
The OpenTelemetry ladder: six bands of four. The band is what you filter on; the four steps inside it are there so a logger with nine levels of its own can map onto this one without losing the order.
| Band | Numbers | What it means |
|---|---|---|
| TRACE | 1 to 4 | Step-by-step detail nobody reads until something is wrong. |
| DEBUG | 5 to 8 | Developer detail. |
| INFO | 9 to 12 | Ordinary things happening. Every event helper lands here. |
| WARN | 13 to 16 | Something recovered, or is about to stop recovering. |
| ERROR | 17 to 20 | Something threw. The error helper lands here. |
| FATAL | 21 to 24 | The process is going down. |
Suggestions, not law. Any other name you send is stored, indexed and queried identically, and no event is ever rejected for the name it carries.
| Name | Usually written by |
|---|---|
| page_view | Browser tag, or you |
| session_start | Browser tag |
| app_install | Desktop SDK, first run only |
| app_launch | Desktop SDK, every run |
| identify | Any client, on identify() |
| page_leave | Browser tag |
| outbound_click | Browser tag |
| file_download | Browser tag |
| form_submit | Browser tag |
| exception | Any client, on error() |
| web_vital | Browser tag |
| http.request | Server SDKs |
| measurement | You, for a numeric sample |
The OpenTelemetry semantic conventions where they exist, and firstrun.* where they do not. These are the keys the pickers offer before your project has written anything.
| Key | What it holds |
|---|---|
| exception.type | The class of the thrown thing. |
| exception.message | The message on it. |
| exception.stacktrace | The formatted stack, as one string. |
| session.id | The visit or the run this belongs to. |
| user.id | Whatever you passed to identify(). Never anything else. |
| service.version | The build of your software that wrote this. |
| os.type | windows, darwin, linux, ios, android. |
| host.arch | The machine architecture. |
| url.path | The path alone. What a breakdown by page groups on. |
| url.full | The whole URL, query string included. |
| http.route | The route template, not the resolved path. |
| http.response.status_code | The status that went back. |
| browser.language | The BCP-47 tag the client reported. |
| firstrun.referrer.host | The referring host alone. |
| firstrun.utm.source | Also .medium, .campaign, .term, .content. |
| firstrun.channel | stable, beta, nightly. |
| firstrun.duration_ms | How long something took. |
| firstrun.metric | What a numeric sample is called: LCP, queue_depth. |
| firstrun.value | The sample itself, as a number. |
| firstrun.unit | The unit it is in, when that is not obvious. |
Any other key works identically. Write order.total or tenant or rows_exported and it is stored, filtered, grouped and aggregated exactly like the ones above. Following a convention buys you a suggestion in a picker and a shared spelling with the next project. It buys nothing else, and skipping one costs nothing.