Some checks failed
Report errors, panics and optional performance traces to Sentry. Sentry stays disabled unless SENTRY_DSN is set. - slog ERROR records become Sentry issues, with the error attribute promoted to an exception so issues group by root cause - queue worker, reminder worker, cache cleanup and HTTP handler panics are captured with a stack trace - events carry platform/plugin/component tags for filtering - HTTP requests are traced when SENTRY_TRACES_SAMPLE_RATE is above 0; /healthz is never traced Configured credentials are redacted from every outgoing payload. This is required rather than defensive: the Telegram webhook embeds the bot token in its URL path, and failed Telegram API calls quote that URL in their error text, so events would otherwise carry the token in the clear. A new platform must register its credential in Config.Secrets(). Also moves the module to Go 1.27, refreshes every dependency and pins golangci-lint v2.13.2. sentry-go 0.48 removed issue creation from its slog integration, so the ERROR-to-issue conversion lives in internal/observability/handler.go instead of relying on the SDK; leaving it to the SDK would have silently downgraded issues to log lines. Claude-Session: https://claude.ai/code/session_01W7tcpMTEyk9RrHvT7Be5zZ
65 lines
2.9 KiB
Markdown
65 lines
2.9 KiB
Markdown
# 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/<token>`), 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)
|
||
```
|