diff --git a/tool/cmd/main.go b/tool/cmd/main.go index 1260d6463..861a86b9f 100644 --- a/tool/cmd/main.go +++ b/tool/cmd/main.go @@ -38,6 +38,7 @@ func main() { &cli.BoolFlag{ Name: "debug", Aliases: []string{"d"}, + Sources: cli.EnvVars(util.EnvOtelcDebug), Usage: "Enable debug mode", Value: false, }, @@ -111,21 +112,29 @@ func initLogger(ctx context.Context, cmd *cli.Command) (context.Context, error) } logFilename := filepath.Join(buildTempDir, debugLogFilename) - writer, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) + logFile, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) if err != nil { return ctx, ex.Wrapf(err, "failed to open log file %q", logFilename) } + level := slog.LevelInfo + if cmd.Bool("debug") { + level = slog.LevelDebug + if setErr := os.Setenv(util.EnvOtelcDebug, "1"); setErr != nil { + return ctx, ex.Wrapf(setErr, "set %s", util.EnvOtelcDebug) + } + } + // Log timestamps and levels are omitted: they add noise when correlating // with Go toolchain output and the log file is for human debugging only. - handler := slog.NewTextHandler(writer, &slog.HandlerOptions{ + handler := slog.NewTextHandler(logFile, &slog.HandlerOptions{ ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey || a.Key == slog.LevelKey { return slog.Attr{} } return a }, - Level: slog.LevelInfo, + Level: level, }) logger := slog.New(handler) ctx = util.ContextWithLogger(ctx, logger) diff --git a/tool/cmd/main_test.go b/tool/cmd/main_test.go new file mode 100644 index 000000000..8e9d02dba --- /dev/null +++ b/tool/cmd/main_test.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "log/slog" + "os" + "path/filepath" + "testing" + + "github.com/open-telemetry/opentelemetry-go-compile-instrumentation/tool/util" + "github.com/urfave/cli/v3" +) + +func TestInitLogger(t *testing.T) { + runWithFlags := func(t *testing.T, debug bool) context.Context { + t.Helper() + tmpDir, mkErr := os.MkdirTemp( //nolint:usetesting // open log file handle prevents cleanup + "", + "otelc-logger-test-*", + ) + if mkErr != nil { + t.Fatal(mkErr) + } + t.Cleanup(func() { os.RemoveAll(tmpDir) }) + + ctxCh := make(chan context.Context, 1) + app := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "work-dir", Value: tmpDir}, + &cli.BoolFlag{Name: "debug", Sources: cli.EnvVars(util.EnvOtelcDebug)}, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + ctx, err := initLogger(ctx, cmd) + if err != nil { + return err + } + ctxCh <- ctx + return nil + }, + } + + args := []string{"otelc"} + if debug { + args = append(args, "--debug") + } + if err := app.Run(context.Background(), args); err != nil { + t.Fatal(err) + } + + gotCtx := <-ctxCh + + logPath := filepath.Join(tmpDir, debugLogFilename) + if _, err := os.Stat(logPath); err != nil { + t.Fatalf("expected log file at %s: %v", logPath, err) + } + + return gotCtx + } + + t.Run("default level is info", func(t *testing.T) { + t.Setenv(util.EnvOtelcDebug, "") + ctx := runWithFlags(t, false) + logger := util.LoggerFromContext(ctx) + if logger.Enabled(context.Background(), slog.LevelDebug) { + t.Error("expected debug logging to be disabled") + } + }) + + t.Run("debug flag enables debug level", func(t *testing.T) { + t.Setenv(util.EnvOtelcDebug, "") + ctx := runWithFlags(t, true) + logger := util.LoggerFromContext(ctx) + if !logger.Enabled(context.Background(), slog.LevelDebug) { + t.Error("expected debug logging to be enabled") + } + }) + + t.Run("debug flag sets env for subprocess propagation", func(t *testing.T) { + t.Setenv(util.EnvOtelcDebug, "") + _ = runWithFlags(t, true) + if got := os.Getenv(util.EnvOtelcDebug); got != "1" { + t.Errorf("expected %s=1, got %q", util.EnvOtelcDebug, got) + } + }) + + t.Run("env var enables debug without flag", func(t *testing.T) { + t.Setenv(util.EnvOtelcDebug, "1") + ctx := runWithFlags(t, false) + logger := util.LoggerFromContext(ctx) + if !logger.Enabled(context.Background(), slog.LevelDebug) { + t.Error("expected debug logging to be enabled via env var") + } + }) +} diff --git a/tool/util/shared.go b/tool/util/shared.go index 8696fdc3d..7575e1907 100644 --- a/tool/util/shared.go +++ b/tool/util/shared.go @@ -19,6 +19,9 @@ const ( // EnvOtelcStats enables per-toolexec timing stats when set to "1". // Set automatically when --stats is used; propagated to child processes. EnvOtelcStats = "OTELC_STATS" + // EnvOtelcDebug enables debug-level logging when set to "1". + // Set automatically when --debug is used; propagated to child processes. + EnvOtelcDebug = "OTELC_DEBUG" BuildTempDir = ".otelc-build" OtelcRoot = "github.com/open-telemetry/opentelemetry-go-compile-instrumentation" )