ReportingError and event reporting

One switch, and every client becomes a reporter

There is no agent to deploy and no instrumentation to write. Monitoring is part of the client library you already generate, so it goes out with your next SDK build and reports from wherever your users installed it.

  • A switch on the project, and the next SDK build carries the reporter with it
  • Your backend reports alongside it, with a package for each of the ten languages
  • Client and server land on one trace, so the call that failed and the code that threw are read together
  • Frames resolve to your original source, from maps your build uploads
  • Every event carries its release, so you know the version a problem started in
  • Errors are not all it sends. Your own events go up the same pipe, with whatever you attach to them
The workspaceLogs, issues and traces

Every angle on one incident, in one product

Query the stream, open a single event whole, group them into issues, and follow one request across every service it touched. It is the same data the whole way through, so answering the next question never means exporting it somewhere else.

Octri Monitoring: querying structured logs, opening a raw event payload, grouped issues, a trace waterfall across client, server, cache and database, the service map, N+1 query detection, synthetic uptime checks and alert rules.

Seven tabs, one dataset

Logs, issues, traces, performance, releases, uptime and alerts, all reading the same events.

One window, everywhere

An hour to ninety days. Set it once and every tab is answering for the same stretch of time.

Facets down the side

Each tab filters by what is actually in it, and every value carries the number of rows it would leave you.

The numbers stay up

Total events, error rate, distinct issues, p95 and the issues costing you most, beside whatever you are reading.

QueryingLog search and filters

Ask the stream a question. Type it, or click it

A SQL-shaped expression over your events, in an editor that completes field names and highlights as you go. The same query is also a row of dropdowns, for the days you would rather not write it.

  • Fields, operators and values completed as you type
  • The same query as dropdowns, built without knowing the syntax
  • One click for the usual cuts: errors, 5xx, 4xx, anything over a second
  • Filter on what you sent, including your own tags and the user an event belongs to
  • Live tail, so the answer keeps updating while you watch it
A monitoring query panel above a filter builder, joined by an arrow. The query is typed into the editor with a completion list offering the level field's operators, then its values, then the field names; two quick-filter chips light as the matching clauses appear. Below, the builder's field, operator and value dropdowns are set to Latency (ms), greater than, and 1000, and pressing the plus sends that clause up into the query text, adds it to the filter list, and lights the Slow chip.
One eventStack traces and source maps

Every error opens onto the code that threw it

Not a message and a timestamp. The failing frame with your own source around it, the steps that led there, and everything the client knew at the moment it broke.

  • Your source around the failing line, resolved through the maps you uploaded
  • The steps that led there, as a timeline of the requests and pages before it
  • Who it happened to, with the device, browser and runtime they were on
  • Step through the occurrences, newest, oldest, or the one worth reading
  • The trace it belongs to, with the server frame for the same request beside it
A list of the errors a project reported today, one of them opened. Closed it is a line like the rest: TypeError, cannot read properties of undefined, reading 'amount'. Opening it reveals the code that threw it — five lines of the project's own TypeScript from src/checkout/session.ts, de-minified against an uploaded source map, with line 118 highlighted and the comment above it explaining that the price moved onto the tier object. Underneath, the trail that led there: the checkout page, the click on the place-order button, and the POST to /v1/checkout that came back 500; and the person it happened to, on Chrome 121 and macOS 14.3, running release 2.4.1.
IssuesError grouping

Thousands of events, a handful of problems

Errors that share a fingerprint collapse into one issue with a count, the number of people it reached, and the line of code it came from.

  • Grouped by fingerprint, with the function and file that threw
  • Ids and numbers normalised out of the message, so one bug is one row however it was worded
  • De-minified against your source maps, so the frame names your code
  • The commits that touched that file, from the repository you connected
  • Assign it, talk it through, resolve it, and hear about it if it comes back
  • Snooze it for an hour, for a week, or until it has happened another hundred times
  • Break one issue down by the tags you send, with the share of events sitting on each value
The Issues table: production errors grouped by fingerprint, each row carrying its message, the endpoint it came from, how many events and users it has hit, when it was last seen, and whether it is unresolved or has regressed.
TracesRequest tracing

One request, timed the whole way down

Every request becomes a tree of spans, and this half of the product is built out of them. Outbound calls, database queries and cache reads are timed for you once you switch instrumentation on, and you can open a span around anything else yourself.

One request drawn as a timeline of spans. A POST to /v1/checkout took 842ms in the browser client that called it, and 790ms of that was the server handling it, so the two are nested: the client SDK and the backend package report into the same trace. Inside the server span, in order: a Redis session read of 6ms, a Postgres select of the plan at 28ms, and a span the developer opened by hand around priceCart(), which ran for 326ms. Inside that one, the same query against tax_rates ran twelve times for 288ms in total, flagged as a suspected N+1. Then an outbound POST to api.stripe.com took 352ms, the longest single thing in the request, followed by an insert into orders at 22ms and a message published to RabbitMQ at 12ms. Each span is coloured by what it was: the client blue, the server green, database queries violet, the cache pink, the outbound call cyan and the queue amber.

The waterfall

A request as a tree, indented by what called what, with each span coloured by whether it was your server, a database, a cache, a queue or a call to someone else.

The service map

Every service the traces touched, the calls between them, and the error rate on each edge.

Queries in a loop

The same query fired again and again inside one trace, counted, and called out harder the more times it repeated.

