Skip to content

Add optional Sentry error reporting #5

Description

@MaikelH

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

  1. 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.
  2. Include cron check-ins? Recommended, but it is a second Sentry product with its own setup and quota.
  3. What to configure beyond the DSN — environment, release, sample rate, debug.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions