Diagnostics
Diagnostics
Your app POSTs captured browser errors — message, stack, page, user — and this module deduplicates them by fingerprint with an occurrence count, so a bug hit 4,000 times is one row that says 4,000 rather than 4,000 rows.
Enable it
dotnet add package BarakoCMS.Diagnostics
builder.Services.AddBarakoCMS(builder.Configuration);
var app = builder.Build();
app.UseBarakoCMS();
The package reference plus a restart is the install. AddBarakoCMS finds every module in the
application's dependency context, and BarakoCMS:Modules:Enabled decides which of them run
(BarakoCMS__Modules__Enabled=Diagnostics). Unset, every referenced module runs and the API logs
one warning saying so. To name it by hand instead, put
modules.Add(new BarakoCMS.Diagnostics.DiagnosticsModule())
in the AddBarakoCMS callback; discovery skips a type the host already added. See MODULES.md in
the repository.
Endpoints
| Method & path | Purpose | Access |
|---|---|---|
POST /api/client-errors |
Report a batch of captured errors | Anonymous |
GET /api/client-errors |
Browse what has been reported for the current tenant (every tenant for a caller holding the role globally) | Admin / SuperAdmin |
GET /api/incidents |
Every failure source, grouped and redacted (see below) | manage_client_errors |
POST /api/client-errors/{id}/resolve |
Mark a fingerprint resolved, with an optional reference (a ticket or pull request link or number, up to 500 characters) and note (up to 2,000); { "resolved": false } reopens it and clears both |
Admin / SuperAdmin |
An error is kept per tenant: the report's tenant, or the tenant the request resolved to when it
names none. The same fault in two tenants is two rows, and resolving another tenant's error answers
404 unless the caller holds the role globally. Tenants are stored lowercased.
Reporting is anonymous on purpose: the errors worth catching often happen before anyone signs in.
That makes it a spammable endpoint, so it runs under its own tighter telemetry rate-limit policy
rather than the global budget.
Incidents
GET /api/incidents is one paged list across every place failures are recorded, grouped by
fingerprint, so a person looking for "what is broken" reads one list instead of four:
| Source | What it reads | Also needs |
|---|---|---|
client-errors |
Browser errors reported to POST /api/client-errors |
|
dead-letters |
Background jobs that used every attempt, dated by when they gave up | view_jobs |
failed-runs |
Workflow actions that failed for good | view_workflow_runs |
failed-deliveries |
Webhook and connector sends with no answer or a non-2xx one | view_workflow_runs |
The view needs manage_client_errors. A source whose records the caller could not read directly is
left out for that caller. Each group carries source, fingerprint, title, detail, count,
firstSeenAt, lastSeenAt, tenants, releases and the resolution fields. Query with
source, resolved, days (1 to 90, default 30), page and pageSize. Each source contributes at
most its newest 1,000 occurrences in the window. Client errors span every tenant for a caller who
holds the capability through a global role; the other sources are stored per tenant and read for
the current one. A response body from a delivery is never read here.
Another module adds a source by registering a scoped barakoCMS.Core.Interfaces.IIncidentSource.
Redaction
Titles, details and resolution text in the incidents view, and everything the forwarder sends, go through one set of rules on the server:
- JSON web tokens, and the value after
BearerorBasic - every query string value, and the user and password in a URL
- the value after a key named like a credential: password, secret, token, api key, access key, private key, auth, session, cookie, signature, otp
- the value after the name of a content field marked Sensitive or Hidden, written as
Name: value,Name=value,"Name": "value"or escaped as\"Name\":\"value\". A quoted value goes whole, escapes included; an unquoted one goes up to the next comma, semicolon, ampersand, bracket or line end, spaces and all - email addresses, and any run of 32 or more letters, digits,
-or_holding both a letter and a digit, which is what an API key or session id looks like
The dead-letter, failed-run and failed-delivery sources also cut every URL in their text to its
scheme and host, as /api/jobs and the workflow-run list do, because a webhook URL can carry its
secret in the path. Browser errors keep their paths, since the script or page is what a reader
needs, with every query value removed.
These are patterns. A Sensitive value that appears with no field name in front of it is not
recognisable as one and stays. The stored error and GET /api/client-errors are unchanged: they
are the unredacted record, behind the same capability.
Forwarding to an error tracker
Off by default. Set a DSN and captured errors are also sent to any receiver that speaks Sentry's envelope protocol: Sentry, or GlitchTip, which is MIT licensed and self-hostable.
Modules__Diagnostics__Forwarding__Enabled=true
Modules__Diagnostics__Forwarding__Dsn=https://PUBLIC_KEY@errors.example.com/PROJECT_ID
Modules__Diagnostics__Forwarding__Environment=production # optional
Modules__Diagnostics__Forwarding__QueueCapacity=500 # optional
Modules__Diagnostics__Forwarding__TimeoutSeconds=5 # optional
Local storage and GET /api/client-errors are unchanged; this is a copy, not a replacement.
Forwarding never touches the ingest response: reports wait in a queue of at most QueueCapacity,
a full queue drops new ones, and a receiver that is down, slow or answering errors is logged and
skipped with no retry.
Exactly what leaves the process, per report, as one envelope event:
| Field | From |
|---|---|
message.formatted |
the error message, redacted |
culprit |
the reported source (script, component or API path), redacted |
request.url |
the page URL, redacted (query values removed) |
level |
error or warning |
release |
the reported app version |
environment |
the configured environment, when set |
fingerprint |
this module's fingerprint of the fault |
tags.kind, tags.tenant, tags.status |
the report's kind, tenant slug and HTTP status |
event_id, timestamp, platform, logger |
generated |
The envelope header also carries the DSN, as the protocol requires. Not sent: the stack, the user agent, the user id or username, and the client's address.
Sending errors
The barakoCMS admin ships a reporter you can copy. Two rules matter if you write your own: send with
plain fetch rather than an HTTP client that retries or refreshes tokens (a reporter that can fail
by reporting is a loop), and cap sends per page session so a render loop cannot flood the API.
Part of barakoCMS
This is an optional module for barakoCMS, an open-source
headless CMS for .NET 10. Every module is published under the barakocms-module tag, so a single
search on nuget.org returns the whole set.
Contributions are welcome — including a module icon or other design work. See CONTRIBUTING.md.
Licensed under MIT.
If barakoCMS is useful to you, a star on the repository helps other people find it.