Problem
Every failure in this tool is currently reported only as a line on stdout. If pg_dump fails, or the S3 upload fails after its one retry, the job logs an error and the container keeps running on schedule. Nobody is told. For a backup tool the consequence is the worst kind: you find out backups were broken at the moment you need one.
We want optional Sentry integration — off unless a DSN is configured, so existing deployments are unaffected.
Where failures happen today
| Location |
Failure |
Current behaviour |
backup.go doPostgresBackup |
pg_dump exits non-zero |
logged with stderr, job aborts |
backup.go uploadToS3 |
upload fails twice |
logged, local file retained |
backup.go runBackup |
local cleanup fails |
logged, continues |
main.go |
config load / client init / scheduler start |
logged, os.Exit(1) |
The two that matter most are pg_dump failing and the upload failing after retry — both mean no backup exists for that run.
Findings from the SDK (verified against v0.48.0)
The slog handler alone will not give us alerts. github.com/getsentry/sentry-go/slog v0.48.0 emits Sentry Logs, not Issues. Its Option struct has only LogLevel, AttrFromContext, AddSource and ReplaceAttr, and Handle delegates straight to a sentry.Logger. There is no event path.
Note that Sentry's own slog docs show an example using an EventLevel field that does not exist in the released module — don't plan around it. Issues require explicit sentry.CaptureException(err) calls at the failure sites.
os.Exit will silently drop startup errors. The Go SDK's transport is asynchronous, so events are buffered and flushed by a background goroutine. main.go now has three os.Exit(1) calls, and os.Exit does not run deferred functions — a defer sentry.Flush(...) in main would never fire on exactly the error paths we most want reported. Each exit path needs an explicit flush.
Env var mapping needs extending. config/config.go only rewrites the s3_ prefix into a nested key:
if strings.HasPrefix(s, "s3_") {
return strings.Replace(s, "s3_", "s3.", 1)
}
So a nested sentry: config block would not pick up SENTRY_DSN without changing that function.
Worth considering: cron monitoring, not just error reporting
Error reporting can only tell us about failures that occur. It cannot tell us a backup never ran — a crashed container, an OOM kill, a bad cron expression or a paused scheduler all produce silence, which is indistinguishable from success.
The Go SDK supports Sentry cron monitors via sentry.CaptureCheckIn with sentry.CrontabSchedule(...), and this application already holds exactly the cron string needed in cfg.Schedule. Wrapping runBackup in in-progress/ok/error check-ins would alert on a missed run, which is arguably the more valuable signal for a backup tool.
Open decisions
- Issues only, or Issues + Logs? Explicit
CaptureException is required either way. The slog handler could additionally ship INFO/WARN as Sentry Logs for context, at the cost of log volume and quota.
- Include cron check-ins? Recommended, but it is a second Sentry product with its own setup and quota.
- What to configure beyond the DSN — environment, release, sample rate, debug.
- Release identifier. We tag CalVer (
v2026.08.01) but the binary has no version embedded, so Release would need a build-time ldflags var to be meaningful.
Constraints
- Fully optional: no DSN means no Sentry initialization and no behaviour change.
- No secrets in Sentry payloads.
PGPASSWORD and the S3 secret must never leave the process; pg_dump stderr in particular should be reviewed before attaching.
- Adds the first non-config runtime dependency to the project.
Problem
Every failure in this tool is currently reported only as a line on stdout. If
pg_dumpfails, or the S3 upload fails after its one retry, the job logs an error and the container keeps running on schedule. Nobody is told. For a backup tool the consequence is the worst kind: you find out backups were broken at the moment you need one.We want optional Sentry integration — off unless a DSN is configured, so existing deployments are unaffected.
Where failures happen today
backup.godoPostgresBackuppg_dumpexits non-zerobackup.gouploadToS3backup.gorunBackupmain.goos.Exit(1)The two that matter most are
pg_dumpfailing and the upload failing after retry — both mean no backup exists for that run.Findings from the SDK (verified against v0.48.0)
The slog handler alone will not give us alerts.
github.com/getsentry/sentry-go/slogv0.48.0 emits Sentry Logs, not Issues. ItsOptionstruct has onlyLogLevel,AttrFromContext,AddSourceandReplaceAttr, andHandledelegates straight to asentry.Logger. There is no event path.Note that Sentry's own slog docs show an example using an
EventLevelfield that does not exist in the released module — don't plan around it. Issues require explicitsentry.CaptureException(err)calls at the failure sites.os.Exitwill silently drop startup errors. The Go SDK's transport is asynchronous, so events are buffered and flushed by a background goroutine.main.gonow has threeos.Exit(1)calls, andos.Exitdoes not run deferred functions — adefer sentry.Flush(...)inmainwould never fire on exactly the error paths we most want reported. Each exit path needs an explicit flush.Env var mapping needs extending.
config/config.goonly rewrites thes3_prefix into a nested key:So a nested
sentry:config block would not pick upSENTRY_DSNwithout changing that function.Worth considering: cron monitoring, not just error reporting
Error reporting can only tell us about failures that occur. It cannot tell us a backup never ran — a crashed container, an OOM kill, a bad cron expression or a paused scheduler all produce silence, which is indistinguishable from success.
The Go SDK supports Sentry cron monitors via
sentry.CaptureCheckInwithsentry.CrontabSchedule(...), and this application already holds exactly the cron string needed incfg.Schedule. WrappingrunBackupin in-progress/ok/error check-ins would alert on a missed run, which is arguably the more valuable signal for a backup tool.Open decisions
CaptureExceptionis required either way. The slog handler could additionally ship INFO/WARN as Sentry Logs for context, at the cost of log volume and quota.v2026.08.01) but the binary has no version embedded, soReleasewould need a build-time ldflags var to be meaningful.Constraints
PGPASSWORDand the S3 secret must never leave the process;pg_dumpstderr in particular should be reviewed before attaching.