package l import ( "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "runtime" "strconv" "time" "linkfog.com/public/lib/bufpool" ) const ( DefaultStdoutLimit uint64 = 2 * 1024 * 1024 * 1024 ) var ( logger Logger logBuf = bufpool.New() ) type Logger struct { r *Rotator logLevel LogLevel verboseLevel int quiet bool stdoutLimit uint64 stdoutBytes uint64 } type LogLevel int // var prefixPath string // func init() { // _, prefixPath, _, _ = runtime.Caller(0) // for i := 0; i < 4; i++ { // prefixPath = filepath.Dir(prefixPath) // } // prefixPath = prefixPath + "/" // } const ( LevelDebug LogLevel = iota LevelInfo LevelWarn LevelError LevelFatal LevelNone ) type Config struct { Path string MaxSize int64 MaxRolls int LogLevel LogLevel VerboseLevel int Quiet bool StdoutLimit uint64 } // SetConfig init logger func SetConfig(c Config) (err error) { err = os.MkdirAll(filepath.Dir(c.Path), 0700) if err != nil { return } logger.logLevel = c.LogLevel logger.verboseLevel = c.VerboseLevel logger.quiet = c.Quiet if c.StdoutLimit != 0 { logger.stdoutLimit = c.StdoutLimit } else { logger.stdoutLimit = DefaultStdoutLimit } logger.r, err = New(c.Path, c.MaxSize, false, c.MaxRolls) return } // Close close logger handle when exit func Close() { if logger.r != nil { logger.r.Close() } } func logMsg(level string, msgs ...interface{}) { _, file, line, ok := runtime.Caller(2) if !ok { file = "none" line = 0 } buf := logBuf.Get() defer logBuf.Put(buf) buf.WriteString(time.Now().Format("2006-01-02 15:04:05")) buf.WriteString(" [") if logger.r != nil { buf.WriteString(level) } else { buf.WriteString(color.autoPaint(level)) } buf.WriteString("] ") var isCode bool if len(msgs) > 0 { if code, ok := msgs[len(msgs)-1].(ErrCode); ok && code != "" { buf.WriteString("[") buf.WriteString(string(code)) buf.WriteString("] ") isCode = true } } buf.WriteString(fmt.Sprintf("%s:%d", filepath.Base(file), line)) for i, msg := range msgs { if isCode && i == (len(msgs)-1) { continue } buf.WriteString(fmt.Sprintf(" %+v", msg)) } buf.WriteString("\n") if !isPrint(level, string(buf.Bytes())) { return } if logger.r != nil { logger.r.Write(buf.Bytes()) } // add some color when print console if !logger.quiet { n, _ := io.Copy(os.Stdout, buf) logger.stdoutBytes += uint64(n) if logger.stdoutLimit != 0 && logger.stdoutBytes >= logger.stdoutLimit { logger.quiet = true notice := fmt.Sprintf("NOTICE: log stdout bytes reach limit %d\n", logger.stdoutLimit) if logger.r != nil { logger.r.Write([]byte(notice)) } os.Stdout.WriteString(notice) } } } func Info(msgs ...interface{}) { if logger.logLevel > LevelInfo { return } logMsg("INFO", msgs...) } func Infof(msgs ...interface{}) { if logger.logLevel > LevelInfo { return } if len(msgs) < 2 { logMsg("INFO", msgs...) return } logMsg("INFO", fmt.Sprintf(msgs[0].(string), msgs[1:]...)) } func Debug(msgs ...interface{}) { if logger.logLevel > LevelDebug { return } logMsg("DEBUG", msgs...) } func Debugf(msgs ...interface{}) { if logger.logLevel > LevelDebug { return } if len(msgs) < 2 { logMsg("DEBUG", msgs...) return } logMsg("DEBUG", fmt.Sprintf(msgs[0].(string), msgs[1:]...)) } func Debugj(msgs ...interface{}) { if logger.logLevel > LevelDebug { return } jsonMsg := []interface{}{} for _, msg := range msgs { b, err := json.MarshalIndent(msg, "", " ") if err != nil { jsonMsg = append(jsonMsg, msg) } else { jsonMsg = append(jsonMsg, string(b)) } } logMsg("DEBUG", jsonMsg...) } func Debugs(msgs ...interface{}) { if logger.logLevel > LevelDebug { return } logMsgWithStack("DEBUG", msgs...) } func wrapError(msgs ...interface{}) error { fmtStr := getFmtMsg(msgs...) return errors.New(fmtStr) } func WrapWarn(msgs ...interface{}) error { fmtStr := "" for i, msg := range msgs { if i == 0 { fmtStr = fmt.Sprintf("%+v", msg) } else { fmtStr = fmt.Sprintf("%s %+v", fmtStr, msg) } } if logger.logLevel <= LevelWarn { logMsg("WARN", fmtStr) } return errors.New(fmtStr) } func Warn(msgs ...interface{}) { if logger.logLevel > LevelWarn { return } logMsg("WARN", msgs...) } func Warnf(msgs ...interface{}) { if logger.logLevel > LevelWarn { return } if len(msgs) < 2 { logMsg("WARN", msgs...) return } logMsg("WARN", fmt.Sprintf(msgs[0].(string), msgs[1:]...)) } func Fatal(msgs ...interface{}) { if logger.logLevel > LevelFatal { return } logMsg("FATAL", msgs...) os.Exit(1) } func Fatalf(msgs ...interface{}) { if logger.logLevel > LevelFatal { return } if len(msgs) < 2 { logMsg("FATAL", msgs...) os.Exit(1) } logMsg("FATAL", fmt.Sprintf(msgs[0].(string), msgs[1:]...)) os.Exit(1) } func logMsgWithStack(level string, msgs ...interface{}) { buf := logBuf.Get() defer logBuf.Put(buf) buf.WriteString(time.Now().Format("2006-01-02 15:04:05")) buf.WriteString(" [") if logger.r != nil { buf.WriteString(level) } else { buf.WriteString(color.autoPaint(level)) } buf.WriteString("] ") for i := 0; i < 10; i++ { _, file, line, ok := runtime.Caller(i) if !ok { break } if i != 0 { buf.WriteString("<-") } buf.WriteString(file) buf.WriteString(":") buf.WriteString(strconv.Itoa(line)) } for _, msg := range msgs { buf.WriteString(fmt.Sprintf(" %+v", msg)) } buf.WriteString("\n") if logger.r != nil { logger.r.Write(buf.Bytes()) } if !logger.quiet { n, _ := io.Copy(os.Stdout, buf) logger.stdoutBytes += uint64(n) if logger.stdoutLimit != 0 && logger.stdoutBytes >= logger.stdoutLimit { logger.quiet = true notice := fmt.Sprintf("NOTICE: log stdout bytes reach limit %d\n", logger.stdoutLimit) if logger.r != nil { logger.r.Write([]byte(notice)) } os.Stdout.WriteString(notice) } } } // func relativePath(path string) string { // return strings.TrimPrefix(filepath.ToSlash(path), prefixPath) // } func EnableDebug() { logger.logLevel = LevelDebug } func DisableDebug() { logger.logLevel = LevelInfo } func CheckEnableDebug() bool { if logger.logLevel == LevelDebug { return true } return false } func SetVerboseLevel(verboseLevel int) { logger.verboseLevel = verboseLevel } func GetVerboseLevel() int { return logger.verboseLevel } func EnableQuiet() { logger.quiet = true } func DisableQuiet() { logger.quiet = false } func IsQuiet() bool { return logger.quiet } func GetStdoutLimit() uint64 { return logger.stdoutLimit } func SetStdoutLimit(limit uint64) { logger.stdoutLimit = limit } func GetStdoutBytes() uint64 { return logger.stdoutBytes }