feat: add observability improvements (metrics, audit log, structured logging)
All checks were successful
Check / check (pull_request) Successful in 1m45s

- Add Prometheus metrics package (internal/metrics) with deployment,
  container health, webhook, HTTP request, and audit counters/histograms
- Add audit_log SQLite table via migration 007
- Add AuditEntry model with CRUD operations and query methods
- Add audit service (internal/service/audit) for recording user actions
- Instrument deploy service with deployment duration, count, and
  in-flight metrics; container health gauge updates on deploy completion
- Instrument webhook service with event counters by app/type/matched
- Instrument HTTP middleware with request count, duration, and response
  size metrics; also log response bytes in structured request logs
- Add audit logging to all key handler operations: login/logout, app
  CRUD, deploy, cancel, rollback, restart/stop/start, webhook receipt,
  and initial setup
- Add GET /api/audit endpoint for querying recent audit entries
- Make /metrics endpoint always available (optionally auth-protected)
- Add comprehensive tests for metrics, audit model, and audit service
- Update existing test infrastructure with metrics and audit dependencies
- Update README with Observability section documenting all metrics,
  audit log, and structured logging
This commit is contained in:
clawbot
2026-03-17 02:23:44 -07:00
parent fd110e69db
commit f558e2cdd8
21 changed files with 1399 additions and 42 deletions

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
@@ -24,8 +25,10 @@ import (
"sneak.berlin/go/upaas/internal/handlers"
"sneak.berlin/go/upaas/internal/healthcheck"
"sneak.berlin/go/upaas/internal/logger"
"sneak.berlin/go/upaas/internal/metrics"
"sneak.berlin/go/upaas/internal/middleware"
"sneak.berlin/go/upaas/internal/service/app"
"sneak.berlin/go/upaas/internal/service/audit"
"sneak.berlin/go/upaas/internal/service/auth"
"sneak.berlin/go/upaas/internal/service/deploy"
"sneak.berlin/go/upaas/internal/service/notify"
@@ -92,7 +95,8 @@ func createAppServices(
logInstance *logger.Logger,
dbInstance *database.Database,
cfg *config.Config,
) (*auth.Service, *app.Service, *deploy.Service, *webhook.Service, *docker.Client) {
metricsInstance *metrics.Metrics,
) (*auth.Service, *app.Service, *deploy.Service, *webhook.Service, *docker.Client, *audit.Service) {
t.Helper()
authSvc, authErr := auth.New(fx.Lifecycle(nil), auth.ServiceParams{
@@ -125,6 +129,7 @@ func createAppServices(
Database: dbInstance,
Docker: dockerClient,
Notify: notifySvc,
Metrics: metricsInstance,
})
require.NoError(t, deployErr)
@@ -132,10 +137,18 @@ func createAppServices(
Logger: logInstance,
Database: dbInstance,
Deploy: deploySvc,
Metrics: metricsInstance,
})
require.NoError(t, webhookErr)
return authSvc, appSvc, deploySvc, webhookSvc, dockerClient
auditSvc, auditErr := audit.New(fx.Lifecycle(nil), audit.ServiceParams{
Logger: logInstance,
Database: dbInstance,
Metrics: metricsInstance,
})
require.NoError(t, auditErr)
return authSvc, appSvc, deploySvc, webhookSvc, dockerClient, auditSvc
}
func setupTestHandlers(t *testing.T) *testContext {
@@ -145,11 +158,14 @@ func setupTestHandlers(t *testing.T) *testContext {
globalInstance, logInstance, dbInstance, hcInstance := createCoreServices(t, cfg)
authSvc, appSvc, deploySvc, webhookSvc, dockerClient := createAppServices(
metricsInstance := metrics.NewForTest(prometheus.NewRegistry())
authSvc, appSvc, deploySvc, webhookSvc, dockerClient, auditSvc := createAppServices(
t,
logInstance,
dbInstance,
cfg,
metricsInstance,
)
handlersInstance, handlerErr := handlers.New(
@@ -164,6 +180,7 @@ func setupTestHandlers(t *testing.T) *testContext {
Deploy: deploySvc,
Webhook: webhookSvc,
Docker: dockerClient,
Audit: auditSvc,
},
)
require.NoError(t, handlerErr)
@@ -173,6 +190,7 @@ func setupTestHandlers(t *testing.T) *testContext {
Globals: globalInstance,
Config: cfg,
Auth: authSvc,
Metrics: metricsInstance,
})
require.NoError(t, mwErr)