// 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) } }