// Package log provides optional colored console logging, independent of decryption. package log import ( "fmt" "os" "path" "runtime" "sync" "time" ) // Logger writes timestamped, colorized log lines to stderr. type Logger struct{} // New returns a logger instance. func New() *Logger { return &Logger{} } func (l *Logger) Debug(a ...any) { output(1, "cyan", a...) } func (l *Logger) Info(a ...any) { output(1, "green", a...) } func (l *Logger) Warn(a ...any) { output(1, "yellow", a...) } func (l *Logger) Error(a ...any) { output(1, "red", a...) } // With returns the same logger (scope is ignored; kept for call-site compatibility). func (l *Logger) With(_ any) *Logger { return l } // PrintSuccess prints a green/cyan success line for CLI use. func PrintSuccess(src, dst string) { mu.Lock() defer mu.Unlock() fmt.Fprintf(os.Stderr, "%s %s: ", time.Now().Format("15:04:05.000"), trace(3)) fmt.Fprintf(os.Stderr, "操作成功: %s -> %s\n", green(src), cyan(dst)) } var mu sync.Mutex func output(skip int, color string, v ...any) { mu.Lock() defer mu.Unlock() fmt.Fprint(os.Stderr, time.Now().Format("15:04:05.000"), " ", trace(skip+2), " ") fmt.Fprintln(os.Stderr, colorize(color, fmt.Sprint(v...))) } func trace(skip int) string { _, file, line, ok := runtime.Caller(skip) if !ok { return "unknown:0" } return fmt.Sprintf("%s:%d", path.Base(file), line) } func colorize(color, s string) string { if !supportsColor() { return s } const reset = "\033[0m" switch color { case "red": return "\033[31m" + s + reset case "green": return "\033[32m" + s + reset case "yellow": return "\033[33m" + s + reset case "cyan": return "\033[36m" + s + reset default: return s } } func green(s string) string { return colorize("green", s) } func cyan(s string) string { return colorize("cyan", s) } func supportsColor() bool { if os.Getenv("NO_COLOR") != "" { return false } // Windows 10+ and most Unix terminals support ANSI. return true }