47 lines
1.6 KiB
Go
47 lines
1.6 KiB
Go
// Package logging formats tool diagnostics consistently across commands.
|
|
package logging
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var outputMu sync.Mutex
|
|
|
|
// Info reports normal progress.
|
|
func Info(name, format string, args ...any) { output(os.Stdout, "I", name, format, args...) }
|
|
|
|
// Warn reports a condition that does not prevent the operation from continuing.
|
|
func Warn(name, format string, args ...any) { output(os.Stderr, "W", name, format, args...) }
|
|
|
|
// Error reports a recoverable operation failure.
|
|
func Error(name, format string, args ...any) { output(os.Stderr, "E", name, format, args...) }
|
|
|
|
// Fatal reports a command-ending failure. The caller controls the exit status.
|
|
func Fatal(name, format string, args ...any) { output(os.Stderr, "F", name, format, args...) }
|
|
|
|
func output(file *os.File, level, name, format string, args ...any) {
|
|
stat, err := file.Stat()
|
|
_, noColor := os.LookupEnv("NO_COLOR")
|
|
color := err == nil && stat.Mode()&os.ModeCharDevice != 0 && !noColor && os.Getenv("TERM") != "dumb"
|
|
outputMu.Lock()
|
|
defer outputMu.Unlock()
|
|
write(file, color, level, name, fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func write(w io.Writer, color bool, level, name, message string) {
|
|
label := "[" + level + "]"
|
|
if color {
|
|
code := map[string]string{"I": "32", "W": "33", "E": "31", "F": "35"}[level]
|
|
label = "\033[" + code + "m" + label + "\033[0m"
|
|
}
|
|
// Each physical line retains its level and component, including wrapped errors.
|
|
message = strings.ReplaceAll(message, "\r\n", "\n")
|
|
for _, line := range strings.Split(strings.TrimRight(message, "\n"), "\n") {
|
|
fmt.Fprintf(w, "%s [%s] %s\n", label, name, line)
|
|
}
|
|
}
|