What it is for
meulog is only useful once the error arrives. There are six doors in, and which one you pick depends on where your code already is.
1. Through a Sentry SDK (the shortest path)
meulog speaks the Sentry protocol. Any official SDK works with no adapter: JavaScript, Python, Ruby, Java, .NET, PHP, Go, Android, iOS, Flutter. Paste the DSN shown on the project screen into the SDK configuration and you are done.
The DSN carries the public token, which is why it can go into the page JavaScript: with it you can only write events.
2. Through the meulog API
POST /api/v1/events, with the server token in the
Authorization header. It takes class, message, stack (text or frames),
environment, version, customer, machine, user, and any attribute you
want to send along. Whatever you send becomes a facet and becomes searchable without
declaring anything.
3. Through OpenTelemetry
POST /v1/logs, in OTLP format. If your shop already exports telemetry,
meulog is one more destination rather than a second instrumentation.
4. By e-mail (the bug report)
A Delphi application with madExcept sends its report by e-mail. The
imap collector reads the mailbox, the bug report format
translates it, and the occurrence comes in with the stack, the screenshot and the
attachments. No SDK, no touching the customer code.
5. From logs already indexed
If the error is already in Elasticsearch, OpenSearch or Loki, meulog goes and fetches it. That is the search cluster.
6. Browser policy violations
The csp receiver takes the reports the browser itself sends when something
violates your page Content Security Policy. It is the only input where the user browser
talks to meulog with no SDK at all.
And four more things you send
POST /api/v1/heartbeats: the heartbeat of a scheduled job.POST /api/v1/deploys: the version that shipped.POST /api/v1/sourcemaps: the source map, from your build.POST /api/v1/usage: usage events, what the person did (opened a screen, clicked, called an endpoint), to sit next to the errors of the same customer, user and session. The browser SDK sends them withusage: trueandmeulog.identify, the Ruby SDK withMeuLog.track(and one per request withtrack_requests), the Delphi SDK sends every breadcrumb whenTrackUsageis on. They have their own hourly quota and their own purge, the usage history, 14 days by default.
Sending usage from a platform with no SDK
It is a POST, and the public token is accepted — which is the whole
reason a mobile app can use this port: its key ships inside the binary, and the public
one is the one that may. An OTLP log record carrying event.name also becomes
a usage event, but it is a heavier path to the same place.
curl -X POST https://meulog.example.com/<account>/api/v1/usage \
-H "Authorization: Bearer <public token>" \
-H "Content-Type: application/json" \
-d '{"events":[{"name":"workout_finished","category":"custom",
"occurred_at":"2026-09-25T03:20:00Z","session":"<uuid>",
"platform":"android","user":"42","context":{"sets":18}}]}'
The accepted fields are name, category, occurred_at, customer, user, machine, session, platform, version, person, context, traits, group_traits. Anything outside that list is dropped
in silence, which is what an allow-list is for — and the reason it is written here.
It goes through sanitization, the stack is translated if there is a source map, the fingerprint is computed, the project quota is checked, and only then is it stored. None of those steps is optional, and all of them happen before the row exists in the database.
Proving it worked
Debug creates a fake error that goes through the whole belt, and Processes says whether the input brought anything recently.