-
Notifications
You must be signed in to change notification settings - Fork 12
Description
Problem
In goa.design/clue/log (v1.2.3), logging configuration supports only a single output and a single formatter via:
log.Context(ctx,
log.WithOutput(io.Writer),
log.WithFormat(...),
)This makes it impossible to achieve a very common production setup:
- ANSI-colored logs to stdout (terminal)
- Non-ANSI logs to a file (for grep / ingestion)
Using io.MultiWriter(os.Stdout, file) causes ANSI escape codes emitted by FormatTerminal to be written to the file as well.
There is currently no supported way to:
- attach multiple outputs, and
- configure different formats per output.
Expected behavior
A user should be able to express something equivalent to:
- stdout →
FormatTerminal(ANSI) - file →
FormatTextorFormatJSON(no ANSI)
without writing a custom io.Writer that strips ANSI codes.
Actual behavior
Only a single formatter is applied, so ANSI escapes always propagate to all outputs.
The only workaround in v1.2.3 is to implement a custom tee writer that:
- writes raw bytes to stdout
- strips ANSI before writing to file
This feels like something the logging library should handle.
Minimal repro
logFile, _ := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
mw := io.MultiWriter(os.Stdout, logFile)
ctx := log.Context(
context.Background(),
log.WithOutput(mw),
)
log.Info(ctx, "hello world")Result:
- stdout: colored (expected)
- app.log: contains ANSI escape sequences (unexpected)
Proposed API (one of many options)
Any of the following would solve this cleanly:
// Option A: multi-output with per-output format
log.WithOutputs(
log.Output{Writer: os.Stdout, Format: log.FormatTerminal},
log.Output{Writer: logFile, Format: log.FormatText},
)// Option B: helper for common case
log.WithTerminalAndFile(os.Stdout, logFile)// Option C: explicit NO_COLOR / disable color on non-terminals
log.WithNoColorFileOutput(logFile)Exact shape is flexible — the key need is separating output destinations from formatting.
Context
This pattern exists in other Go logging ecosystems:
- zap (
zapcore.NewTee) - slog (
HandlerGroup) - go-kit/log (multi logger composition)
The lack of this capability in clue is surprising given its otherwise clean design.