diff --git a/apps/workspace-engine/go.mod b/apps/workspace-engine/go.mod index 34b418a42..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 @@ -296,6 +299,7 @@ require ( 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.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..ff91c6536 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,16 @@ 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/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 new file mode 100644 index 000000000..d73e2baf5 --- /dev/null +++ b/apps/workspace-engine/log_handler.go @@ -0,0 +1,81 @@ +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} +} + +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(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 { + 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 new file mode 100644 index 000000000..364896b48 --- /dev/null +++ b/apps/workspace-engine/log_handler_test.go @@ -0,0 +1,124 @@ +package main + +import ( + "bytes" + "context" + "errors" + "log/slog" + "strings" + "testing" + "time" +) + +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) + } + } +} + +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..4422f4fa8 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,74 @@ 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.LogLevel) + + 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.WithExportInterval(5*time.Second), + )), + 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/config/env.go b/apps/workspace-engine/pkg/config/env.go index 28dbe792c..d40480829 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"` + LogLevel string `default:"INFO" envconfig:"LOG_LEVEL"` GithubBotAppID string `default:"" envconfig:"GITHUB_BOT_APP_ID"` GithubBotPrivateKey string `default:"" envconfig:"GITHUB_BOT_PRIVATE_KEY"` diff --git a/apps/workspace-engine/pkg/db/client.go b/apps/workspace-engine/pkg/db/client.go index 35cad560e..1ada719b8 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.ErrorContext(ctx, "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.ErrorContext(ctx, "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..7c02b510d 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.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 a522d551d..71a158055 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,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 { - log.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 e8ac3616e..643a6b926 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.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 6308c22fb..6076dd647 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,12 @@ 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.WarnContext( + ctx, + "Failed to ensure notification config, continuing dispatch", + "error", + err, + ) } if !cfg.triggerRunOnChange { @@ -112,6 +117,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.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 2b5f65374..9487f3bbb 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.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 d8e8ef5e2..b09d4943d 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.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 3938ba5c8..4bc3093d7 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.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 bf9743f42..c7f5e8a2f 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.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 ebb08e755..2a90d7d1a 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.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 f8bdeb26d..83b2ee70a 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.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 { - log.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 80520a12d..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 @@ -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,22 +121,23 @@ 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.ErrorContext(ctx, + "failed to parse policy id", + "policy_id", policy.Id, + "error", err, + ) continue } policyIDs = append(policyIDs, policyID) } - log.Info( + slog.InfoContext(ctx, "setting policies for release target", - "policy_ids", - len(policyIDs), - "environment_id", - environmentID, - "deployment_id", - deploymentID, - "resource_id", - resourceID, + "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/pkg/store/resources/get_resources.go b/apps/workspace-engine/pkg/store/resources/get_resources.go index 08b9924ac..9c1cd7ac2 100644 --- a/apps/workspace-engine/pkg/store/resources/get_resources.go +++ b/apps/workspace-engine/pkg/store/resources/get_resources.go @@ -3,9 +3,9 @@ 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" @@ -62,7 +62,7 @@ func (p *PostgresGetResources) GetResources( baseQuery += " AND " + filter.Clause args = append(args, filter.Args...) - log.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 7779be0b4..52ccd5a3f 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,8 +69,8 @@ func EvaluateRule( program, err := celEnv.Compile(rule.Cel) if err != nil { - log.Warn("Skipping rule with invalid CEL expression", - "rule", rule.ID, "error", err) + slog.WarnContext(ctx, "Skipping rule with invalid CEL expression", + "rule", rule.ID.String(), "error", err) return nil, nil } @@ -150,8 +150,8 @@ func EvaluateRules( for _, rule := range rules { matches, err := EvaluateRule(ctx, entity, &rule, allCandidates) if err != nil { - log.Warn("Skipping rule due to evaluation error", - "rule", rule.ID, "error", err) + slog.WarnContext(ctx, "Skipping rule due to evaluation error", + "rule", rule.ID.String(), "error", err) continue } allMatches = append(allMatches, matches...) 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 f7c595c0e..7122522d5 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,14 @@ func (s *Service) run(ctx context.Context) { if ctx.Err() != nil { return } - log.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 { - log.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/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..8632aa750 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,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 { - log.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) @@ -124,12 +132,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 +165,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..da82194df 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.ErrorContext(ctx, "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..78697f69f 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.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() - log.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) } - log.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/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..0e7a65ad9 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,12 @@ 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.InfoContext( + ctx, + "no desired release for release target, skipping", + "rt", + rt.ToOAPI().Key(), + ) return &ReconcileResult{}, nil } @@ -51,7 +56,7 @@ func Reconcile( } if len(activeJobs) > 0 { span.SetAttributes(attribute.Int("active_jobs", len(activeJobs))) - log.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/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..c6b6bdf4c 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.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 { - log.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 { - log.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 { - log.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 { - log.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 { - log.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 { - log.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 { - log.Error( + slog.ErrorContext(ctx, "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..48539f3b0 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.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 { - log.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 d7a9e548f..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 @@ -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.ErrorContext(ctx, "Datadog metric request failed", "site", site, "error", err) return time.Time{}, nil, err } defer resp.Body.Close() @@ -169,19 +169,27 @@ 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.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 { - log.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 { - log.Warn("Could not extract query values", "error", err) + slog.WarnContext(ctx, "Could not extract query values", "error", err) } data := map[string]any{ @@ -193,7 +201,7 @@ func (p *Provider) Measure( "queries": queries, } - log.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 bd540bb01..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 @@ -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.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(), } - log.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 bbb936730..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 @@ -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.ErrorContext(ctx, "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.DebugContext(ctx, "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..2119be44f 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.InfoContext(ctx, "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..a1b109e0d 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.ErrorContext(ctx, "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..09bbe3f89 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.ErrorContext(c.Request.Context(), "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..c678eb322 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.InfoContext(ctx, "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.WarnContext(ctx, "Received signal, shutting down", "signal", sig) case <-ctx.Done(): - log.Warn("Context cancelled, shutting down") + slog.WarnContext(ctx, "Context cancelled, shutting down") } cancel() @@ -90,9 +89,16 @@ 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.InfoContext(shutdownCtx, "Stopping service", "service", svc.Name()) if err := svc.Stop(shutdownCtx); err != nil { - log.Error("Service stop error", "service", svc.Name(), "error", err) + slog.ErrorContext( + shutdownCtx, + "Service stop error", + "service", + svc.Name(), + "error", + err, + ) } }) } @@ -105,9 +111,9 @@ func (r *Runner) Run(ctx context.Context) error { select { case <-done: - log.Info("All services stopped") + slog.InfoContext(shutdownCtx, "All services stopped") case <-shutdownCtx.Done(): - log.Warn("Shutdown timeout exceeded, forcing exit") + slog.WarnContext(shutdownCtx, "Shutdown timeout exceeded, forcing exit") } return nil 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