Skip to content

refactor: adopt log/slog for structured leveled logging to separate diagnostic output from command output #400

@Harishrs2006

Description

@Harishrs2006

Reason/Context

The entire microcks-cli codebase uses fmt.Printf/fmt.Println for all output — command results, error messages, HTTP dumps, and diagnostic messages all go to stdout indiscriminately. This creates three concrete production problems:

1. CI/CD pipelines cannot parse command output
Commands like import print "Microcks has discovered 'foo'" mixed with "Got error when invoking..." on the same stdout stream. There is no way to reliably extract machine-readable results without brittle text parsing.

2. Verbose mode leaks secrets (issue #265)
--verbose dumps full HTTP request/response bodies including Bearer tokens and Basic auth credentials to stdout. There is no way to enable HTTP debugging without exposing credentials — it is binary: silent or everything.

3. Diagnostic output contaminates command output
POSIX convention: command results go to stdout, diagnostic messages go to stderr. The CLI mixes both on stdout, making it impossible to pipe command output (microcks test ... | jq .) without also receiving error messages in the pipe.

Go 1.21 introduced log/slog — structured, leveled logging in the standard library. This project uses Go 1.25. The fix is available in stdlib with zero new dependencies.

Description

Adopt log/slog across the codebase to establish a proper separation between command output and diagnostic output.

New --log-level flag replacing the binary --verbose:

  • error — only fatal errors
  • info — normal command progress (current default behavior)
  • debug — HTTP request/response dumps, token exchange details (current --verbose)

Redaction at the handler level:
A custom slog.Handler wraps the default handler and redacts fields matching Authorization, token, secret, password — resolving issue #265 permanently regardless of log level.

Stdout vs Stderr separation:
All slog output routes to os.Stderr. All structured command results route to os.Stdout. This enables:

  • microcks test ... --output json 2>/dev/null | jq '.success'
  • microcks test ... --log-level debug 2>debug.log

Non-breaking: --verbose becomes a deprecated alias for --log-level=debug. Default behavior unchanged.

Implementation ideas

Initialize slog in cmd/cmd.go PersistentPreRun:

var logLevel slog.LevelVar
handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
    Level: &logLevel,
})
slog.SetDefault(slog.New(NewRedactingHandler(handler)))

Custom redacting handler (stdlib onlyzero new dependencies):

type RedactingHandler struct{ inner slog.Handler }

func (h *RedactingHandler) Handle(ctx context.Context, r slog.Record) error {
    r.Attrs(func(a slog.Attr) bool {
        if isSecret(a.Key) {
            a.Value = slog.StringValue("[REDACTED]")
        }
        return true
    })
    return h.inner.Handle(ctx, r)
}

Replacements throughout codebase:

fmt.Printf("Got error: %s", err) → slog.Error("operation failed", "error", err)
fmt.Printf("Callback: %s", r.URL) → slog.Debug("oauth callback", "url", r.URL)
log.Printf("Token: %s", token) → removed (was issue #345)
Files affected: cmd/*.go, pkg/connectors/microcks_client.go, pkg/connectors/keycloak_client.go
New dependencies: zerolog/slog is stdlib since Go 1.21
Issues this resolves: #265, #345, #373, #387

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions