# Observability ButterRobot reports errors, panics and optional performance traces to [Sentry](https://sentry.io). Sentry is **disabled by default**. Setting `SENTRY_DSN` is the only thing required to turn it on. ## Configuration | Variable | Default | Description | | --- | --- | --- | | `SENTRY_DSN` | *(empty)* | Sentry DSN. Empty disables all reporting. | | `SENTRY_ENVIRONMENT` | `production` | Environment name attached to every event. | | `SENTRY_RELEASE` | *(build version)* | Release identifier. Falls back to the version embedded at build time. | | `SENTRY_TRACES_SAMPLE_RATE` | `0` | Fraction of HTTP requests traced, `0.0`–`1.0`. `0` disables tracing. | | `SENTRY_ENABLE_LOGS` | `n` | Set to `y` to also forward `DEBUG`/`INFO`/`WARN` records as Sentry logs. | Example: ``` SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0 SENTRY_ENVIRONMENT=production SENTRY_TRACES_SAMPLE_RATE=0.1 ``` ## What gets reported - **Errors**: every `slog` record logged at `ERROR` level becomes a Sentry event, with its structured attributes attached. - **Panics**: queue worker, reminder worker, cache cleanup and HTTP handler panics are captured with a stack trace. - **Traces**: HTTP requests, when `SENTRY_TRACES_SAMPLE_RATE` is above `0`. Requests to `/healthz` are never traced. - **Logs**: `DEBUG`/`INFO`/`WARN` records, only when `SENTRY_ENABLE_LOGS=y`. ## Secret redaction Platform credentials travel through paths that error reporting would otherwise capture verbatim: the Telegram webhook embeds the bot token in its URL (`/telegram/incoming/`), and a failed Telegram API call quotes the request URL — which also contains the token — in its error text. Every configured secret is therefore replaced with `[REDACTED]` in outgoing events, transactions and logs, across messages, exception values, breadcrumbs, tags, extra data and the captured HTTP request. The redacted values come from `config.Config.Secrets()`; **a new platform must add its credential there** or it will be reported to Sentry in the clear. Values shorter than 8 characters are not redacted, so a placeholder such as the default `SECRET_KEY` does not mangle unrelated text. ## Tags Events raised while handling a message carry tags that make filtering in Sentry straightforward: - `platform`: the platform the message arrived from (for example `telegram`). - `plugin`: the plugin being executed when the error occurred. - `component`: the background component at fault, for example `reminder-scheduler`. ## Reporting from a plugin Plugins do not need to talk to Sentry directly — anything logged at `ERROR` level by the application is forwarded automatically. Plugins that keep their own logger should log errors through `slog` so they inherit the same pipeline. To attach extra tags around a block of work, wrap the context and log with `ErrorContext`: ```go ctx := observability.WithTags(ctx, map[string]string{"plugin": "my.plugin"}) logger.ErrorContext(ctx, "Something failed", "error", err) ```