Add owner/admin-only log retention deletion to Settings

New api/logretention package: GET /logs/retention/preview and
DELETE /logs/retention, both gated to RoleAdmin (Owner satisfies it
too), issue purpose-built parameterized statements against ClickHouse's
logs table (a count and a synchronous ALTER TABLE ... DELETE mutation)
rather than routing through querylang/executor's SELECT-only SQLRunner.

Settings gets a new "Log retention" section, visible only to an owner
or admin, that previews how many records a chosen age cutoff would
remove before showing an explicit confirm/cancel panel -- no delete
happens without that second step.

Scoped to core's single-tenant ClickHouse table; enterprise/'s
per-tenant routing and Tantivy's lack of a bulk-delete primitive are
disclosed gaps in api/logretention/store.go's doc comment, not silently
assumed to already work.
This commit is contained in:
2026-08-21 14:49:18 -07:00
parent 864e68253a
commit 787def06fd
6 changed files with 676 additions and 1 deletions
+8
View File
@@ -33,6 +33,7 @@ import (
"github.com/sentry/sentry/api/httpserver"
"github.com/sentry/sentry/api/internal/config"
"github.com/sentry/sentry/api/localauth"
"github.com/sentry/sentry/api/logretention"
"github.com/sentry/sentry/api/queryapi"
"github.com/sentry/sentry/api/querylang/executor"
"github.com/sentry/sentry/api/searchclient"
@@ -159,6 +160,12 @@ func main() {
// queryHandler's/aiHandler's nil audit loggers above.
agentsHandler := agents.NewHandler(logger, agents.NewStore(pgPool), authorizer, nil)
// Same conn sqlRunner above already wraps -- logretention issues its
// own purpose-built statements against the `logs` table directly
// rather than going through sqlRunner's SELECT-only RunSQL (see
// logretention.Store's doc comment).
logRetentionHandler := logretention.NewHandler(logger, logretention.NewStore(conn), authorizer)
// One shared mux, CORS applied once around the whole thing -- see
// httpserver's doc comment for why this changed from each
// handler wrapping itself individually.
@@ -166,6 +173,7 @@ func main() {
queryHandler.RegisterRoutes(mux)
dashboardsHandler.RegisterRoutes(mux)
agentsHandler.RegisterRoutes(mux)
logRetentionHandler.RegisterRoutes(mux)
// Only registered when local auth is actually enabled -- see
// localauth.Handler.RegisterRoutes' doc comment for why a disabled