diff --git a/internal/log/color.go b/internal/log/color.go new file mode 100644 index 0000000..dffbac6 --- /dev/null +++ b/internal/log/color.go @@ -0,0 +1,33 @@ +package log + +import ( + "fmt" + "os" +) + +const ( + colorBlack = iota + 30 + colorRed + colorGreen + colorYellow + colorBlue + colorMagenta + colorCyan + colorWhite + + colorBold = 1 + colorDarkGray = 90 +) + +// colorize returns the string s wrapped in ANSI code c, unless disabled is true or c is 0. +func colorize(s interface{}, c int, disabled bool) string { + e := os.Getenv("NO_COLOR") + if e != "" || c == 0 { + disabled = true + } + + if disabled { + return fmt.Sprintf("%s", s) + } + return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s) +} diff --git a/internal/log/log.go b/internal/log/log.go index 52f6ac2..eb9af7a 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -3,6 +3,9 @@ package log import ( "fmt" "os" + "path/filepath" + "regexp" + "runtime" "strings" "time" @@ -28,24 +31,44 @@ func Init(ll, lf string) error { return fmt.Errorf("unknown log level: %s", ll) } + cw := zerolog.ConsoleWriter{Out: os.Stderr} switch lf { case "rfc3339": - Logger = zerolog.New( - zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, - ).Level(loggerLevel).With().Timestamp().Caller().Logger() + cw.TimeFormat = time.RFC3339 + cw.FormatCaller = getFormatCaller(cw.NoColor) + Logger = zerolog.New(cw).Level(loggerLevel).With().Timestamp().Caller().Logger() case "basic": - Logger = zerolog.New( - zerolog.ConsoleWriter{ - Out: os.Stderr, - FormatTimestamp: func(i interface{}) string { return "" }, - FormatLevel: func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%-6s|", i)) }, - }, - ).Level(loggerLevel).With().Caller().Logger() + cw.FormatTimestamp = func(i interface{}) string { return "" } + cw.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%-6s|", i)) } + cw.FormatCaller = getFormatCaller(cw.NoColor) + Logger = zerolog.New(cw).Level(loggerLevel).With().Caller().Logger() case "json": - Logger = zerolog.New(os.Stderr).Level(loggerLevel).With().Timestamp().Logger() + Logger = zerolog.New(cw).Level(loggerLevel).With().Timestamp().Logger() default: return fmt.Errorf("unknown log format: %s", lf) } return nil } + +// getFormatCaller is a wrapper that generates a Formatter for the +// ConsoleWriter.FormatCaller field. The Formatter generated uses the base name +// of the source file where the log message originated from and ensures that it +// is still colorized, if enabled. +func getFormatCaller(noColor bool) zerolog.Formatter { + return func(i interface{}) string { + re := regexp.MustCompile(`(?P.*):(?P\d+)`) + path := re.ReplaceAllString(i.(string), "${path}") + line := re.ReplaceAllString(i.(string), "${line}") + + var out string + _, f, l, ok := runtime.Caller(7) + if ok { + out = fmt.Sprintf("%s:%d", filepath.Base(f), l) + } else { + out = fmt.Sprintf("%s:%d", path, line) + } + + return colorize(out, colorBold, noColor) + colorize(" >", colorCyan, noColor) + } +}