diff --git a/go.mod b/go.mod index 5022b6160..0ccef1c24 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible diff --git a/go.sum b/go.sum index 582d62e0e..bd012e8c6 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUao github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= @@ -16,6 +18,10 @@ github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvK github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= @@ -44,6 +50,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 0f5552554..891bd2ab3 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -44,6 +44,7 @@ const ( defaultFileExpire = time.Minute pathFilterKey = "/os/glog/glog" memoryLockPrefixForPrintingToFile = "glog.printToFile:" + mustWithColor = true ) const ( @@ -220,20 +221,20 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // printToWriter writes buffer to writer. func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { - buffer := input.Buffer() if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { - l.printToFile(ctx, input.Time, buffer) + l.printToFile(ctx, input.Time, input.Buffer()) } // Allow output to stdout? if l.config.StdoutPrint { - if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { + if err := input.Stdout(); err != nil { intlog.Error(ctx, err) } } } else { - if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { + if _, err := l.config.Writer.Write(input.Buffer().Bytes()); err != nil { + // panic(err) intlog.Error(ctx, err) } } diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index c0213b982..bb9529023 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -8,6 +8,7 @@ package glog import ( "context" + "github.com/fatih/color" "github.com/gogf/gf/internal/intlog" "io" @@ -245,3 +246,16 @@ func (l *Logger) Async(enabled ...bool) *Logger { } return logger } + +// Color is a chaining function, +// which set level prefix color logging output feature. +func (l *Logger) Color(color color.Attribute) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + logger.config.currentColor = color + return logger +} diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 54322a7f8..d16564662 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -7,6 +7,7 @@ package glog import ( + "github.com/fatih/color" "io" "strings" "time" @@ -20,26 +21,28 @@ import ( // Config is the configuration object for logger. type Config struct { - Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. - Writer io.Writer `json:"-"` // Customized io.Writer. - Flags int `json:"flags"` // Extra flags for logging output features. - Path string `json:"path"` // Logging directory path. - File string `json:"file"` // Format for logging file. - Level int `json:"level"` // Output level. - Prefix string `json:"prefix"` // Prefix string for every logging content. - StSkip int `json:"stSkip"` // Skip count for stack. - StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) - StFilter string `json:"stFilter"` // Stack string filter. - CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. - HeaderPrint bool `json:"header"` // Print header or not(true in default). - StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). - LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. - RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. - RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. - RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. - RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. - RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. - RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronous checks the backups and expiration at intervals. It's 1 hour in default. + Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. + Writer io.Writer `json:"-"` // Customized io.Writer. + Flags int `json:"flags"` // Extra flags for logging output features. + Path string `json:"path"` // Logging directory path. + File string `json:"file"` // Format for logging file. + Level int `json:"level"` // Output level. + Prefix string `json:"prefix"` // Prefix string for every logging content. + StSkip int `json:"stSkip"` // Skip count for stack. + StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) + StFilter string `json:"stFilter"` // Stack string filter. + CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. + HeaderPrint bool `json:"header"` // Print header or not(true in default). + StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). + LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. + RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. + RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. + RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. + RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. + RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. + RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + FileColorEnable bool `json:"fileColorEnable"` // Logging level prefix with color or not (false in default). + currentColor color.Attribute `json:"-"` } // DefaultConfig returns the default configuration for logger. diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index acca9ea35..152629e90 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -9,6 +9,9 @@ package glog import ( "bytes" "context" + "github.com/fatih/color" + "github.com/gogf/gf/internal/intlog" + "os" "time" ) @@ -45,9 +48,39 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { func (i *HandlerInput) Buffer() *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) - if i.LevelFormat != "" { - i.addStringToBuffer(buffer, i.LevelFormat) + levelString := i.LevelFormat + if i.logger.config.FileColorEnable { + fg := i.getLevelFormatColor() + levelString = color.New(fg).Sprintf(i.LevelFormat) } + i.addStringToBuffer(buffer, levelString) + msg := i.GetContent() + i.addStringToBuffer(buffer, msg.String()) + return buffer +} + +// Stdout print log to console +func (i *HandlerInput) Stdout() error { + if _, err := os.Stdout.Write([]byte(i.TimeFormat)); err != nil { + intlog.Error(i.Ctx, err) + return err + } + fg := i.getLevelFormatColor() + if _, err := color.New(fg).Print(" " + i.LevelFormat + " "); err != nil { + intlog.Error(i.Ctx, err) + return err + } + msg := i.GetContent() + if _, err := os.Stdout.Write(msg.Bytes()); err != nil { + intlog.Error(i.Ctx, err) + return err + } + return nil +} + +// GetContent returns the primary content. +func (i *HandlerInput) GetContent() *bytes.Buffer { + buffer := bytes.NewBuffer(nil) if i.CallerFunc != "" { i.addStringToBuffer(buffer, i.CallerFunc) } @@ -67,6 +100,15 @@ func (i *HandlerInput) Buffer() *bytes.Buffer { return buffer } +// getLevelFormatColor returns the prefix string color. +func (i *HandlerInput) getLevelFormatColor() color.Attribute { + fg := defaultLevelColor[i.Level] + if i.logger.config.currentColor != 0 { + fg = i.logger.config.currentColor + } + return fg +} + func (i *HandlerInput) String() string { return i.Buffer().String() } diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index 9e9ba02ca..f726bd7bb 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -7,6 +7,7 @@ package glog import ( + "github.com/fatih/color" "github.com/gogf/gf/errors/gerror" "strings" ) @@ -28,6 +29,17 @@ const ( LEVEL_FATA // 1024 ) +const ( + COLOR_BLACK = 30 + iota + COLOR_RED + COLOR_GREEN + COLOR_YELLOW + COLOR_BLUE + COLOR_MAGENTA + COLOR_CYAN + COLOR_WHITE +) + // defaultLevelPrefixes defines the default level and its mapping prefix string. var defaultLevelPrefixes = map[int]string{ LEVEL_DEBU: "DEBU", @@ -40,6 +52,18 @@ var defaultLevelPrefixes = map[int]string{ LEVEL_FATA: "FATA", } +// defaultLevelColor defines the default level and its mapping prefix string. +var defaultLevelColor = map[int]color.Attribute{ + LEVEL_DEBU: COLOR_YELLOW, + LEVEL_INFO: COLOR_GREEN, + LEVEL_NOTI: COLOR_CYAN, + LEVEL_WARN: COLOR_YELLOW, + LEVEL_ERRO: COLOR_RED, + LEVEL_CRIT: COLOR_RED, + LEVEL_PANI: COLOR_RED, + LEVEL_FATA: COLOR_RED, +} + // levelStringMap defines level string name to its level mapping. var levelStringMap = map[string]int{ "ALL": LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT,