From 5191cabe7858e4030954c2a02e1068a4c17a1784 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Wed, 6 May 2026 14:02:04 -0500 Subject: [PATCH 01/13] chore(workspace-engine): add OTel logs SDK dependencies --- apps/workspace-engine/go.mod | 4 ++++ apps/workspace-engine/go.sum | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/apps/workspace-engine/go.mod b/apps/workspace-engine/go.mod index 34b418a42..ae8d3b419 100644 --- a/apps/workspace-engine/go.mod +++ b/apps/workspace-engine/go.mod @@ -292,10 +292,14 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect + go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.58.0 // indirect + go.opentelemetry.io/otel/log v0.14.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect diff --git a/apps/workspace-engine/go.sum b/apps/workspace-engine/go.sum index 15664711a..13f6441cd 100644 --- a/apps/workspace-engine/go.sum +++ b/apps/workspace-engine/go.sum @@ -972,6 +972,8 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 h1:bwnLpizECbPr1RrQ27waeY2SPIPeccCx/xLuoYADZ9s= +go.opentelemetry.io/contrib/bridges/otelslog v0.13.0/go.mod h1:3nWlOiiqA9UtUnrcNk82mYasNxD8ehOspL0gOfEo6Y4= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8= @@ -982,6 +984,8 @@ go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 h1:PeBoRj6af6xMI7qCu go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0/go.mod h1:ingqBCtMCe8I4vpz/UVzCW6sxoqgZB37nao91mLQ3Bw= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= @@ -994,10 +998,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY= go.opentelemetry.io/otel/exporters/prometheus v0.58.0 h1:CJAxWKFIqdBennqxJyOgnt5LqkeFRT+Mz3Yjz3hL+h8= go.opentelemetry.io/otel/exporters/prometheus v0.58.0/go.mod h1:7qo/4CLI+zYSNbv0GMNquzuss2FVZo3OYrGh96n4HNc= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= +go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= +go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= From 72794c180d83dc279c712831e408d5d60fdeeda2 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Wed, 6 May 2026 14:10:01 -0500 Subject: [PATCH 02/13] chore(workspace-engine): add OTEL_LOG_LEVEL config --- apps/workspace-engine/pkg/config/env.go | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/workspace-engine/pkg/config/env.go b/apps/workspace-engine/pkg/config/env.go index 28dbe792c..b1fd6579f 100644 --- a/apps/workspace-engine/pkg/config/env.go +++ b/apps/workspace-engine/pkg/config/env.go @@ -31,6 +31,7 @@ type Config struct { OTELServiceName string `default:"ctrlplane/workspace-engine" envconfig:"OTEL_SERVICE_NAME"` OTELExporterOTLPEndpoint string `default:"localhost:4318" envconfig:"OTEL_EXPORTER_OTLP_ENDPOINT"` + OTELLogLevel string `default:"INFO" envconfig:"OTEL_LOG_LEVEL"` GithubBotAppID string `default:"" envconfig:"GITHUB_BOT_APP_ID"` GithubBotPrivateKey string `default:"" envconfig:"GITHUB_BOT_PRIVATE_KEY"` From 8b3322afa5238218a75d7076f6f7e53fbda2d211 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Wed, 6 May 2026 14:25:09 -0500 Subject: [PATCH 03/13] feat(workspace-engine): add slog tee handler for multi-sink logging --- apps/workspace-engine/log_handler.go | 53 ++++++++++++++++++++++ apps/workspace-engine/log_handler_test.go | 55 +++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 apps/workspace-engine/log_handler.go create mode 100644 apps/workspace-engine/log_handler_test.go diff --git a/apps/workspace-engine/log_handler.go b/apps/workspace-engine/log_handler.go new file mode 100644 index 000000000..dfc20e655 --- /dev/null +++ b/apps/workspace-engine/log_handler.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "errors" + "log/slog" +) + +type teeHandler struct { + handlers []slog.Handler +} + +func newTeeHandler(handlers ...slog.Handler) *teeHandler { + return &teeHandler{handlers: handlers} +} + +func (t *teeHandler) Enabled(ctx context.Context, level slog.Level) bool { + for _, h := range t.handlers { + if h.Enabled(ctx, level) { + return true + } + } + return false +} + +func (t *teeHandler) Handle(ctx context.Context, r slog.Record) error { + var errs []error + for _, h := range t.handlers { + if !h.Enabled(ctx, r.Level) { + continue + } + if err := h.Handle(ctx, r.Clone()); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +func (t *teeHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + next := make([]slog.Handler, len(t.handlers)) + for i, h := range t.handlers { + next[i] = h.WithAttrs(attrs) + } + return &teeHandler{handlers: next} +} + +func (t *teeHandler) WithGroup(name string) slog.Handler { + next := make([]slog.Handler, len(t.handlers)) + for i, h := range t.handlers { + next[i] = h.WithGroup(name) + } + return &teeHandler{handlers: next} +} diff --git a/apps/workspace-engine/log_handler_test.go b/apps/workspace-engine/log_handler_test.go new file mode 100644 index 000000000..9f15e8e7e --- /dev/null +++ b/apps/workspace-engine/log_handler_test.go @@ -0,0 +1,55 @@ +package main + +import ( + "bytes" + "context" + "log/slog" + "strings" + "testing" +) + +func TestTeeHandlerForwardsToBoth(t *testing.T) { + var bufA, bufB bytes.Buffer + h := newTeeHandler( + slog.NewTextHandler(&bufA, &slog.HandlerOptions{Level: slog.LevelDebug}), + slog.NewTextHandler(&bufB, &slog.HandlerOptions{Level: slog.LevelDebug}), + ) + logger := slog.New(h) + logger.Info("hello", "key", "val") + + if !strings.Contains(bufA.String(), "hello") || !strings.Contains(bufA.String(), "key=val") { + t.Fatalf("handler A missing record: %q", bufA.String()) + } + if !strings.Contains(bufB.String(), "hello") || !strings.Contains(bufB.String(), "key=val") { + t.Fatalf("handler B missing record: %q", bufB.String()) + } +} + +func TestTeeHandlerEnabledIsUnion(t *testing.T) { + infoOnly := slog.NewTextHandler(&bytes.Buffer{}, &slog.HandlerOptions{Level: slog.LevelInfo}) + debugOnly := slog.NewTextHandler(&bytes.Buffer{}, &slog.HandlerOptions{Level: slog.LevelDebug}) + h := newTeeHandler(infoOnly, debugOnly) + + if !h.Enabled(context.Background(), slog.LevelDebug) { + t.Fatal("expected DEBUG enabled because debugOnly accepts it") + } + if !h.Enabled(context.Background(), slog.LevelInfo) { + t.Fatal("expected INFO enabled") + } +} + +func TestTeeHandlerWithAttrsPropagates(t *testing.T) { + var bufA, bufB bytes.Buffer + h := newTeeHandler( + slog.NewTextHandler(&bufA, nil), + slog.NewTextHandler(&bufB, nil), + ) + logger := slog.New(h).With("svc", "we") + logger.Info("up") + + for name, got := range map[string]string{"A": bufA.String(), "B": bufB.String()} { + if !strings.Contains(got, "svc=we") { + t.Fatalf("handler %s missing With attrs: %q", name, got) + } + } +} From 310051705c1a152e88bf1ee57062832bb43d77cd Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 11:38:54 -0500 Subject: [PATCH 04/13] Switch to slog and add otel handler --- apps/workspace-engine/go.mod | 6 +- apps/workspace-engine/go.sum | 2 + apps/workspace-engine/log_handler.go | 25 ++++++ apps/workspace-engine/log_handler_test.go | 69 +++++++++++++++++ apps/workspace-engine/main.go | 19 +++-- apps/workspace-engine/otel.go | 77 +++++++++++++++++-- apps/workspace-engine/pkg/db/client.go | 9 ++- apps/workspace-engine/pkg/db/convert.go | 6 +- .../jobagents/argo/application_upserter.go | 4 +- .../pkg/jobagents/argo/argocd_plan.go | 4 +- .../argoworkflows/workflow_submitter.go | 4 +- .../pkg/jobagents/terraformcloud/tfe.go | 6 +- .../reconcile/events/deploymentselector.go | 4 +- .../pkg/reconcile/events/desiredrelease.go | 4 +- .../reconcile/events/environmentselector.go | 4 +- .../pkg/reconcile/events/jobeligibility.go | 4 +- .../pkg/reconcile/events/policyeval.go | 4 +- apps/workspace-engine/pkg/reconcile/worker.go | 6 +- .../store/policies/get_for_release_targets.go | 6 +- .../pkg/store/resources/get_resources.go | 11 +-- .../workspace/relationships/eval/evaluate.go | 6 +- .../svc/claimcleanup/service.go | 6 +- .../controllers/deploymentplan/controller.go | 12 +-- .../deploymentplanresult/controller.go | 12 +-- .../controller.go | 14 ++-- .../controllers/desiredrelease/controller.go | 12 +-- .../controllers/desiredrelease/reconcile.go | 8 +- .../controller.go | 12 +-- .../svc/controllers/forcedeploy/controller.go | 10 ++- .../svc/controllers/forcedeploy/reconcile.go | 6 +- .../svc/controllers/jobdispatch/controller.go | 12 +-- .../controllers/jobeligibility/controller.go | 12 +-- .../jobeligibility/getters_postgres.go | 18 ++--- .../jobverificationmetric/controller.go | 12 +-- .../jobverificationmetric/metrics/metric.go | 6 +- .../metrics/provider/datadog/datadog.go | 12 +-- .../metrics/provider/http/http.go | 6 +- .../metrics/provider/prometheus/prometheus.go | 12 +-- .../jobverificationmetric/reconcile.go | 4 +- .../svc/controllers/policyeval/controller.go | 12 +-- .../relationshipeval/controller.go | 12 +-- apps/workspace-engine/svc/http/http.go | 8 +- .../svc/http/server/server.go | 4 +- apps/workspace-engine/svc/pprof/pprof.go | 6 +- apps/workspace-engine/svc/service.go | 17 ++-- 45 files changed, 362 insertions(+), 163 deletions(-) diff --git a/apps/workspace-engine/go.mod b/apps/workspace-engine/go.mod index ae8d3b419..3914d8a96 100644 --- a/apps/workspace-engine/go.mod +++ b/apps/workspace-engine/go.mod @@ -29,11 +29,14 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.1 github.com/teambition/rrule-go v1.8.2 + go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 go.opentelemetry.io/otel v1.43.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 go.opentelemetry.io/otel/sdk v1.42.0 + go.opentelemetry.io/otel/sdk/log v0.14.0 go.opentelemetry.io/otel/sdk/metric v1.42.0 go.opentelemetry.io/otel/trace v1.43.0 k8s.io/apimachinery v0.34.1 @@ -292,14 +295,11 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect - go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.58.0 // indirect go.opentelemetry.io/otel/log v0.14.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect diff --git a/apps/workspace-engine/go.sum b/apps/workspace-engine/go.sum index 13f6441cd..ff91c6536 100644 --- a/apps/workspace-engine/go.sum +++ b/apps/workspace-engine/go.sum @@ -1006,6 +1006,8 @@ go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXY go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= diff --git a/apps/workspace-engine/log_handler.go b/apps/workspace-engine/log_handler.go index dfc20e655..17583bdd2 100644 --- a/apps/workspace-engine/log_handler.go +++ b/apps/workspace-engine/log_handler.go @@ -51,3 +51,28 @@ func (t *teeHandler) WithGroup(name string) slog.Handler { } return &teeHandler{handlers: next} } + +type levelHandler struct { + handler slog.Handler + level slog.Level +} + +func newLevelHandler(level slog.Level, h slog.Handler) *levelHandler { + return &levelHandler{handler: h, level: level} +} + +func (l *levelHandler) Enabled(_ context.Context, lvl slog.Level) bool { + return lvl >= l.level +} + +func (l *levelHandler) Handle(ctx context.Context, r slog.Record) error { + return l.handler.Handle(ctx, r) +} + +func (l *levelHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &levelHandler{handler: l.handler.WithAttrs(attrs), level: l.level} +} + +func (l *levelHandler) WithGroup(name string) slog.Handler { + return &levelHandler{handler: l.handler.WithGroup(name), level: l.level} +} diff --git a/apps/workspace-engine/log_handler_test.go b/apps/workspace-engine/log_handler_test.go index 9f15e8e7e..364896b48 100644 --- a/apps/workspace-engine/log_handler_test.go +++ b/apps/workspace-engine/log_handler_test.go @@ -3,9 +3,11 @@ package main import ( "bytes" "context" + "errors" "log/slog" "strings" "testing" + "time" ) func TestTeeHandlerForwardsToBoth(t *testing.T) { @@ -53,3 +55,70 @@ func TestTeeHandlerWithAttrsPropagates(t *testing.T) { } } } + +func TestTeeHandlerWithGroupPropagates(t *testing.T) { + var bufA, bufB bytes.Buffer + h := newTeeHandler( + slog.NewTextHandler(&bufA, nil), + slog.NewTextHandler(&bufB, nil), + ) + logger := slog.New(h).WithGroup("net").With("port", 8080) + logger.Info("up") + + for name, got := range map[string]string{"A": bufA.String(), "B": bufB.String()} { + if !strings.Contains(got, "net.port=8080") { + t.Fatalf("handler %s missing grouped attr: %q", name, got) + } + } +} + +type errHandler struct { + enabled bool + err error +} + +func (e *errHandler) Enabled(context.Context, slog.Level) bool { return e.enabled } +func (e *errHandler) Handle(context.Context, slog.Record) error { return e.err } +func (e *errHandler) WithAttrs([]slog.Attr) slog.Handler { return e } +func (e *errHandler) WithGroup(string) slog.Handler { return e } + +func TestTeeHandlerJoinsErrors(t *testing.T) { + errA := errors.New("a failed") + errB := errors.New("b failed") + h := newTeeHandler( + &errHandler{enabled: true, err: errA}, + &errHandler{enabled: true, err: errB}, + ) + err := h.Handle(context.Background(), slog.NewRecord(time.Time{}, slog.LevelInfo, "msg", 0)) + if !errors.Is(err, errA) || !errors.Is(err, errB) { + t.Fatalf("expected joined errors to wrap both errA and errB, got: %v", err) + } +} + +func TestLevelHandlerFiltersBelowThreshold(t *testing.T) { + var buf bytes.Buffer + inner := slog.NewTextHandler(&buf, nil) + h := newLevelHandler(slog.LevelWarn, inner) + + if h.Enabled(context.Background(), slog.LevelInfo) { + t.Fatal("INFO should be disabled when threshold is WARN") + } + if !h.Enabled(context.Background(), slog.LevelWarn) { + t.Fatal("WARN should be enabled at threshold WARN") + } + if !h.Enabled(context.Background(), slog.LevelError) { + t.Fatal("ERROR should be enabled above threshold WARN") + } + + logger := slog.New(h) + logger.Info("hidden") + logger.Warn("visible") + + got := buf.String() + if strings.Contains(got, "hidden") { + t.Fatalf("INFO record leaked through level filter: %q", got) + } + if !strings.Contains(got, "visible") { + t.Fatalf("WARN record was dropped: %q", got) + } +} diff --git a/apps/workspace-engine/main.go b/apps/workspace-engine/main.go index eb55a43ae..03009295b 100644 --- a/apps/workspace-engine/main.go +++ b/apps/workspace-engine/main.go @@ -2,10 +2,12 @@ package main import ( "context" + "fmt" + "log/slog" + "os" "strings" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "workspace-engine/pkg/config" "workspace-engine/pkg/db" @@ -31,6 +33,13 @@ var ( ) func main() { + cleanupLogger, err := initLogger() + if err != nil { + fmt.Fprintf(os.Stderr, "failed to init logger: %v\n", err) + os.Exit(1) + } + defer cleanupLogger() + cleanupTracer, _ := initTracer() defer cleanupTracer() @@ -73,15 +82,15 @@ func main() { continue } - log.Info("Adding service", "name", s.Name()) + slog.Info("Adding service", "name", s.Name()) runner.Add(s) } - log.Info("Enabled services", "services", enabled) + slog.Info("Enabled services", "services", enabled) if err := runner.Run(ctx); err != nil { - log.Error("Runner failed", "error", err) + slog.Error("Runner failed", "error", err) } - log.Info("Workspace engine shut down") + slog.Info("Workspace engine shut down") } diff --git a/apps/workspace-engine/otel.go b/apps/workspace-engine/otel.go index 68a2c07a8..89e18dd1c 100644 --- a/apps/workspace-engine/otel.go +++ b/apps/workspace-engine/otel.go @@ -3,13 +3,18 @@ package main import ( "context" "fmt" + "log/slog" + "os" + "strings" "time" - "github.com/charmbracelet/log" + "go.opentelemetry.io/contrib/bridges/otelslog" "go.opentelemetry.io/contrib/instrumentation/runtime" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + sdklog "go.opentelemetry.io/otel/sdk/log" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -69,7 +74,7 @@ func initTracer() (func(), error) { otel.SetTracerProvider(tp) - log.Info("OpenTelemetry tracing initialized", + slog.Info("OpenTelemetry tracing initialized", "service", serviceName, "endpoint", endpoint) @@ -77,7 +82,7 @@ func initTracer() (func(), error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := tp.Shutdown(ctx); err != nil { - log.Error("Failed to shutdown tracer provider", "error", err) + slog.Error("Failed to shutdown tracer provider", "error", err) } }, nil } @@ -126,7 +131,7 @@ func initMetrics() (func(), error) { return nil, fmt.Errorf("failed to start runtime metrics: %w", err) } - log.Info("OpenTelemetry metrics initialized", + slog.Info("OpenTelemetry metrics initialized", "service", serviceName, "endpoint", endpoint, "export_interval", "10s") @@ -135,7 +140,69 @@ func initMetrics() (func(), error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := mp.Shutdown(ctx); err != nil { - log.Error("Failed to shutdown meter provider", "error", err) + slog.Error("Failed to shutdown meter provider", "error", err) + } + }, nil +} + +func parseLogLevel(s string) slog.Level { + switch strings.ToUpper(strings.TrimSpace(s)) { + case "DEBUG": + return slog.LevelDebug + case "WARN", "WARNING": + return slog.LevelWarn + case "ERROR": + return slog.LevelError + default: + return slog.LevelInfo + } +} + +func initLogger() (func(), error) { + ctx := context.Background() + + serviceName := config.Global.OTELServiceName + endpoint := stripScheme(config.Global.OTELExporterOTLPEndpoint) + level := parseLogLevel(config.Global.OTELLogLevel) + + res, err := resource.New(ctx, + resource.WithAttributes(semconv.ServiceNameKey.String(serviceName)), + ) + if err != nil { + return nil, fmt.Errorf("failed to create resource: %w", err) + } + + opts := []otlploghttp.Option{otlploghttp.WithInsecure()} + if endpoint != "" { + opts = append(opts, otlploghttp.WithEndpoint(endpoint)) + } + + exporter, err := otlploghttp.New(ctx, opts...) + if err != nil { + return nil, fmt.Errorf("failed to create OTLP log exporter: %w", err) + } + + lp := sdklog.NewLoggerProvider( + sdklog.WithProcessor(sdklog.NewBatchProcessor(exporter)), + sdklog.WithResource(res), + ) + + otelHandler := newLevelHandler(level, otelslog.NewHandler(serviceName, otelslog.WithLoggerProvider(lp))) + + stderrHandler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}) + + slog.SetDefault(slog.New(newTeeHandler(stderrHandler, otelHandler))) + + slog.Info("OpenTelemetry logging initialized", + "service", serviceName, + "endpoint", endpoint, + "level", level.String()) + + return func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := lp.Shutdown(ctx); err != nil { + slog.Error("Failed to shutdown logger provider", "error", err) } }, nil } diff --git a/apps/workspace-engine/pkg/db/client.go b/apps/workspace-engine/pkg/db/client.go index 35cad560e..1f06ce713 100644 --- a/apps/workspace-engine/pkg/db/client.go +++ b/apps/workspace-engine/pkg/db/client.go @@ -2,10 +2,11 @@ package db import ( "context" + "log/slog" + "os" "sync" "time" - "github.com/charmbracelet/log" "github.com/exaring/otelpgx" "github.com/jackc/pgx/v5/pgxpool" "workspace-engine/pkg/config" @@ -21,7 +22,8 @@ func GetPool(ctx context.Context) *pgxpool.Pool { once.Do(func() { cfg, err := pgxpool.ParseConfig(config.Global.PostgresURL) if err != nil { - log.Fatal("Failed to parse database config:", err) + slog.Error("Failed to parse database config", "error", err) + os.Exit(1) } cfg.MaxConns = int32(config.Global.PostgresMaxPoolSize) @@ -33,7 +35,8 @@ func GetPool(ctx context.Context) *pgxpool.Pool { pool, err = pgxpool.NewWithConfig(ctx, cfg) if err != nil { - log.Fatal("Failed to create database pool:", err) + slog.Error("Failed to create database pool", "error", err) + os.Exit(1) } }) return pool diff --git a/apps/workspace-engine/pkg/db/convert.go b/apps/workspace-engine/pkg/db/convert.go index bdfbc3f65..e566bba14 100644 --- a/apps/workspace-engine/pkg/db/convert.go +++ b/apps/workspace-engine/pkg/db/convert.go @@ -3,8 +3,8 @@ package db import ( "encoding/json" "fmt" + "log/slog" - "github.com/charmbracelet/log" "github.com/google/uuid" "workspace-engine/pkg/oapi" ) @@ -474,12 +474,12 @@ func ToOapiFullRelease(row GetDesiredReleaseByReleaseTargetRow) *oapi.Release { if len(row.Variables) > 0 { var raw map[string]json.RawMessage if err := json.Unmarshal(row.Variables, &raw); err != nil { - log.Error("failed to unmarshal release variables", "error", err) + slog.Error("failed to unmarshal release variables", "error", err) } else { for k, v := range raw { var lv oapi.LiteralValue if err := lv.UnmarshalJSON(v); err != nil { - log.Error("failed to unmarshal literal value", "key", k, "error", err) + slog.Error("failed to unmarshal literal value", "key", k, "error", err) continue } variables[k] = lv diff --git a/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go b/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go index 3980c0c68..35334bf87 100644 --- a/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go +++ b/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go @@ -3,6 +3,7 @@ package argo import ( "context" "fmt" + "log/slog" "strings" "time" @@ -10,7 +11,6 @@ import ( argocdapplication "github.com/argoproj/argo-cd/v3/pkg/apiclient/application" "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" "github.com/avast/retry-go" - "github.com/charmbracelet/log" ) // GoApplicationUpserter is the production implementation of @@ -64,7 +64,7 @@ func upsertWithRetry( retry.MaxDelay(10*time.Second), retry.DelayType(retry.BackOffDelay), retry.OnRetry(func(n uint, err error) { - log.Warn("Retrying ArgoCD application upsert", + slog.Warn("Retrying ArgoCD application upsert", "attempt", n+1, "error", err) }), diff --git a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go index a522d551d..84d87bfa7 100644 --- a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go +++ b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go @@ -6,12 +6,12 @@ import ( "encoding/hex" "encoding/json" "fmt" + "log/slog" "sort" "strings" "time" "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" - "github.com/charmbracelet/log" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" sigsyaml "sigs.k8s.io/yaml" @@ -88,7 +88,7 @@ func prepareTmpApp(app *v1alpha1.Application, tmpName string) *v1alpha1.Applicat func (p *ArgoCDPlanner) deleteTmpApp(ctx context.Context, serverAddr, apiKey, name string) { if err := p.deleter.DeleteApplication(ctx, serverAddr, apiKey, name); err != nil { - log.Warn("Failed to delete temporary plan application", "app", name, "error", err) + slog.Warn("Failed to delete temporary plan application", "app", name, "error", err) } } diff --git a/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go b/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go index e8ac3616e..fe2d35f1f 100644 --- a/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go +++ b/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go @@ -3,6 +3,7 @@ package argoworkflows import ( "context" "fmt" + "log/slog" "strings" "time" @@ -10,7 +11,6 @@ import ( workflowpkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/workflow" wfv1 "github.com/argoproj/argo-workflows/v4/pkg/apis/workflow/v1alpha1" "github.com/avast/retry-go" - "github.com/charmbracelet/log" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -104,7 +104,7 @@ func createWorkflowWithRetry( retry.MaxDelay(10*time.Second), retry.DelayType(retry.BackOffDelay), retry.OnRetry(func(n uint, err error) { - log.Warn("Retrying ArgoWorkflow submission", + slog.Warn("Retrying ArgoWorkflow submission", "attempt", n+1, "error", err) }), diff --git a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go index 6308c22fb..33b9d6a0d 100644 --- a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go +++ b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go @@ -3,9 +3,9 @@ package terraformcloud import ( "context" "fmt" + "log/slog" "os" - "github.com/charmbracelet/log" "workspace-engine/pkg/jobagents/types" "workspace-engine/pkg/oapi" ) @@ -83,7 +83,7 @@ func (t *TFE) Dispatch(ctx context.Context, job *oapi.Job) error { cfg.webhookUrl, webhookSecret, ); err != nil { - log.Warn("Failed to ensure notification config, continuing dispatch", "error", err) + slog.Warn("Failed to ensure notification config, continuing dispatch", "error", err) } if !cfg.triggerRunOnChange { @@ -112,6 +112,6 @@ func (t *TFE) updateJobStatus( metadata map[string]string, ) { if err := t.setter.UpdateJob(ctx, jobID, status, message, metadata); err != nil { - log.Error("Failed to update job status", "jobID", jobID, "error", err) + slog.Error("Failed to update job status", "jobID", jobID, "error", err) } } diff --git a/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go b/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go index 2b5f65374..0c055f266 100644 --- a/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go +++ b/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go @@ -2,8 +2,8 @@ package events import ( "context" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/pkg/reconcile" ) @@ -35,7 +35,7 @@ func EnqueueManyDeploymentResourceselectorEval( if len(params) == 0 { return nil } - log.Info("enqueueing deployment resourceselector evals", "count", len(params)) + slog.Info("enqueueing deployment resourceselector evals", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go b/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go index d8e8ef5e2..71a6572cb 100644 --- a/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go +++ b/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go @@ -3,8 +3,8 @@ package events import ( "context" "fmt" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/pkg/reconcile" ) @@ -42,7 +42,7 @@ func EnqueueManyDesiredRelease( if len(params) == 0 { return nil } - log.Info("enqueueing desired release", "count", len(params)) + slog.Info("enqueueing desired release", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/environmentselector.go b/apps/workspace-engine/pkg/reconcile/events/environmentselector.go index 3938ba5c8..c7e57d019 100644 --- a/apps/workspace-engine/pkg/reconcile/events/environmentselector.go +++ b/apps/workspace-engine/pkg/reconcile/events/environmentselector.go @@ -2,8 +2,8 @@ package events import ( "context" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/pkg/reconcile" ) @@ -35,7 +35,7 @@ func EnqueueManyEnvironmentResourceselectorEval( if len(params) == 0 { return nil } - log.Info("enqueueing environment resourceselector evals", "count", len(params)) + slog.Info("enqueueing environment resourceselector evals", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go b/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go index bf9743f42..9dd7aba57 100644 --- a/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go +++ b/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go @@ -3,8 +3,8 @@ package events import ( "context" "fmt" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/pkg/reconcile" ) @@ -42,7 +42,7 @@ func EnqueueManyJobEligibility( if len(params) == 0 { return nil } - log.Info("enqueueing job eligibility", "count", len(params)) + slog.Info("enqueueing job eligibility", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/policyeval.go b/apps/workspace-engine/pkg/reconcile/events/policyeval.go index ebb08e755..79912cb60 100644 --- a/apps/workspace-engine/pkg/reconcile/events/policyeval.go +++ b/apps/workspace-engine/pkg/reconcile/events/policyeval.go @@ -2,8 +2,8 @@ package events import ( "context" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/pkg/reconcile" ) @@ -35,7 +35,7 @@ func EnqueueManyPolicyEval( if len(params) == 0 { return nil } - log.Info("enqueueing policy eval", "count", len(params)) + slog.Info("enqueueing policy eval", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/worker.go b/apps/workspace-engine/pkg/reconcile/worker.go index f8bdeb26d..333ca0081 100644 --- a/apps/workspace-engine/pkg/reconcile/worker.go +++ b/apps/workspace-engine/pkg/reconcile/worker.go @@ -3,10 +3,10 @@ package reconcile import ( "context" "fmt" + "log/slog" "sync" "time" - "github.com/charmbracelet/log" "workspace-engine/svc" ) @@ -131,7 +131,7 @@ func (w *Worker) Run(ctx context.Context) error { LeaseDuration: w.cfg.LeaseDuration, }) if err != nil { - log.Error("error claiming items", "error", err) + slog.Error("error claiming items", "error", err) } if err == nil && len(items) > 0 { currentPoll = w.cfg.PollInterval @@ -212,7 +212,7 @@ func (w *Worker) processClaimedItem(ctx context.Context, item Item) { leaseWG.Wait() if processErr != nil { - log.Error( + slog.Error( "Error processing item", "item", item.ID, diff --git a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go index 80520a12d..ef7e3bbff 100644 --- a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go +++ b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go @@ -3,8 +3,8 @@ package policies import ( "context" "fmt" + "log/slog" - "github.com/charmbracelet/log" "github.com/google/uuid" gocache "github.com/patrickmn/go-cache" "go.opentelemetry.io/otel" @@ -121,13 +121,13 @@ func (p *PostgresGetPoliciesForReleaseTarget) GetPoliciesForReleaseTarget( for _, policy := range policies { policyID, err := uuid.Parse(policy.Id) if err != nil { - log.Error("failed to parse policy id", "policy_id", policy.Id, "error", err) + slog.Error("failed to parse policy id", "policy_id", policy.Id, "error", err) continue } policyIDs = append(policyIDs, policyID) } - log.Info( + slog.Info( "setting policies for release target", "policy_ids", len(policyIDs), diff --git a/apps/workspace-engine/pkg/store/resources/get_resources.go b/apps/workspace-engine/pkg/store/resources/get_resources.go index 08b9924ac..8b69053d4 100644 --- a/apps/workspace-engine/pkg/store/resources/get_resources.go +++ b/apps/workspace-engine/pkg/store/resources/get_resources.go @@ -3,15 +3,16 @@ package resources import ( "context" "fmt" + "log/slog" "time" - "github.com/charmbracelet/log" - "github.com/google/cel-go/cel" - "github.com/google/uuid" - "go.opentelemetry.io/otel" "workspace-engine/pkg/celutil" "workspace-engine/pkg/db" "workspace-engine/pkg/oapi" + + "github.com/google/cel-go/cel" + "github.com/google/uuid" + "go.opentelemetry.io/otel" ) var tracer = otel.Tracer("workspace-engine/pkg/store/resources") @@ -62,7 +63,7 @@ func (p *PostgresGetResources) GetResources( baseQuery += " AND " + filter.Clause args = append(args, filter.Args...) - log.Info("get resources optimization", "filter", filter.Clause) + slog.Info("get resources optimization", "filter", filter.Clause) } } diff --git a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go index 7779be0b4..7d644e17e 100644 --- a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go +++ b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go @@ -3,9 +3,9 @@ package eval import ( "context" "fmt" + "log/slog" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -69,7 +69,7 @@ func EvaluateRule( program, err := celEnv.Compile(rule.Cel) if err != nil { - log.Warn("Skipping rule with invalid CEL expression", + slog.Warn("Skipping rule with invalid CEL expression", "rule", rule.ID, "error", err) return nil, nil } @@ -150,7 +150,7 @@ func EvaluateRules( for _, rule := range rules { matches, err := EvaluateRule(ctx, entity, &rule, allCandidates) if err != nil { - log.Warn("Skipping rule due to evaluation error", + slog.Warn("Skipping rule due to evaluation error", "rule", rule.ID, "error", err) continue } diff --git a/apps/workspace-engine/svc/claimcleanup/service.go b/apps/workspace-engine/svc/claimcleanup/service.go index f7c595c0e..cc250d2d9 100644 --- a/apps/workspace-engine/svc/claimcleanup/service.go +++ b/apps/workspace-engine/svc/claimcleanup/service.go @@ -2,9 +2,9 @@ package claimcleanup import ( "context" + "log/slog" "time" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "workspace-engine/pkg/reconcile/postgres" "workspace-engine/svc" @@ -63,11 +63,11 @@ func (s *Service) run(ctx context.Context) { if ctx.Err() != nil { return } - log.Error("claim-cleanup: failed to release expired claims", "error", err) + slog.Error("claim-cleanup: failed to release expired claims", "error", err) continue } if released > 0 { - log.Info("claim-cleanup: released expired claims", "count", released) + slog.Info("claim-cleanup: released expired claims", "count", released) } } } diff --git a/apps/workspace-engine/svc/controllers/deploymentplan/controller.go b/apps/workspace-engine/svc/controllers/deploymentplan/controller.go index 427b8bb56..83594abd8 100644 --- a/apps/workspace-engine/svc/controllers/deploymentplan/controller.go +++ b/apps/workspace-engine/svc/controllers/deploymentplan/controller.go @@ -5,9 +5,10 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -253,13 +254,13 @@ func (c *Controller) processTarget( func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.DeploymentPlanKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating deployment plan reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -290,7 +291,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create deployment plan reconcile worker", "error", err) + slog.Error("Failed to create deployment plan reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/deploymentplanresult/controller.go b/apps/workspace-engine/svc/controllers/deploymentplanresult/controller.go index 4b933db7e..45d41d641 100644 --- a/apps/workspace-engine/svc/controllers/deploymentplanresult/controller.go +++ b/apps/workspace-engine/svc/controllers/deploymentplanresult/controller.go @@ -4,9 +4,10 @@ import ( "context" "encoding/json" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" @@ -203,13 +204,13 @@ func (c *Controller) Process(ctx context.Context, item reconcile.Item) (reconcil func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.DeploymentPlanTargetResultKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating deployment plan result reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -238,7 +239,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create deployment plan result reconcile worker", "error", err) + slog.Error("Failed to create deployment plan result reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go index 78a63dc15..d4431d6f4 100644 --- a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go +++ b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go @@ -3,9 +3,10 @@ package deploymentresourceselectoreval import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -68,7 +69,7 @@ func (c *Controller) Process(ctx context.Context, item reconcile.Item) (reconcil for _, resource := range resources { resourceIDUUID, err := uuid.Parse(resource.Id) if err != nil { - log.Error("failed to parse resource id", "resource_id", resource.Id, "error", err) + slog.Error("failed to parse resource id", "resource_id", resource.Id, "error", err) continue } matchedIDs = append(matchedIDs, resourceIDUUID) @@ -124,12 +125,12 @@ func NewController(getter Getter, setter Setter, queue reconcile.Queue) *Control func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.DeploymentResourceselectorEvalKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating deployment resourceselector eval worker", "maxConcurrency", maxConcurrency, ) @@ -157,7 +158,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create deployment resourceselector eval worker", "error", err) + slog.Error("Failed to create deployment resourceselector eval worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go index 06249a86c..eeaf8b86b 100644 --- a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go +++ b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go @@ -3,9 +3,10 @@ package desiredrelease import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -101,12 +102,12 @@ func NewController(getter Getter, setter Setter) *Controller { func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.DesiredReleaseKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating desired release reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -135,7 +136,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create desired release reconcile worker", "error", err) + slog.Error("Failed to create desired release reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go b/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go index 9f0530d0d..312db61a4 100644 --- a/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go +++ b/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go @@ -3,9 +3,9 @@ package desiredrelease import ( "context" "fmt" + "log/slog" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -67,7 +67,7 @@ func (r *reconciler) findDeployableVersion(ctx context.Context) *time.Time { *r.scope, ) if err != nil { - log.Error("find deployable version", "error", err) + slog.Error("find deployable version", "error", err) return nil } @@ -118,7 +118,7 @@ func Reconcile( ctx, span := tracer.Start(ctx, "desiredrelease.Reconcile") defer span.End() - log.Info("reconcile", "workspaceID", workspaceID, "rt", rt) + slog.Info("reconcile", "workspaceID", workspaceID, "rt", rt) workspaceIDUUID, err := uuid.Parse(workspaceID) if err != nil { @@ -131,7 +131,7 @@ func Reconcile( return nil, recordErr(span, "load input", err) } - log.Info("find deployable version") + slog.Info("find deployable version") nextTime := r.findDeployableVersion(ctx) if r.version == nil { diff --git a/apps/workspace-engine/svc/controllers/environmentresourceselectoreval/controller.go b/apps/workspace-engine/svc/controllers/environmentresourceselectoreval/controller.go index 65766e004..8942cc7cf 100644 --- a/apps/workspace-engine/svc/controllers/environmentresourceselectoreval/controller.go +++ b/apps/workspace-engine/svc/controllers/environmentresourceselectoreval/controller.go @@ -3,9 +3,10 @@ package environmentresourceselectoreval import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -83,12 +84,12 @@ func NewController(getter Getter, setter Setter) *Controller { func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.EnvironmentResourceselectorEvalKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating environment resourceselector eval worker", "maxConcurrency", maxConcurrency, ) @@ -115,7 +116,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create environment resourceselector eval worker", "error", err) + slog.Error("Failed to create environment resourceselector eval worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/forcedeploy/controller.go b/apps/workspace-engine/svc/controllers/forcedeploy/controller.go index db0db6f80..295d2af68 100644 --- a/apps/workspace-engine/svc/controllers/forcedeploy/controller.go +++ b/apps/workspace-engine/svc/controllers/forcedeploy/controller.go @@ -3,9 +3,10 @@ package forcedeploy import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -71,8 +72,8 @@ func NewController(getter Getter, setter Setter) *Controller { func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.ForceDeployKind @@ -96,7 +97,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { worker, err := reconcile.NewWorker(kind, queue, controller, nodeConfig) if err != nil { - log.Fatal("Failed to create force deploy reconcile worker", "error", err) + slog.Error("Failed to create force deploy reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go index ae2ee6901..bf6c73508 100644 --- a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go +++ b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go @@ -3,9 +3,9 @@ package forcedeploy import ( "context" "fmt" + "log/slog" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -41,7 +41,7 @@ func Reconcile( return nil, recordErr(span, "get desired release", err) } if release == nil { - log.Info("no desired release for release target, skipping", "rt", rt.ToOAPI().Key()) + slog.Info("no desired release for release target, skipping", "rt", rt.ToOAPI().Key()) return &ReconcileResult{}, nil } @@ -51,7 +51,7 @@ func Reconcile( } if len(activeJobs) > 0 { span.SetAttributes(attribute.Int("active_jobs", len(activeJobs))) - log.Info("release target has active jobs, requeueing", + slog.Info("release target has active jobs, requeueing", "rt", rt.ToOAPI().Key(), "activeJobs", len(activeJobs), ) diff --git a/apps/workspace-engine/svc/controllers/jobdispatch/controller.go b/apps/workspace-engine/svc/controllers/jobdispatch/controller.go index 267c05070..cfe6a0190 100644 --- a/apps/workspace-engine/svc/controllers/jobdispatch/controller.go +++ b/apps/workspace-engine/svc/controllers/jobdispatch/controller.go @@ -3,9 +3,10 @@ package jobdispatch import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -116,8 +117,8 @@ func NewController( func New(workerID string, pgxPool *pgxpool.Pool) *reconcile.Worker { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := "job-dispatch" @@ -147,7 +148,7 @@ func New(workerID string, pgxPool *pgxpool.Pool) *reconcile.Worker { ) maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating job dispatch reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -174,7 +175,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) *reconcile.Worker { nodeConfig, ) if err != nil { - log.Fatal("Failed to create job dispatch reconcile worker", "error", err) + slog.Error("Failed to create job dispatch reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/jobeligibility/controller.go b/apps/workspace-engine/svc/controllers/jobeligibility/controller.go index b07b07c1c..18fb48567 100644 --- a/apps/workspace-engine/svc/controllers/jobeligibility/controller.go +++ b/apps/workspace-engine/svc/controllers/jobeligibility/controller.go @@ -3,9 +3,10 @@ package jobeligibility import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -80,12 +81,12 @@ func NewController(getter Getter, setter Setter) *Controller { func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.JobEligibilityKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating job eligibility reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -116,7 +117,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create job eligibility reconcile worker", "error", err) + slog.Error("Failed to create job eligibility reconcile worker", "error", err) + os.Exit(1) } _ = ctx diff --git a/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go b/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go index 5074145c2..c8b9bfd9e 100644 --- a/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go +++ b/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go @@ -3,8 +3,8 @@ package jobeligibility import ( "context" "errors" + "log/slog" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5" "go.opentelemetry.io/otel/attribute" @@ -71,7 +71,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( ) map[string]*oapi.Job { deploymentID, err := uuid.Parse(releaseTarget.DeploymentId) if err != nil { - log.Error( + slog.Error( "failed to parse deployment id", "deploymentID", releaseTarget.DeploymentId, @@ -82,7 +82,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( } environmentID, err := uuid.Parse(releaseTarget.EnvironmentId) if err != nil { - log.Error( + slog.Error( "failed to parse environment id", "environmentID", releaseTarget.EnvironmentId, @@ -93,7 +93,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( } resourceID, err := uuid.Parse(releaseTarget.ResourceId) if err != nil { - log.Error( + slog.Error( "failed to parse resource id", "resourceID", releaseTarget.ResourceId, @@ -108,7 +108,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( ResourceID: resourceID, }) if err != nil { - log.Error( + slog.Error( "failed to get jobs for release target", "releaseTarget", releaseTarget.Key(), @@ -131,7 +131,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( ) map[string]*oapi.Job { deploymentID, err := uuid.Parse(releaseTarget.DeploymentId) if err != nil { - log.Error( + slog.Error( "failed to parse deployment id", "deploymentID", releaseTarget.DeploymentId, @@ -142,7 +142,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( } environmentID, err := uuid.Parse(releaseTarget.EnvironmentId) if err != nil { - log.Error( + slog.Error( "failed to parse environment id", "environmentID", releaseTarget.EnvironmentId, @@ -153,7 +153,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( } resourceID, err := uuid.Parse(releaseTarget.ResourceId) if err != nil { - log.Error( + slog.Error( "failed to parse resource id", "resourceID", releaseTarget.ResourceId, @@ -170,7 +170,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( Statuses: []string{"in_progress", "action_required", "pending"}, }) if err != nil { - log.Error( + slog.Error( "failed to get jobs for release target", "releaseTarget", releaseTarget.Key(), diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/controller.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/controller.go index 46f4e5f61..f4591098e 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/controller.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/controller.go @@ -3,9 +3,10 @@ package jobverificationmetric import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -61,12 +62,12 @@ func (c *Controller) Process(ctx context.Context, item reconcile.Item) (reconcil func New(workerID string, pgxPool *pgxpool.Pool) *reconcile.Worker { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } maxConcurrency := config.GetMaxConcurrency(JobVerificationMetricKind) - log.Debug( + slog.Debug( "Creating job verification metric reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -93,7 +94,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) *reconcile.Worker { nodeConfig, ) if err != nil { - log.Fatal("Failed to create verification reconcile worker", "error", err) + slog.Error("Failed to create verification reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go index 7d5a0511c..d3da49758 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go @@ -4,8 +4,8 @@ import ( "context" "encoding/json" "fmt" + "log/slog" - "github.com/charmbracelet/log" "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider" "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog" httpProvider "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http" @@ -62,7 +62,7 @@ func Measure( if hasFailureCondition { failureEvaluator, err := NewEvaluator(*metric.FailureCondition) if err != nil { - log.Error("Failed to create failure evaluator", "error", err) + slog.Error("Failed to create failure evaluator", "error", err) return Measurement{}, err } @@ -90,7 +90,7 @@ func Measure( successEvaluator, err := NewEvaluator(metric.SuccessCondition) if err != nil { - log.Error("Failed to create success evaluator", "error", err) + slog.Error("Failed to create success evaluator", "error", err) return Measurement{}, err } diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go index d7a9e548f..c1a02c747 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go @@ -6,11 +6,11 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "reflect" "time" - "github.com/charmbracelet/log" "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider" ) @@ -157,7 +157,7 @@ func (p *Provider) Measure( duration := time.Since(startTime) if err != nil { - log.Error("Datadog metric request failed", "site", resolved.Site, "error", err) + slog.Error("Datadog metric request failed", "site", resolved.Site, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() @@ -169,19 +169,19 @@ func (p *Provider) Measure( var rawJson any if err := json.Unmarshal(respBody, &rawJson); err != nil { - log.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } var jsonResponse datadogResponseV2 if err := json.Unmarshal(respBody, &jsonResponse); err != nil { - log.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } queries, err := extractQueryValue(jsonResponse) if err != nil { - log.Warn("Could not extract query values", "error", err) + slog.Warn("Could not extract query values", "error", err) } data := map[string]any{ @@ -193,7 +193,7 @@ func (p *Provider) Measure( "queries": queries, } - log.Debug("Datadog metric measurement", + slog.Debug("Datadog metric measurement", "site", resolved.Site, "status", resp.StatusCode, "duration", duration) diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go index bd540bb01..c5f6f5579 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go @@ -6,10 +6,10 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "time" - "github.com/charmbracelet/log" "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider" ) @@ -109,7 +109,7 @@ func (p *Provider) Measure( duration := time.Since(startTime) if err != nil { - log.Error("HTTP metric request failed", "url", resolved.URL, "error", err) + slog.Error("HTTP metric request failed", "url", resolved.URL, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() @@ -128,7 +128,7 @@ func (p *Provider) Measure( "duration": duration.Milliseconds(), } - log.Debug("HTTP metric measurement", + slog.Debug("HTTP metric measurement", "url", resolved.URL, "status", resp.StatusCode, "duration", duration) diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go index bbb936730..88b22b7b3 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go @@ -6,13 +6,13 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "net/url" "strconv" "strings" "time" - "github.com/charmbracelet/log" "github.com/prometheus/common/model" "workspace-engine/svc/controllers/jobverificationmetric/metrics/provider" ) @@ -122,7 +122,7 @@ func (p *PrometheusProvider) Measure( resp, err := client.Do(req) duration := time.Since(startTime) if err != nil { - log.Error( + slog.Error( "Prometheus metric request failed", "address", resolvedProvider.Address, @@ -143,7 +143,7 @@ func (p *PrometheusProvider) Measure( return time.Time{}, nil, err } - log.Debug("Prometheus metric measurement", + slog.Debug("Prometheus metric measurement", "address", resolvedProvider.Address, "query", resolvedProvider.Query, "status", resp.StatusCode, @@ -378,7 +378,7 @@ func buildResultData( value, results, err := extractResults(promResp) if err != nil { - log.Warn("Could not extract Prometheus result values", "error", err) + slog.Warn("Could not extract Prometheus result values", "error", err) } data["value"] = value @@ -416,7 +416,7 @@ func extractVectorResults(raw json.RawMessage) (*float64, []map[string]any, erro for i, v := range vectors { val, err := parseScalarValue(v.Value[1]) if err != nil { - log.Warn("Could not parse vector value", "index", i, "error", err) + slog.Warn("Could not parse vector value", "index", i, "error", err) continue } if primary == nil { @@ -451,7 +451,7 @@ func extractMatrixResults(raw json.RawMessage) (*float64, []map[string]any, erro lastPair := m.Values[len(m.Values)-1] val, err := parseScalarValue(lastPair[1]) if err != nil { - log.Warn("Could not parse matrix value", "index", i, "error", err) + slog.Warn("Could not parse matrix value", "index", i, "error", err) continue } if primary == nil { diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go index 0986b8f1c..66eda8211 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go @@ -3,9 +3,9 @@ package jobverificationmetric import ( "context" "fmt" + "log/slog" "time" - "github.com/charmbracelet/log" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -51,7 +51,7 @@ func Reconcile( span.AddEvent("interval not yet elapsed, deferring", trace.WithAttributes(attribute.String("remaining", remaining.String())), ) - log.Info("Interval not yet elapsed, deferring", "remaining", remaining) + slog.Info("Interval not yet elapsed, deferring", "remaining", remaining) return &ReconcileResult{RequeueAfter: &remaining}, nil } diff --git a/apps/workspace-engine/svc/controllers/policyeval/controller.go b/apps/workspace-engine/svc/controllers/policyeval/controller.go index 37116c418..b9e3f2973 100644 --- a/apps/workspace-engine/svc/controllers/policyeval/controller.go +++ b/apps/workspace-engine/svc/controllers/policyeval/controller.go @@ -3,9 +3,10 @@ package policyeval import ( "context" "fmt" + "log/slog" + "os" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -88,12 +89,12 @@ func NewController(getter Getter, setter Setter) *Controller { // New creates a production-ready policy eval worker backed by Postgres. func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.PolicyEvalKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating policy eval reconcile worker", "maxConcurrency", maxConcurrency, ) @@ -121,7 +122,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create policy eval reconcile worker", "error", err) + slog.Error("Failed to create policy eval reconcile worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/controllers/relationshipeval/controller.go b/apps/workspace-engine/svc/controllers/relationshipeval/controller.go index b5a1019aa..5f855bddc 100644 --- a/apps/workspace-engine/svc/controllers/relationshipeval/controller.go +++ b/apps/workspace-engine/svc/controllers/relationshipeval/controller.go @@ -3,11 +3,12 @@ package relationshipeval import ( "context" "fmt" + "log/slog" + "os" "runtime" "strings" "time" - "github.com/charmbracelet/log" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "go.opentelemetry.io/otel" @@ -184,12 +185,12 @@ func NewController(getter Getter, setter Setter) *Controller { func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { if pgxPool == nil { - log.Fatal("Failed to get pgx pool") - panic("failed to get pgx pool") + slog.Error("Failed to get pgx pool") + os.Exit(1) } kind := events.RelationshipEvalKind maxConcurrency := config.GetMaxConcurrency(kind) - log.Debug( + slog.Debug( "Creating relationship eval worker", "maxConcurrency", maxConcurrency, ) @@ -214,7 +215,8 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - log.Fatal("Failed to create relationship eval worker", "error", err) + slog.Error("Failed to create relationship eval worker", "error", err) + os.Exit(1) } return worker diff --git a/apps/workspace-engine/svc/http/http.go b/apps/workspace-engine/svc/http/http.go index 7262c70d9..e8dd41cdf 100644 --- a/apps/workspace-engine/svc/http/http.go +++ b/apps/workspace-engine/svc/http/http.go @@ -3,9 +3,10 @@ package http import ( "context" "fmt" + "log/slog" "net/http" + "os" - "github.com/charmbracelet/log" "github.com/jackc/pgx/v5/pgxpool" "workspace-engine/pkg/config" "workspace-engine/svc" @@ -38,9 +39,10 @@ func (s *Service) Start(_ context.Context) error { } go func() { - log.Info("HTTP server listening", "address", addr) + slog.Info("HTTP server listening", "address", addr) if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatal("HTTP ListenAndServe error", "error", err) + slog.Error("HTTP ListenAndServe error", "error", err) + os.Exit(1) } }() diff --git a/apps/workspace-engine/svc/http/server/server.go b/apps/workspace-engine/svc/http/server/server.go index 2ed98d2b3..dc0f8f982 100644 --- a/apps/workspace-engine/svc/http/server/server.go +++ b/apps/workspace-engine/svc/http/server/server.go @@ -2,10 +2,10 @@ package server import ( "fmt" + "log/slog" "net/http" "time" - "github.com/charmbracelet/log" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgxpool" swaggerfiles "github.com/swaggo/files" @@ -88,7 +88,7 @@ func LoggerMiddleware() gin.HandlerFunc { errMsg = c.Errors.String() } - log.Error("request", + slog.Error("request", "method", method, "path", path, "status", statusCode, diff --git a/apps/workspace-engine/svc/pprof/pprof.go b/apps/workspace-engine/svc/pprof/pprof.go index 0ca7260c0..8b15154fd 100644 --- a/apps/workspace-engine/svc/pprof/pprof.go +++ b/apps/workspace-engine/svc/pprof/pprof.go @@ -3,10 +3,10 @@ package pprof import ( "context" "fmt" + "log/slog" "net/http" _ "net/http/pprof" - "github.com/charmbracelet/log" "workspace-engine/svc" ) @@ -31,9 +31,9 @@ func (s *Service) Start(_ context.Context) error { } go func() { - log.Info("pprof server listening", "address", s.addr) + slog.Info("pprof server listening", "address", s.addr) if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Error("pprof ListenAndServe error", "error", err) + slog.Error("pprof ListenAndServe error", "error", err) } }() diff --git a/apps/workspace-engine/svc/service.go b/apps/workspace-engine/svc/service.go index f53c7911d..8478e21b4 100644 --- a/apps/workspace-engine/svc/service.go +++ b/apps/workspace-engine/svc/service.go @@ -2,14 +2,13 @@ package svc import ( "context" + "log/slog" "os" "os/signal" "slices" "sync" "syscall" "time" - - "github.com/charmbracelet/log" ) // Service represents a long-running subsystem of the workspace-engine (HTTP @@ -64,7 +63,7 @@ func (r *Runner) Run(ctx context.Context) error { defer cancel() for _, svc := range r.services { - log.Info("Starting service", "service", svc.Name()) + slog.Info("Starting service", "service", svc.Name()) if err := svc.Start(ctx); err != nil { cancel() return err @@ -76,9 +75,9 @@ func (r *Runner) Run(ctx context.Context) error { select { case sig := <-sigChan: - log.Warn("Received signal, shutting down", "signal", sig) + slog.Warn("Received signal, shutting down", "signal", sig) case <-ctx.Done(): - log.Warn("Context cancelled, shutting down") + slog.Warn("Context cancelled, shutting down") } cancel() @@ -90,9 +89,9 @@ func (r *Runner) Run(ctx context.Context) error { for _, v := range slices.Backward(r.services) { svc := v wg.Go(func() { - log.Info("Stopping service", "service", svc.Name()) + slog.Info("Stopping service", "service", svc.Name()) if err := svc.Stop(shutdownCtx); err != nil { - log.Error("Service stop error", "service", svc.Name(), "error", err) + slog.Error("Service stop error", "service", svc.Name(), "error", err) } }) } @@ -105,9 +104,9 @@ func (r *Runner) Run(ctx context.Context) error { select { case <-done: - log.Info("All services stopped") + slog.Info("All services stopped") case <-shutdownCtx.Done(): - log.Warn("Shutdown timeout exceeded, forcing exit") + slog.Warn("Shutdown timeout exceeded, forcing exit") } return nil From 6fd7d46a8c9ce9aef282795be2f15775f4493366 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 11:53:05 -0500 Subject: [PATCH 05/13] Tune export interval from default 1s --- apps/workspace-engine/otel.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/workspace-engine/otel.go b/apps/workspace-engine/otel.go index 89e18dd1c..d841b0dab 100644 --- a/apps/workspace-engine/otel.go +++ b/apps/workspace-engine/otel.go @@ -183,7 +183,9 @@ func initLogger() (func(), error) { } lp := sdklog.NewLoggerProvider( - sdklog.WithProcessor(sdklog.NewBatchProcessor(exporter)), + sdklog.WithProcessor(sdklog.NewBatchProcessor(exporter, + sdklog.WithExportInterval(5*time.Second), + )), sdklog.WithResource(res), ) From 31b78a454a482fe4f1c34bf4886bf193c1acc723 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 12:05:44 -0500 Subject: [PATCH 06/13] Use context logging for trace correlation --- apps/workspace-engine/pkg/db/client.go | 4 ++-- .../pkg/jobagents/argo/application_upserter.go | 2 +- .../pkg/jobagents/argo/argocd_plan.go | 2 +- .../argoworkflows/workflow_submitter.go | 2 +- .../pkg/jobagents/terraformcloud/tfe.go | 4 ++-- .../pkg/reconcile/events/deploymentselector.go | 2 +- .../pkg/reconcile/events/desiredrelease.go | 2 +- .../pkg/reconcile/events/environmentselector.go | 2 +- .../pkg/reconcile/events/jobeligibility.go | 2 +- .../pkg/reconcile/events/policyeval.go | 2 +- apps/workspace-engine/pkg/reconcile/worker.go | 4 ++-- .../store/policies/get_for_release_targets.go | 4 ++-- .../pkg/store/resources/get_resources.go | 2 +- .../pkg/workspace/relationships/eval/evaluate.go | 4 ++-- .../evaluator/deploymentdependency/getter.go | 4 ++-- .../workspace-engine/svc/claimcleanup/service.go | 4 ++-- .../deploymentresourceselectoreval/controller.go | 2 +- .../svc/controllers/desiredrelease/reconcile.go | 6 +++--- .../svc/controllers/forcedeploy/reconcile.go | 4 ++-- .../jobeligibility/getters_postgres.go | 16 ++++++++-------- .../jobverificationmetric/metrics/metric.go | 4 ++-- .../metrics/provider/datadog/datadog.go | 10 +++++----- .../metrics/provider/http/http.go | 4 ++-- .../metrics/provider/prometheus/prometheus.go | 4 ++-- .../jobverificationmetric/reconcile.go | 2 +- apps/workspace-engine/svc/http/server/server.go | 2 +- apps/workspace-engine/svc/service.go | 14 +++++++------- 27 files changed, 57 insertions(+), 57 deletions(-) diff --git a/apps/workspace-engine/pkg/db/client.go b/apps/workspace-engine/pkg/db/client.go index 1f06ce713..1ada719b8 100644 --- a/apps/workspace-engine/pkg/db/client.go +++ b/apps/workspace-engine/pkg/db/client.go @@ -22,7 +22,7 @@ func GetPool(ctx context.Context) *pgxpool.Pool { once.Do(func() { cfg, err := pgxpool.ParseConfig(config.Global.PostgresURL) if err != nil { - slog.Error("Failed to parse database config", "error", err) + slog.ErrorContext(ctx, "Failed to parse database config", "error", err) os.Exit(1) } @@ -35,7 +35,7 @@ func GetPool(ctx context.Context) *pgxpool.Pool { pool, err = pgxpool.NewWithConfig(ctx, cfg) if err != nil { - slog.Error("Failed to create database pool", "error", err) + slog.ErrorContext(ctx, "Failed to create database pool", "error", err) os.Exit(1) } }) diff --git a/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go b/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go index 35334bf87..7c02b510d 100644 --- a/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go +++ b/apps/workspace-engine/pkg/jobagents/argo/application_upserter.go @@ -64,7 +64,7 @@ func upsertWithRetry( retry.MaxDelay(10*time.Second), retry.DelayType(retry.BackOffDelay), retry.OnRetry(func(n uint, err error) { - slog.Warn("Retrying ArgoCD application upsert", + slog.WarnContext(ctx, "Retrying ArgoCD application upsert", "attempt", n+1, "error", err) }), diff --git a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go index 84d87bfa7..2389d9054 100644 --- a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go +++ b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go @@ -88,7 +88,7 @@ func prepareTmpApp(app *v1alpha1.Application, tmpName string) *v1alpha1.Applicat func (p *ArgoCDPlanner) deleteTmpApp(ctx context.Context, serverAddr, apiKey, name string) { if err := p.deleter.DeleteApplication(ctx, serverAddr, apiKey, name); err != nil { - slog.Warn("Failed to delete temporary plan application", "app", name, "error", err) + slog.WarnContext(ctx, "Failed to delete temporary plan application", "app", name, "error", err) } } diff --git a/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go b/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go index fe2d35f1f..643a6b926 100644 --- a/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go +++ b/apps/workspace-engine/pkg/jobagents/argoworkflows/workflow_submitter.go @@ -104,7 +104,7 @@ func createWorkflowWithRetry( retry.MaxDelay(10*time.Second), retry.DelayType(retry.BackOffDelay), retry.OnRetry(func(n uint, err error) { - slog.Warn("Retrying ArgoWorkflow submission", + slog.WarnContext(ctx, "Retrying ArgoWorkflow submission", "attempt", n+1, "error", err) }), diff --git a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go index 33b9d6a0d..14a828fd6 100644 --- a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go +++ b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go @@ -83,7 +83,7 @@ func (t *TFE) Dispatch(ctx context.Context, job *oapi.Job) error { cfg.webhookUrl, webhookSecret, ); err != nil { - slog.Warn("Failed to ensure notification config, continuing dispatch", "error", err) + slog.WarnContext(ctx, "Failed to ensure notification config, continuing dispatch", "error", err) } if !cfg.triggerRunOnChange { @@ -112,6 +112,6 @@ func (t *TFE) updateJobStatus( metadata map[string]string, ) { if err := t.setter.UpdateJob(ctx, jobID, status, message, metadata); err != nil { - slog.Error("Failed to update job status", "jobID", jobID, "error", err) + slog.ErrorContext(ctx, "Failed to update job status", "jobID", jobID, "error", err) } } diff --git a/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go b/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go index 0c055f266..9487f3bbb 100644 --- a/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go +++ b/apps/workspace-engine/pkg/reconcile/events/deploymentselector.go @@ -35,7 +35,7 @@ func EnqueueManyDeploymentResourceselectorEval( if len(params) == 0 { return nil } - slog.Info("enqueueing deployment resourceselector evals", "count", len(params)) + slog.InfoContext(ctx, "enqueueing deployment resourceselector evals", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go b/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go index 71a6572cb..b09d4943d 100644 --- a/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go +++ b/apps/workspace-engine/pkg/reconcile/events/desiredrelease.go @@ -42,7 +42,7 @@ func EnqueueManyDesiredRelease( if len(params) == 0 { return nil } - slog.Info("enqueueing desired release", "count", len(params)) + slog.InfoContext(ctx, "enqueueing desired release", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/environmentselector.go b/apps/workspace-engine/pkg/reconcile/events/environmentselector.go index c7e57d019..4bc3093d7 100644 --- a/apps/workspace-engine/pkg/reconcile/events/environmentselector.go +++ b/apps/workspace-engine/pkg/reconcile/events/environmentselector.go @@ -35,7 +35,7 @@ func EnqueueManyEnvironmentResourceselectorEval( if len(params) == 0 { return nil } - slog.Info("enqueueing environment resourceselector evals", "count", len(params)) + slog.InfoContext(ctx, "enqueueing environment resourceselector evals", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go b/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go index 9dd7aba57..c7f5e8a2f 100644 --- a/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go +++ b/apps/workspace-engine/pkg/reconcile/events/jobeligibility.go @@ -42,7 +42,7 @@ func EnqueueManyJobEligibility( if len(params) == 0 { return nil } - slog.Info("enqueueing job eligibility", "count", len(params)) + slog.InfoContext(ctx, "enqueueing job eligibility", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/events/policyeval.go b/apps/workspace-engine/pkg/reconcile/events/policyeval.go index 79912cb60..2a90d7d1a 100644 --- a/apps/workspace-engine/pkg/reconcile/events/policyeval.go +++ b/apps/workspace-engine/pkg/reconcile/events/policyeval.go @@ -35,7 +35,7 @@ func EnqueueManyPolicyEval( if len(params) == 0 { return nil } - slog.Info("enqueueing policy eval", "count", len(params)) + slog.InfoContext(ctx, "enqueueing policy eval", "count", len(params)) items := make([]reconcile.EnqueueParams, len(params)) for i, p := range params { items[i] = reconcile.EnqueueParams{ diff --git a/apps/workspace-engine/pkg/reconcile/worker.go b/apps/workspace-engine/pkg/reconcile/worker.go index 333ca0081..83b2ee70a 100644 --- a/apps/workspace-engine/pkg/reconcile/worker.go +++ b/apps/workspace-engine/pkg/reconcile/worker.go @@ -131,7 +131,7 @@ func (w *Worker) Run(ctx context.Context) error { LeaseDuration: w.cfg.LeaseDuration, }) if err != nil { - slog.Error("error claiming items", "error", err) + slog.ErrorContext(ctx, "error claiming items", "error", err) } if err == nil && len(items) > 0 { currentPoll = w.cfg.PollInterval @@ -212,7 +212,7 @@ func (w *Worker) processClaimedItem(ctx context.Context, item Item) { leaseWG.Wait() if processErr != nil { - slog.Error( + slog.ErrorContext(ctx, "Error processing item", "item", item.ID, diff --git a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go index ef7e3bbff..7910eff03 100644 --- a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go +++ b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go @@ -121,13 +121,13 @@ func (p *PostgresGetPoliciesForReleaseTarget) GetPoliciesForReleaseTarget( for _, policy := range policies { policyID, err := uuid.Parse(policy.Id) if err != nil { - slog.Error("failed to parse policy id", "policy_id", policy.Id, "error", err) + slog.ErrorContext(ctx, "failed to parse policy id", "policy_id", policy.Id, "error", err) continue } policyIDs = append(policyIDs, policyID) } - slog.Info( + slog.InfoContext(ctx, "setting policies for release target", "policy_ids", len(policyIDs), diff --git a/apps/workspace-engine/pkg/store/resources/get_resources.go b/apps/workspace-engine/pkg/store/resources/get_resources.go index 8b69053d4..54325db73 100644 --- a/apps/workspace-engine/pkg/store/resources/get_resources.go +++ b/apps/workspace-engine/pkg/store/resources/get_resources.go @@ -63,7 +63,7 @@ func (p *PostgresGetResources) GetResources( baseQuery += " AND " + filter.Clause args = append(args, filter.Args...) - slog.Info("get resources optimization", "filter", filter.Clause) + slog.InfoContext(ctx, "get resources optimization", "filter", filter.Clause) } } diff --git a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go index 7d644e17e..f55039a81 100644 --- a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go +++ b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go @@ -69,7 +69,7 @@ func EvaluateRule( program, err := celEnv.Compile(rule.Cel) if err != nil { - slog.Warn("Skipping rule with invalid CEL expression", + slog.WarnContext(ctx, "Skipping rule with invalid CEL expression", "rule", rule.ID, "error", err) return nil, nil } @@ -150,7 +150,7 @@ func EvaluateRules( for _, rule := range rules { matches, err := EvaluateRule(ctx, entity, &rule, allCandidates) if err != nil { - slog.Warn("Skipping rule due to evaluation error", + slog.WarnContext(ctx, "Skipping rule due to evaluation error", "rule", rule.ID, "error", err) continue } diff --git a/apps/workspace-engine/pkg/workspace/releasemanager/policy/evaluator/deploymentdependency/getter.go b/apps/workspace-engine/pkg/workspace/releasemanager/policy/evaluator/deploymentdependency/getter.go index 99c092620..b3a6ad8a4 100644 --- a/apps/workspace-engine/pkg/workspace/releasemanager/policy/evaluator/deploymentdependency/getter.go +++ b/apps/workspace-engine/pkg/workspace/releasemanager/policy/evaluator/deploymentdependency/getter.go @@ -40,7 +40,7 @@ func (p *PostgresGetters) GetReleaseTargetsForResource( ) []*oapi.ReleaseTarget { rows, err := p.queries.GetReleaseTargetsForResource(ctx, uuid.MustParse(resourceID)) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to get release targets for resource", "resourceID", resourceID, @@ -77,7 +77,7 @@ func (p *PostgresGetters) GetCurrentlyDeployedVersion( ) if err != nil { if !errors.Is(err, pgx.ErrNoRows) { - slog.Error( + slog.ErrorContext(ctx, "failed to get current release for release target", "releaseTarget", rt.Key(), diff --git a/apps/workspace-engine/svc/claimcleanup/service.go b/apps/workspace-engine/svc/claimcleanup/service.go index cc250d2d9..db7b626aa 100644 --- a/apps/workspace-engine/svc/claimcleanup/service.go +++ b/apps/workspace-engine/svc/claimcleanup/service.go @@ -63,11 +63,11 @@ func (s *Service) run(ctx context.Context) { if ctx.Err() != nil { return } - slog.Error("claim-cleanup: failed to release expired claims", "error", err) + slog.ErrorContext(ctx, "claim-cleanup: failed to release expired claims", "error", err) continue } if released > 0 { - slog.Info("claim-cleanup: released expired claims", "count", released) + slog.InfoContext(ctx, "claim-cleanup: released expired claims", "count", released) } } } diff --git a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go index d4431d6f4..01650780a 100644 --- a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go +++ b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go @@ -69,7 +69,7 @@ func (c *Controller) Process(ctx context.Context, item reconcile.Item) (reconcil for _, resource := range resources { resourceIDUUID, err := uuid.Parse(resource.Id) if err != nil { - slog.Error("failed to parse resource id", "resource_id", resource.Id, "error", err) + slog.ErrorContext(ctx, "failed to parse resource id", "resource_id", resource.Id, "error", err) continue } matchedIDs = append(matchedIDs, resourceIDUUID) diff --git a/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go b/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go index 312db61a4..78697f69f 100644 --- a/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go +++ b/apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go @@ -67,7 +67,7 @@ func (r *reconciler) findDeployableVersion(ctx context.Context) *time.Time { *r.scope, ) if err != nil { - slog.Error("find deployable version", "error", err) + slog.ErrorContext(ctx, "find deployable version", "error", err) return nil } @@ -118,7 +118,7 @@ func Reconcile( ctx, span := tracer.Start(ctx, "desiredrelease.Reconcile") defer span.End() - slog.Info("reconcile", "workspaceID", workspaceID, "rt", rt) + slog.InfoContext(ctx, "reconcile", "workspaceID", workspaceID, "rt", rt) workspaceIDUUID, err := uuid.Parse(workspaceID) if err != nil { @@ -131,7 +131,7 @@ func Reconcile( return nil, recordErr(span, "load input", err) } - slog.Info("find deployable version") + slog.InfoContext(ctx, "find deployable version") nextTime := r.findDeployableVersion(ctx) if r.version == nil { diff --git a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go index bf6c73508..2027775ad 100644 --- a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go +++ b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go @@ -41,7 +41,7 @@ func Reconcile( return nil, recordErr(span, "get desired release", err) } if release == nil { - slog.Info("no desired release for release target, skipping", "rt", rt.ToOAPI().Key()) + slog.InfoContext(ctx, "no desired release for release target, skipping", "rt", rt.ToOAPI().Key()) return &ReconcileResult{}, nil } @@ -51,7 +51,7 @@ func Reconcile( } if len(activeJobs) > 0 { span.SetAttributes(attribute.Int("active_jobs", len(activeJobs))) - slog.Info("release target has active jobs, requeueing", + slog.InfoContext(ctx, "release target has active jobs, requeueing", "rt", rt.ToOAPI().Key(), "activeJobs", len(activeJobs), ) diff --git a/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go b/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go index c8b9bfd9e..c6b6bdf4c 100644 --- a/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go +++ b/apps/workspace-engine/svc/controllers/jobeligibility/getters_postgres.go @@ -71,7 +71,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( ) map[string]*oapi.Job { deploymentID, err := uuid.Parse(releaseTarget.DeploymentId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse deployment id", "deploymentID", releaseTarget.DeploymentId, @@ -82,7 +82,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( } environmentID, err := uuid.Parse(releaseTarget.EnvironmentId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse environment id", "environmentID", releaseTarget.EnvironmentId, @@ -93,7 +93,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( } resourceID, err := uuid.Parse(releaseTarget.ResourceId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse resource id", "resourceID", releaseTarget.ResourceId, @@ -108,7 +108,7 @@ func (g *PostgresGetter) GetJobsForReleaseTarget( ResourceID: resourceID, }) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to get jobs for release target", "releaseTarget", releaseTarget.Key(), @@ -131,7 +131,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( ) map[string]*oapi.Job { deploymentID, err := uuid.Parse(releaseTarget.DeploymentId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse deployment id", "deploymentID", releaseTarget.DeploymentId, @@ -142,7 +142,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( } environmentID, err := uuid.Parse(releaseTarget.EnvironmentId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse environment id", "environmentID", releaseTarget.EnvironmentId, @@ -153,7 +153,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( } resourceID, err := uuid.Parse(releaseTarget.ResourceId) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to parse resource id", "resourceID", releaseTarget.ResourceId, @@ -170,7 +170,7 @@ func (g *PostgresGetter) GetJobsInProcessingStateForReleaseTarget( Statuses: []string{"in_progress", "action_required", "pending"}, }) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "failed to get jobs for release target", "releaseTarget", releaseTarget.Key(), diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go index d3da49758..48539f3b0 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/metric.go @@ -62,7 +62,7 @@ func Measure( if hasFailureCondition { failureEvaluator, err := NewEvaluator(*metric.FailureCondition) if err != nil { - slog.Error("Failed to create failure evaluator", "error", err) + slog.ErrorContext(ctx, "Failed to create failure evaluator", "error", err) return Measurement{}, err } @@ -90,7 +90,7 @@ func Measure( successEvaluator, err := NewEvaluator(metric.SuccessCondition) if err != nil { - slog.Error("Failed to create success evaluator", "error", err) + slog.ErrorContext(ctx, "Failed to create success evaluator", "error", err) return Measurement{}, err } diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go index c1a02c747..0275c32f1 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go @@ -157,7 +157,7 @@ func (p *Provider) Measure( duration := time.Since(startTime) if err != nil { - slog.Error("Datadog metric request failed", "site", resolved.Site, "error", err) + slog.ErrorContext(ctx, "Datadog metric request failed", "site", resolved.Site, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() @@ -169,19 +169,19 @@ func (p *Provider) Measure( var rawJson any if err := json.Unmarshal(respBody, &rawJson); err != nil { - slog.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.ErrorContext(ctx, "Failed to parse Datadog response", "body", string(respBody), "error", err) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } var jsonResponse datadogResponseV2 if err := json.Unmarshal(respBody, &jsonResponse); err != nil { - slog.Error("Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.ErrorContext(ctx, "Failed to parse Datadog response", "body", string(respBody), "error", err) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } queries, err := extractQueryValue(jsonResponse) if err != nil { - slog.Warn("Could not extract query values", "error", err) + slog.WarnContext(ctx, "Could not extract query values", "error", err) } data := map[string]any{ @@ -193,7 +193,7 @@ func (p *Provider) Measure( "queries": queries, } - slog.Debug("Datadog metric measurement", + slog.DebugContext(ctx, "Datadog metric measurement", "site", resolved.Site, "status", resp.StatusCode, "duration", duration) diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go index c5f6f5579..756288845 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/http/http.go @@ -109,7 +109,7 @@ func (p *Provider) Measure( duration := time.Since(startTime) if err != nil { - slog.Error("HTTP metric request failed", "url", resolved.URL, "error", err) + slog.ErrorContext(ctx, "HTTP metric request failed", "url", resolved.URL, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() @@ -128,7 +128,7 @@ func (p *Provider) Measure( "duration": duration.Milliseconds(), } - slog.Debug("HTTP metric measurement", + slog.DebugContext(ctx, "HTTP metric measurement", "url", resolved.URL, "status", resp.StatusCode, "duration", duration) diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go index 88b22b7b3..326976fe0 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/prometheus/prometheus.go @@ -122,7 +122,7 @@ func (p *PrometheusProvider) Measure( resp, err := client.Do(req) duration := time.Since(startTime) if err != nil { - slog.Error( + slog.ErrorContext(ctx, "Prometheus metric request failed", "address", resolvedProvider.Address, @@ -143,7 +143,7 @@ func (p *PrometheusProvider) Measure( return time.Time{}, nil, err } - slog.Debug("Prometheus metric measurement", + slog.DebugContext(ctx, "Prometheus metric measurement", "address", resolvedProvider.Address, "query", resolvedProvider.Query, "status", resp.StatusCode, diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go index 66eda8211..2119be44f 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/reconcile.go @@ -51,7 +51,7 @@ func Reconcile( span.AddEvent("interval not yet elapsed, deferring", trace.WithAttributes(attribute.String("remaining", remaining.String())), ) - slog.Info("Interval not yet elapsed, deferring", "remaining", remaining) + slog.InfoContext(ctx, "Interval not yet elapsed, deferring", "remaining", remaining) return &ReconcileResult{RequeueAfter: &remaining}, nil } diff --git a/apps/workspace-engine/svc/http/server/server.go b/apps/workspace-engine/svc/http/server/server.go index dc0f8f982..09bbe3f89 100644 --- a/apps/workspace-engine/svc/http/server/server.go +++ b/apps/workspace-engine/svc/http/server/server.go @@ -88,7 +88,7 @@ func LoggerMiddleware() gin.HandlerFunc { errMsg = c.Errors.String() } - slog.Error("request", + slog.ErrorContext(c.Request.Context(), "request", "method", method, "path", path, "status", statusCode, diff --git a/apps/workspace-engine/svc/service.go b/apps/workspace-engine/svc/service.go index 8478e21b4..0630b3735 100644 --- a/apps/workspace-engine/svc/service.go +++ b/apps/workspace-engine/svc/service.go @@ -63,7 +63,7 @@ func (r *Runner) Run(ctx context.Context) error { defer cancel() for _, svc := range r.services { - slog.Info("Starting service", "service", svc.Name()) + slog.InfoContext(ctx, "Starting service", "service", svc.Name()) if err := svc.Start(ctx); err != nil { cancel() return err @@ -75,9 +75,9 @@ func (r *Runner) Run(ctx context.Context) error { select { case sig := <-sigChan: - slog.Warn("Received signal, shutting down", "signal", sig) + slog.WarnContext(ctx, "Received signal, shutting down", "signal", sig) case <-ctx.Done(): - slog.Warn("Context cancelled, shutting down") + slog.WarnContext(ctx, "Context cancelled, shutting down") } cancel() @@ -89,9 +89,9 @@ func (r *Runner) Run(ctx context.Context) error { for _, v := range slices.Backward(r.services) { svc := v wg.Go(func() { - slog.Info("Stopping service", "service", svc.Name()) + slog.InfoContext(shutdownCtx, "Stopping service", "service", svc.Name()) if err := svc.Stop(shutdownCtx); err != nil { - slog.Error("Service stop error", "service", svc.Name(), "error", err) + slog.ErrorContext(shutdownCtx, "Service stop error", "service", svc.Name(), "error", err) } }) } @@ -104,9 +104,9 @@ func (r *Runner) Run(ctx context.Context) error { select { case <-done: - slog.Info("All services stopped") + slog.InfoContext(shutdownCtx, "All services stopped") case <-shutdownCtx.Done(): - slog.Warn("Shutdown timeout exceeded, forcing exit") + slog.WarnContext(shutdownCtx, "Shutdown timeout exceeded, forcing exit") } return nil From 210ecb3da4d9e79bbce3d7fff1d2a5342a9991c4 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 12:28:47 -0500 Subject: [PATCH 07/13] Enable log collection locally --- otel-collector-local.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/otel-collector-local.yaml b/otel-collector-local.yaml index bdde82967..f01cd5d64 100644 --- a/otel-collector-local.yaml +++ b/otel-collector-local.yaml @@ -26,4 +26,8 @@ service: metrics: receivers: [otlp] processors: [batch] - exporters: [logging, prometheus] \ No newline at end of file + exporters: [logging, prometheus] + logs: + receivers: [otlp] + processors: [batch] + exporters: [logging] \ No newline at end of file From a3959e55ccbea393618dfdf6e78bc8aca09c371b Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 12:29:02 -0500 Subject: [PATCH 08/13] uuid as string for logs --- .../pkg/store/policies/get_for_release_targets.go | 6 +++--- .../pkg/workspace/relationships/eval/evaluate.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go index 7910eff03..72d0b29e1 100644 --- a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go +++ b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go @@ -132,11 +132,11 @@ func (p *PostgresGetPoliciesForReleaseTarget) GetPoliciesForReleaseTarget( "policy_ids", len(policyIDs), "environment_id", - environmentID, + environmentID.String(), "deployment_id", - deploymentID, + deploymentID.String(), "resource_id", - resourceID, + resourceID.String(), ) setPoliciesSpanCtx, setPoliciesSpan := tracer.Start(ctx, "SetPoliciesForReleaseTarget") db.GetQueries(setPoliciesSpanCtx). diff --git a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go index f55039a81..52ccd5a3f 100644 --- a/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go +++ b/apps/workspace-engine/pkg/workspace/relationships/eval/evaluate.go @@ -70,7 +70,7 @@ func EvaluateRule( program, err := celEnv.Compile(rule.Cel) if err != nil { slog.WarnContext(ctx, "Skipping rule with invalid CEL expression", - "rule", rule.ID, "error", err) + "rule", rule.ID.String(), "error", err) return nil, nil } @@ -151,7 +151,7 @@ func EvaluateRules( matches, err := EvaluateRule(ctx, entity, &rule, allCandidates) if err != nil { slog.WarnContext(ctx, "Skipping rule due to evaluation error", - "rule", rule.ID, "error", err) + "rule", rule.ID.String(), "error", err) continue } allMatches = append(allMatches, matches...) From c78158d507efaa954e48d7790d5271013915f038 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 14:25:09 -0500 Subject: [PATCH 09/13] Lint fixes --- .../pkg/store/policies/get_for_release_targets.go | 6 +++++- apps/workspace-engine/pkg/store/resources/get_resources.go | 7 +++---- apps/workspace-engine/svc/claimcleanup/service.go | 5 ++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go index 72d0b29e1..4c10dd9f9 100644 --- a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go +++ b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go @@ -121,7 +121,11 @@ func (p *PostgresGetPoliciesForReleaseTarget) GetPoliciesForReleaseTarget( for _, policy := range policies { policyID, err := uuid.Parse(policy.Id) if err != nil { - slog.ErrorContext(ctx, "failed to parse policy id", "policy_id", policy.Id, "error", err) + slog.ErrorContext(ctx, + "failed to parse policy id", + "policy_id", policy.Id, + "error", err, + ) continue } policyIDs = append(policyIDs, policyID) diff --git a/apps/workspace-engine/pkg/store/resources/get_resources.go b/apps/workspace-engine/pkg/store/resources/get_resources.go index 54325db73..9c1cd7ac2 100644 --- a/apps/workspace-engine/pkg/store/resources/get_resources.go +++ b/apps/workspace-engine/pkg/store/resources/get_resources.go @@ -6,13 +6,12 @@ import ( "log/slog" "time" - "workspace-engine/pkg/celutil" - "workspace-engine/pkg/db" - "workspace-engine/pkg/oapi" - "github.com/google/cel-go/cel" "github.com/google/uuid" "go.opentelemetry.io/otel" + "workspace-engine/pkg/celutil" + "workspace-engine/pkg/db" + "workspace-engine/pkg/oapi" ) var tracer = otel.Tracer("workspace-engine/pkg/store/resources") diff --git a/apps/workspace-engine/svc/claimcleanup/service.go b/apps/workspace-engine/svc/claimcleanup/service.go index db7b626aa..7122522d5 100644 --- a/apps/workspace-engine/svc/claimcleanup/service.go +++ b/apps/workspace-engine/svc/claimcleanup/service.go @@ -63,7 +63,10 @@ func (s *Service) run(ctx context.Context) { if ctx.Err() != nil { return } - slog.ErrorContext(ctx, "claim-cleanup: failed to release expired claims", "error", err) + slog.ErrorContext(ctx, + "claim-cleanup: failed to release expired claims", + "error", err, + ) continue } if released > 0 { From 227814f5b5307eb6b909f16fe2d31ad617fc0e04 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 14:27:57 -0500 Subject: [PATCH 10/13] Lint fix --- apps/workspace-engine/otel.go | 5 ++++- .../pkg/jobagents/argo/argocd_plan.go | 6 +++++- .../pkg/jobagents/terraformcloud/tfe.go | 7 ++++++- .../deploymentresourceselectoreval/controller.go | 9 ++++++++- .../svc/controllers/forcedeploy/reconcile.go | 7 ++++++- .../metrics/provider/datadog/datadog.go | 12 ++++++++++-- apps/workspace-engine/svc/service.go | 9 ++++++++- 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/workspace-engine/otel.go b/apps/workspace-engine/otel.go index d841b0dab..514763403 100644 --- a/apps/workspace-engine/otel.go +++ b/apps/workspace-engine/otel.go @@ -189,7 +189,10 @@ func initLogger() (func(), error) { sdklog.WithResource(res), ) - otelHandler := newLevelHandler(level, otelslog.NewHandler(serviceName, otelslog.WithLoggerProvider(lp))) + otelHandler := newLevelHandler( + level, + otelslog.NewHandler(serviceName, otelslog.WithLoggerProvider(lp)), + ) stderrHandler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}) diff --git a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go index 2389d9054..71a158055 100644 --- a/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go +++ b/apps/workspace-engine/pkg/jobagents/argo/argocd_plan.go @@ -88,7 +88,11 @@ func prepareTmpApp(app *v1alpha1.Application, tmpName string) *v1alpha1.Applicat func (p *ArgoCDPlanner) deleteTmpApp(ctx context.Context, serverAddr, apiKey, name string) { if err := p.deleter.DeleteApplication(ctx, serverAddr, apiKey, name); err != nil { - slog.WarnContext(ctx, "Failed to delete temporary plan application", "app", name, "error", err) + slog.WarnContext(ctx, + "Failed to delete temporary plan application", + "app", name, + "error", err, + ) } } diff --git a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go index 14a828fd6..6076dd647 100644 --- a/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go +++ b/apps/workspace-engine/pkg/jobagents/terraformcloud/tfe.go @@ -83,7 +83,12 @@ func (t *TFE) Dispatch(ctx context.Context, job *oapi.Job) error { cfg.webhookUrl, webhookSecret, ); err != nil { - slog.WarnContext(ctx, "Failed to ensure notification config, continuing dispatch", "error", err) + slog.WarnContext( + ctx, + "Failed to ensure notification config, continuing dispatch", + "error", + err, + ) } if !cfg.triggerRunOnChange { diff --git a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go index 01650780a..8632aa750 100644 --- a/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go +++ b/apps/workspace-engine/svc/controllers/deploymentresourceselectoreval/controller.go @@ -69,7 +69,14 @@ func (c *Controller) Process(ctx context.Context, item reconcile.Item) (reconcil for _, resource := range resources { resourceIDUUID, err := uuid.Parse(resource.Id) if err != nil { - slog.ErrorContext(ctx, "failed to parse resource id", "resource_id", resource.Id, "error", err) + slog.ErrorContext( + ctx, + "failed to parse resource id", + "resource_id", + resource.Id, + "error", + err, + ) continue } matchedIDs = append(matchedIDs, resourceIDUUID) diff --git a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go index 2027775ad..0e7a65ad9 100644 --- a/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go +++ b/apps/workspace-engine/svc/controllers/forcedeploy/reconcile.go @@ -41,7 +41,12 @@ func Reconcile( return nil, recordErr(span, "get desired release", err) } if release == nil { - slog.InfoContext(ctx, "no desired release for release target, skipping", "rt", rt.ToOAPI().Key()) + slog.InfoContext( + ctx, + "no desired release for release target, skipping", + "rt", + rt.ToOAPI().Key(), + ) return &ReconcileResult{}, nil } diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go index 0275c32f1..c6aada491 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go @@ -169,13 +169,21 @@ func (p *Provider) Measure( var rawJson any if err := json.Unmarshal(respBody, &rawJson); err != nil { - slog.ErrorContext(ctx, "Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.ErrorContext(ctx, + "Failed to parse Datadog response", + "body", string(respBody), + "error", err, + ) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } var jsonResponse datadogResponseV2 if err := json.Unmarshal(respBody, &jsonResponse); err != nil { - slog.ErrorContext(ctx, "Failed to parse Datadog response", "body", string(respBody), "error", err) + slog.ErrorContext(ctx, + "Failed to parse Datadog response", + "body", string(respBody), + "error", err, + ) return time.Time{}, nil, fmt.Errorf("failed to parse response: %w", err) } diff --git a/apps/workspace-engine/svc/service.go b/apps/workspace-engine/svc/service.go index 0630b3735..c678eb322 100644 --- a/apps/workspace-engine/svc/service.go +++ b/apps/workspace-engine/svc/service.go @@ -91,7 +91,14 @@ func (r *Runner) Run(ctx context.Context) error { wg.Go(func() { slog.InfoContext(shutdownCtx, "Stopping service", "service", svc.Name()) if err := svc.Stop(shutdownCtx); err != nil { - slog.ErrorContext(shutdownCtx, "Service stop error", "service", svc.Name(), "error", err) + slog.ErrorContext( + shutdownCtx, + "Service stop error", + "service", + svc.Name(), + "error", + err, + ) } }) } From 87b7380468eb809491d4d397ce65eb8adee55621 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 14:37:23 -0500 Subject: [PATCH 11/13] Log level fixes --- apps/workspace-engine/log_handler.go | 7 +++++-- apps/workspace-engine/otel.go | 2 +- apps/workspace-engine/pkg/config/env.go | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/workspace-engine/log_handler.go b/apps/workspace-engine/log_handler.go index 17583bdd2..d73e2baf5 100644 --- a/apps/workspace-engine/log_handler.go +++ b/apps/workspace-engine/log_handler.go @@ -61,8 +61,11 @@ func newLevelHandler(level slog.Level, h slog.Handler) *levelHandler { return &levelHandler{handler: h, level: level} } -func (l *levelHandler) Enabled(_ context.Context, lvl slog.Level) bool { - return lvl >= l.level +func (l *levelHandler) Enabled(ctx context.Context, lvl slog.Level) bool { + if lvl < l.level { + return false + } + return l.handler.Enabled(ctx, lvl) } func (l *levelHandler) Handle(ctx context.Context, r slog.Record) error { diff --git a/apps/workspace-engine/otel.go b/apps/workspace-engine/otel.go index 514763403..4422f4fa8 100644 --- a/apps/workspace-engine/otel.go +++ b/apps/workspace-engine/otel.go @@ -163,7 +163,7 @@ func initLogger() (func(), error) { serviceName := config.Global.OTELServiceName endpoint := stripScheme(config.Global.OTELExporterOTLPEndpoint) - level := parseLogLevel(config.Global.OTELLogLevel) + level := parseLogLevel(config.Global.LogLevel) res, err := resource.New(ctx, resource.WithAttributes(semconv.ServiceNameKey.String(serviceName)), diff --git a/apps/workspace-engine/pkg/config/env.go b/apps/workspace-engine/pkg/config/env.go index b1fd6579f..d40480829 100644 --- a/apps/workspace-engine/pkg/config/env.go +++ b/apps/workspace-engine/pkg/config/env.go @@ -31,7 +31,7 @@ type Config struct { OTELServiceName string `default:"ctrlplane/workspace-engine" envconfig:"OTEL_SERVICE_NAME"` OTELExporterOTLPEndpoint string `default:"localhost:4318" envconfig:"OTEL_EXPORTER_OTLP_ENDPOINT"` - OTELLogLevel string `default:"INFO" envconfig:"OTEL_LOG_LEVEL"` + LogLevel string `default:"INFO" envconfig:"LOG_LEVEL"` GithubBotAppID string `default:"" envconfig:"GITHUB_BOT_APP_ID"` GithubBotPrivateKey string `default:"" envconfig:"GITHUB_BOT_PRIVATE_KEY"` From 66e0886610db0e1cc33b47265e082a7579e4a811 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 15:17:47 -0500 Subject: [PATCH 12/13] Log fixes --- .../pkg/store/policies/get_for_release_targets.go | 13 +++++-------- .../svc/controllers/desiredrelease/controller.go | 11 ++++++----- .../metrics/provider/datadog/datadog.go | 2 +- .../svc/controllers/policyeval/controller.go | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go index 4c10dd9f9..0841cc3c7 100644 --- a/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go +++ b/apps/workspace-engine/pkg/store/policies/get_for_release_targets.go @@ -133,14 +133,11 @@ func (p *PostgresGetPoliciesForReleaseTarget) GetPoliciesForReleaseTarget( slog.InfoContext(ctx, "setting policies for release target", - "policy_ids", - len(policyIDs), - "environment_id", - environmentID.String(), - "deployment_id", - deploymentID.String(), - "resource_id", - resourceID.String(), + "policy_count", len(policyIDs), + "policy_ids", fmt.Sprint(policyIDs), + "environment_id", environmentID.String(), + "deployment_id", deploymentID.String(), + "resource_id", resourceID.String(), ) setPoliciesSpanCtx, setPoliciesSpan := tracer.Start(ctx, "SetPoliciesForReleaseTarget") db.GetQueries(setPoliciesSpanCtx). diff --git a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go index eeaf8b86b..c03adb268 100644 --- a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go +++ b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go @@ -7,10 +7,6 @@ import ( "os" "time" - "github.com/jackc/pgx/v5/pgxpool" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" "workspace-engine/pkg/config" "workspace-engine/pkg/db" "workspace-engine/pkg/reconcile" @@ -19,6 +15,11 @@ import ( "workspace-engine/pkg/store/policies" "workspace-engine/pkg/store/releasetargets" "workspace-engine/svc" + + "github.com/jackc/pgx/v5/pgxpool" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" ) var tracer = otel.Tracer("workspace-engine/svc/controllers/desiredrelease") @@ -136,7 +137,7 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - slog.Error("Failed to create desired release reconcile worker", "error", err) + slog.ErrorContext(ctx, "Failed to create desired release reconcile worker", "error", err) os.Exit(1) } diff --git a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go index c6aada491..0f6871429 100644 --- a/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go +++ b/apps/workspace-engine/svc/controllers/jobverificationmetric/metrics/provider/datadog/datadog.go @@ -157,7 +157,7 @@ func (p *Provider) Measure( duration := time.Since(startTime) if err != nil { - slog.ErrorContext(ctx, "Datadog metric request failed", "site", resolved.Site, "error", err) + slog.ErrorContext(ctx, "Datadog metric request failed", "site", site, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() diff --git a/apps/workspace-engine/svc/controllers/policyeval/controller.go b/apps/workspace-engine/svc/controllers/policyeval/controller.go index b9e3f2973..a1b109e0d 100644 --- a/apps/workspace-engine/svc/controllers/policyeval/controller.go +++ b/apps/workspace-engine/svc/controllers/policyeval/controller.go @@ -122,7 +122,7 @@ func New(workerID string, pgxPool *pgxpool.Pool) svc.Service { nodeConfig, ) if err != nil { - slog.Error("Failed to create policy eval reconcile worker", "error", err) + slog.ErrorContext(ctx, "Failed to create policy eval reconcile worker", "error", err) os.Exit(1) } From 615a479fd99949dfb822e7f843ea24346091b275 Mon Sep 17 00:00:00 2001 From: James Singleton Date: Thu, 7 May 2026 15:30:37 -0500 Subject: [PATCH 13/13] Lint --- .../svc/controllers/desiredrelease/controller.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go index c03adb268..da82194df 100644 --- a/apps/workspace-engine/svc/controllers/desiredrelease/controller.go +++ b/apps/workspace-engine/svc/controllers/desiredrelease/controller.go @@ -7,6 +7,10 @@ import ( "os" "time" + "github.com/jackc/pgx/v5/pgxpool" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "workspace-engine/pkg/config" "workspace-engine/pkg/db" "workspace-engine/pkg/reconcile" @@ -15,11 +19,6 @@ import ( "workspace-engine/pkg/store/policies" "workspace-engine/pkg/store/releasetargets" "workspace-engine/svc" - - "github.com/jackc/pgx/v5/pgxpool" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" ) var tracer = otel.Tracer("workspace-engine/svc/controllers/desiredrelease")