From 387f22b9cabb9b7f49d7dccd773b18ce94a3ffd5 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Tue, 3 Aug 2021 02:21:51 +0200 Subject: [PATCH] added default logger coloring --- README.md | 5 +- color_windows.go | 21 +++++ go.mod | 2 +- go.sum | 0 simple_logger.go | 82 +++++++++++++++++++- style.go | 194 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 color_windows.go create mode 100644 go.sum create mode 100644 style.go diff --git a/README.md b/README.md index 6b86665..f9fbd38 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # log - [![Go Reference](https://pkg.go.dev/badge/github.com/DisgoOrg/log.svg)](https://pkg.go.dev/github.com/DisgoOrg/log) [![Go Report](https://goreportcard.com/badge/github.com/DisgoOrg/log)](https://goreportcard.com/report/github.com/DisgoOrg/log) [![Go Version](https://img.shields.io/github/go-mod/go-version/DisgoOrg/log)](https://golang.org/doc/devel/release.html) @@ -12,11 +11,11 @@ The `Logger` interface can be used instead to give the user choice over which lo This lib ships with a default implementation of the `Logger` interface -[SimpleLogger](https://github.com/DisgoOrg/log/blob/master/simple_logger.go) is a wrapped standard [Logger](https://pkg.go.dev/log) to fit the `Logger` interface +[SimpleLogger](https://github.com/DisgoOrg/log/blob/master/simple_logger.go) is a wrapped +standard [Logger](https://pkg.go.dev/log) to fit the `Logger` interface You can use your own implementation or a library like [logrus](https://github.com/sirupsen/logrus) - ### Installing ```sh diff --git a/color_windows.go b/color_windows.go new file mode 100644 index 0000000..ac62801 --- /dev/null +++ b/color_windows.go @@ -0,0 +1,21 @@ +package log + +import ( + "os" + "runtime" + "syscall" +) + +// based off https://github.com/TwinProduction/go-color/blob/master/color_windows.go +func init() { + if runtime.GOOS == "windows" { + // Try to make ANSI work + handle := syscall.Handle(os.Stdout.Fd()) + kernel32DLL := syscall.NewLazyDLL("kernel32.dll") + setConsoleModeProc := kernel32DLL.NewProc("SetConsoleMode") + // If it fails, fallback to no styles + if _, _, err := setConsoleModeProc.Call(uintptr(handle), 0x0001|0x0002|0x0004); err != nil && err.Error() != "The operation completed successfully." { + enableColors = false + } + } +} diff --git a/go.mod b/go.mod index f0d5c77..1498c01 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/DisgoOrg/log -go 1.16 \ No newline at end of file +go 1.16 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/simple_logger.go b/simple_logger.go index 1c6b52a..a061616 100644 --- a/simple_logger.go +++ b/simple_logger.go @@ -12,7 +12,7 @@ var std *SimpleLogger = nil // These flags define which text to prefix to each log entry generated by the Logger. // Bits are or'ed together to control what's printed. -// With the exception of the Lmsgprefix flag, there is no +// Except the Lmsgprefix flag, there is no // control over the order they appear (the order listed here) // or the format they present (as described in the comments). // The prefix is followed by a colon only when Llongfile or Lshortfile @@ -21,6 +21,7 @@ var std *SimpleLogger = nil // 2009/01/23 01:23:23 message // while flags Ldate | Ltime | Lmicroseconds | Llongfile produce, // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message +//goland:noinspection GoUnusedConst const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 Ltime // the time in the local time zone: 01:23:23 @@ -65,16 +66,32 @@ func (l Level) String() string { } } +var ( + enableColors = true + prefixStyle = ForegroundColorBrightBlack + levelStyle = StyleBold + textStyle = ForegroundColorWhite +) + +var styles = map[Level]Style{ + LevelDebug: ForegroundColorWhite, + LevelInfo: ForegroundColorCyan, + LevelWarn: ForegroundColorYellow, + LevelError: ForegroundColorBrightRed, + LevelFatal: ForegroundColorRed, + LevelPanic: ForegroundColorMagenta, +} + //Default returns the default SimpleLogger //goland:noinspection GoUnusedExportedFunction func Default() *SimpleLogger { if std == nil { - std = New(log.LstdFlags | log.Lmsgprefix) + std = New(log.LstdFlags) } return std } -// New returns a new SimpleLogger implementation +// New returns a newInt SimpleLogger implementation //goland:noinspection GoUnusedExportedFunction func New(flags int) *SimpleLogger { return &SimpleLogger{ @@ -87,6 +104,7 @@ func New(flags int) *SimpleLogger { type SimpleLogger struct { logger *log.Logger level Level + prefix Style } // SetLevel sets the lowest Level to log for @@ -103,7 +121,24 @@ func (l *SimpleLogger) log(level Level, args ...interface{}) { if level < l.level { return } - l.logger.SetPrefix(level.String() + " ") + + if l.prefix != prefixStyle { + l.prefix = prefixStyle + l.logger.SetPrefix(prefixStyle.String()) + } + + args = append(args, "", StyleReset) + copy(args[2:], args) + + levelStr := level.String() + " " + textStyleStr := "" + if enableColors { + levelStr = levelStyle.And(styles[level]).ApplyClear(levelStr) + textStyleStr = textStyle.String() + } + args[0] = levelStr + args[1] = textStyleStr + switch level { case LevelFatal: l.logger.Fatal(args...) @@ -113,6 +148,7 @@ func (l *SimpleLogger) log(level Level, args ...interface{}) { l.logger.Print(args...) } } + func (l *SimpleLogger) logf(level Level, format string, args ...interface{}) { l.log(level, fmt.Sprintf(format, args...)) } @@ -178,71 +214,109 @@ func (l *SimpleLogger) Panicf(format string, args ...interface{}) { } // SetLevel sets the Level of the default Logger +//goland:noinspection GoUnusedExportedFunction func SetLevel(level Level) { Default().SetLevel(level) } +// SetLevelColor sets the Style of the given Level +//goland:noinspection GoUnusedExportedFunction +func SetLevelColor(level Level, color Style) { + styles[level] = color +} + +// SetLevelStyle sets the default Style of all Level(s) +//goland:noinspection GoUnusedExportedFunction +func SetLevelStyle(style Style) { + levelStyle = style +} + +// EnableColors enables/disables usage of Style(s) in all Logger(s) +//goland:noinspection GoUnusedExportedFunction +func EnableColors(enable bool) { + enableColors = enable +} + +// SetTextColor sets the Style which is used for text of a log message +//goland:noinspection GoUnusedExportedFunction +func SetTextColor(color Style) { + textStyle = color +} + // SetFlags sets the log flags like: Ldate, Ltime, Lmicroseconds, Llongfile, Lshortfile, LUTC, Lmsgprefix,LstdFlags of the default Logger +//goland:noinspection GoUnusedExportedFunction func SetFlags(flags int) { Default().SetFlags(flags) } // Debug logs on the LevelDebug with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Debug(args ...interface{}) { Default().Debug(args...) } // Debugf logs on the LevelDebug with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Debugf(format string, args ...interface{}) { Default().Debugf(format, args...) } // Info logs on the LevelInfo with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Info(args ...interface{}) { Default().Info(args...) } // Infof logs on the LevelInfo with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Infof(format string, args ...interface{}) { Default().Infof(format, args...) } // Warn logs on the LevelWarn with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Warn(args ...interface{}) { Default().Warn(args...) } // Warnf logs on the Level with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Warnf(format string, args ...interface{}) { Default().Warnf(format, args...) } // Error logs on the LevelError with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Error(args ...interface{}) { Default().Error(args...) } // Errorf logs on the LevelError with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Errorf(format string, args ...interface{}) { Default().Errorf(format, args...) } // Fatal logs on the LevelFatal with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Fatal(args ...interface{}) { Default().Fatal(args...) } // Fatalf logs on the LevelFatal with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Fatalf(format string, args ...interface{}) { Default().Fatalf(format, args...) } // Panic logs on the LevelPanic with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Panic(args ...interface{}) { Default().Panic(args...) } // Panicf logs on the LevelPanic with the default SimpleLogger +//goland:noinspection GoUnusedExportedFunction func Panicf(format string, args ...interface{}) { Default().Panicf(format, args...) } diff --git a/style.go b/style.go new file mode 100644 index 0000000..8af3011 --- /dev/null +++ b/style.go @@ -0,0 +1,194 @@ +// Package log based on https://en.wikipedia.org/wiki/ANSI_escape_code +package log + +import ( + "fmt" + "strconv" +) + +// general Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + StyleReset = newInt(0) + StyleBold = newInt(1) + StyleFaint = newInt(2) + StyleItalic = newInt(3) + StyleUnderline = newInt(4) + StyleSlowBlink = newInt(5) + StyleRapidBlink = newInt(6) + StyleInvert = newInt(7) + StyleHide = newInt(8) + StyleStrike = newInt(9) + StyleDefaultFont = newInt(10) +) + +// AlternateFont returns a Style which alternates the font +//goland:noinspection GoUnusedExportedFunction +func AlternateFont(font int) Style { + if font < 1 || font > 9 { + panic("font can't be smaller than 1 and bigger than 9") + } + return newInt(font + 10) +} + +// more general Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + StyleBlackLetterFont = newInt(20) + StyleDoubleUnderlined = newInt(21) + StyleNormalIntensity = newInt(22) + StyleNeitherItalicNorBlackLetter = newInt(23) + StyleNotUnderlined = newInt(24) + StyleNotBlinking = newInt(25) + StyleProportionalSpacing = newInt(26) + StyleNotReversed = newInt(27) + StyleReveal = newInt(28) + StyleNotCrossedOut = newInt(29) +) + +// foreground color Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + ForegroundColorBlack = newInt(30) + ForegroundColorRed = newInt(31) + ForegroundColorGreen = newInt(32) + ForegroundColorYellow = newInt(33) + ForegroundColorBlue = newInt(34) + ForegroundColorMagenta = newInt(35) + ForegroundColorCyan = newInt(36) + ForegroundColorWhite = newInt(37) +) + +// SetForegroundColor returns a Style which sets the foreground color +//goland:noinspection GoUnusedExportedFunction +func SetForegroundColor(r, g, b int) Style { + return colorStyle(38, r, g, b) +} + +// background color Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + DefaultForegroundColor = newInt(39) + BackgroundColorBlack = newInt(40) + BackgroundColorRed = newInt(41) + BackgroundColorGreen = newInt(42) + BackgroundColorYellow = newInt(43) + BackgroundColorBlue = newInt(44) + BackgroundColorMagenta = newInt(45) + BackgroundColorCyan = newInt(46) + BackgroundColorWhite = newInt(47) +) + +// SetBackgroundColor returns a Style which sets the background color +//goland:noinspection GoUnusedExportedFunction +func SetBackgroundColor(r, g, b int) Style { + return colorStyle(48, r, g, b) +} + +// more general Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + DefaultBackgroundColor = newInt(49) + StyleDisableProportionalSpacing = newInt(50) + StyleFramed = newInt(51) + StyleEncircled = newInt(52) + StyleOverlined = newInt(53) + StyleNeitherFramedNorEncircled = newInt(54) + StyleNotOverlined = newInt(55) +) + +// SetUnderlineColor returns a Style which sets the underline color +//goland:noinspection GoUnusedExportedFunction +func SetUnderlineColor(r, g, b int) Style { + return colorStyle(58, r, g, b) +} + +// more general Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + StyleDefaultUnderlineColor = newInt(59) + StyleIdeogramUnderlineOrRightSideLine = newInt(60) + StyleIdeogramDoubleUnderlineOrDoubleLineOnRightSide = newInt(61) + StyleIdeogramOverlineOrLeftSideLine = newInt(62) + StyleIdeogramDoubleOverlineOrDoubleLineOnTheLeftSide = newInt(63) + StyleIdeogramStressMarking = newInt(64) + StyleNoIdeogramAttributes = newInt(65) +) + +// super/subscript Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + StyleSuperscript = newInt(73) + StyleSubscript = newInt(74) + StyleNeitherSuperscriptNorSubscript = newInt(75) +) + +// foreground bright color Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + ForegroundColorBrightBlack = newInt(90) + ForegroundColorBrightRed = newInt(91) + ForegroundColorBrightGreen = newInt(92) + ForegroundColorBrightYellow = newInt(93) + ForegroundColorBrightBlue = newInt(94) + ForegroundColorBrightMagenta = newInt(95) + ForegroundColorBrightCyan = newInt(96) + ForegroundColorBrightWhite = newInt(97) +) + +// background bright color Style(s) +//goland:noinspection GoUnusedGlobalVariable +var ( + BackgroundColorBrightBlack = newInt(100) + BackgroundColorBrightRed = newInt(101) + BackgroundColorBrightGreen = newInt(102) + BackgroundColorBrightYellow = newInt(103) + BackgroundColorBrightBlue = newInt(104) + BackgroundColorBrightMagenta = newInt(105) + BackgroundColorBrightCyan = newInt(106) + BackgroundColorBrightWhite = newInt(107) +) + +func newInt(c int) Style { + return Style("\033[" + strconv.Itoa(c) + "m") +} + +func newStr(str string) Style { + return Style("\033[" + str + "m") +} + +func colorStyle(c, r, g, b int) Style { + return newStr(fmt.Sprintf("%d;%d;%d;%d;%d m", c, 2, r, g, b)) +} + +// Style represents a text +type Style string + +// String returns the Style as string to be used in a Terminal +func (s Style) String() string { + return string(s) +} + +// And adds 2 Style(s) together +func (s Style) And(style Style) Style { + return s + style +} + +// Apply applies a given Style to the given text +func (s Style) Apply(text string) string { + return s.String() + text + StyleReset.String() +} + +// ApplyClear applies a given Style to the given text with clearing all Style(s) before +func (s Style) ApplyClear(text string) string { + return StyleReset.String() + s.Apply(text) +} + +// ApplyStyles wraps a given message in the given Style(s). +//goland:noinspection GoUnusedExportedFunction +func ApplyStyles(message string, colors ...Style) string { + for _, color := range colors { + message = color.Apply(message) + } + return message +}