Percentiles per route

Throughput, error rate, and p50, p75 and p95 for every route you serve.

Budgets

Say how fast a route should be. Then watch the budget.

A target is per route, and once one is set the table stops being a list of numbers you have to interpret every time you open it.

  • A latency target and an error target, set per route
  • Breaches flagged on the row itself, in a table you can scan
  • How fast the budget is burning, while there is still some of it left
  • Filter to what is breaching, and skip the routes doing their job
AlertsAlerting and notifications

Rules that watch it while you are doing something else

Three kinds of rule, delivered where your team already talks. Each one keeps a count of how often it has actually fired, which is what tells you it is wired up.

  • A count crossing a line inside a rolling window, over any query you can write
  • The first sighting of something you have not seen before
  • An issue you resolved throwing again
  • Into Slack, or a webhook pointed wherever you want it
  • A cooldown per rule, so one bad afternoon is one message
  • Run the rules now to see what they would have caught
  • Pause one without losing how it was written
The Alerts table: rules listed by name and kind, each with the condition it watches, the channel it posts to, and how many times it has fired.
UptimeUptime and status page

Checks on a schedule, and a page you can point people at

Probes run against your endpoints on their own clock and keep every result. The uptime they collect is also a public page, which is the one part of monitoring your customers ever look at.

  • Generated from your spec, a probe per safe read endpoint
  • What counts as up is yours: a status to stay under, and a time to stay inside
  • Run one now rather than waiting for its next turn
  • A status page on your own domain, with the history behind every check
  • Incidents with a life of their own, from investigating to resolved, every update kept on the page
  • Notices for the rest, like the maintenance window on Sunday
  • A banner on your documentation for as long as a check is failing
Two halves of one feature. Above: three synthetic checks running on their own schedule against api.northwind.io, each with the interval it runs at and the rule that decides whether it passed. The probe on the search endpoint returns 503, which fails its rule of status under 500. Below: the public status page at status.northwind.io reacting without anyone touching it — the search entry reads Down, its bar for today turns amber because the rest of the day was healthy, and the overall banner changes from all systems operational to partial outage. The team posts an incident: elevated errors on search, investigating, one shard returning 5xx, payments and webhooks unaffected. The checks keep running, and when the search probe answers 200 again the page climbs back on its own — operational, banner cleared, and the incident marked resolved with the update that says why.
IngestScrubbing and deduplication

What happens to an event on the way in

Every event goes through the same pipeline before it is stored, and most of what that pipeline does is decide what should never be kept.

One event arriving at the monitoring ingest endpoint and being processed before it is stored. The request is authenticated with a project-scoped ingest token and answered 202 Accepted straight away, because the SDKs post fire-and-forget. It then passes three gates. Redaction removes the authorization header from the payload because of the name of the key, removes a card number stored under an innocent-looking key because of the shape of the value, and masks the reporting user's address to d***@northwind.io so the address is never stored but the issue can still be grouped per person. Fingerprinting templates the variable parts out of the request path and the message — /v1/checkout/4172/session becomes /v1/checkout/<id>/session, and "User 4172 not found in tier 'pro'" becomes "User <num> not found in tier <str>" — and hashes them into the grouping key 9f3c1d7a, without changing the values the event is stored with. Deduplication drops a retried delivery carrying the same event id. What lands is one occurrence on an issue this project already had, taking its count from 1,283 to 1,284, inside the Northwind API project and nowhere else.

Secrets never land

Passwords, tokens and authorization headers are redacted. Cards, keys and bearer tokens are masked wherever they turn up, stack frames included, and an email keeps its shape and loses the rest.

One bug, one row

Ids, numbers and quoted values are templated out of a message before it is grouped, so the same failure is one issue whether it names user 123 or user 456.

Duplicates drop

An event carrying an id already seen is discarded, so a delivery that had to be retried is still one event on your screen.

Scoped to your project

The token an SDK reports with fixes which project the event belongs to, and reads are scoped the same way on the way back out.

Getting startedSetup and instrumentation

Three ways in. Take whichever you already have

It starts wherever your code already is. Take the one that matches what you have today, add the others whenever you want them, and none of the three is an afternoon's work.

The SDKs we generate for you

One switch on the project, and there is nothing to add to anyone's code. The reporter ships inside the next build, so the copies your users install report from their machines.

The API you run yourself

A package and one call at startup, in TypeScript, Python, Go, Ruby, PHP, Rust, Java, Kotlin, Swift or Dart. Requests are timed, exceptions are reported, and your queries and outbound calls are timed with them.

A URL, and nothing else

Uptime and a public status page need no package and no build. Point a check at an endpoint you already serve, or let your spec write the checks for you.

The SDKs and the API

Run those two together and they stop being two products. A call that failed in somebody else's application and the line of your code that threw arrive as one trace.

Setup for each of the three, and what each one records. Monitoring guides in the docs →

Monitoring that already knows your API

It reads the same spec your documentation and your client libraries came from, so it knows your endpoints and your operations before the first event arrives. The uptime checks are written off that list, and an error comes in named after the operation that threw it. Deprecate something and this is the screen that tells you who is still calling it.

Questions

No. Reporting is compiled into the client library you already publish, and the backend packages are one call at startup. Nothing else has to run on your machines.

Your code is already in production

Monitoring is the switch that has it tell you how it is doing.