self-hosted error monitor Open the panel

Sending errors to meulog

the six doors in, from SDK to e-mail open the screen

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 with usage: true and meulog.identify, the Ruby SDK with MeuLog.track (and one per request with track_requests), the Delphi SDK sends every breadcrumb when TrackUsage is 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.

What meulog does to the event before storing it

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.