/*********************************************** File Name: logging Author: Abby Cin Mail: abbytsing@gmail.com Created Time: 10/31/19 7:41 AM ***********************************************/ package logging import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "runtime" "strconv" "strings" "sync" "time" ) const ( DEBUG = 0 INFO = 1 WARN = 2 ERROR = 3 FATAL = 4 ) type Logger struct { FileName string `json:"filename" toml:"filename"` RollSize int64 `json:"roll_size" toml:"roll_size"` RollInterval time.Duration `json:"roll_interval" toml:"roll_interval"` Level int `json:"level" toml:"level"` PanicOnFatal bool `json:"panic_on_fatal" toml:"panic_on_fatal"` genName func() string format func(int, string, int) string dir string idx int roll bool level [5]string sizeCounter int64 timePoint time.Time file *os.File mtx sync.Mutex } var backend = Logger{} func init() { backend.roll = false backend.PanicOnFatal = true backend.level = [5]string{"DEBUG", "INFO ", "WARN ", "ERROR", "FATAL"} backend.format = func(level int, file string, line int) string { // fuck golang return fmt.Sprintf("[%s %s] %s:%d ", time.Now().Format("2006-01-02 15:04:05.000"), backend.level[level], file, line) } backend.file = os.Stdout } func Init(cfg *Logger) error { backend.roll = true backend.RollSize = cfg.RollSize backend.RollInterval = cfg.RollInterval backend.Level = cfg.Level backend.PanicOnFatal = cfg.PanicOnFatal backend.dir = filepath.Dir(cfg.FileName) backend.FileName = filepath.Base(strings.TrimSuffix(cfg.FileName, filepath.Ext(cfg.FileName))) backend.sizeCounter = 0 backend.timePoint = time.Now() backend.idx = 0 prefix := fmt.Sprintf("%s-%s", backend.FileName, time.Now().Format("20060102")) abs, err := filepath.Abs(backend.dir) if err != nil { return err; } if _, err := os.Stat(abs); os.IsNotExist(err) { os.Mkdir(abs, 0755) } entries, err := ioutil.ReadDir(abs) if err != nil { return err } // /path/to/prefix-20190617-pid-1.log for _, e := range entries { if strings.HasPrefix(e.Name(), prefix) { tmp, err := strconv.Atoi(strings.Split(strings.TrimSuffix(e.Name(), filepath.Ext(e.Name())), "-")[3]) if err != nil { return err } if backend.idx < tmp { backend.idx = tmp } } } backend.genName = func() string { cur := time.Now().Format("20060102") backend.idx += 1 return fmt.Sprintf("%s/%s-%s-%d-%d.log", backend.dir, backend.FileName, cur, os.Getpid(), backend.idx) } backend.file, err = os.Create(backend.genName()) return err } func (b *Logger) dispatch(level int, f string, args ...interface{}) { b.mtx.Lock() defer b.mtx.Unlock() if level < b.Level { return } if b.roll && (b.sizeCounter > b.RollSize || time.Since(b.timePoint) > b.RollInterval) { b.sizeCounter = 0 b.timePoint = time.Now() Release() b.file, _ = os.Create(b.genName()) } _, file, line, _ := runtime.Caller(2) // golang is horrible data := []byte(b.format(level, path.Base(file), line) + fmt.Sprintf(f, args...)) n, err := b.file.Write(append(data, '\n')) if err != nil { panic(err) } b.sizeCounter += int64(n) } func Release() { _ = backend.file.Sync() _ = backend.file.Close() } func Sync() error { return backend.file.Sync() } func Info(f string, args ...interface{}) { backend.dispatch(INFO, f, args...) } func Debug(f string, args ...interface{}) { backend.dispatch(DEBUG, f, args...) } func Warn(f string, args ...interface{}) { backend.dispatch(WARN, f, args...) } func Error(f string, args ...interface{}) { backend.dispatch(ERROR, f, args...) } func Fatal(f string, args ...interface{}) { backend.dispatch(FATAL, f, args...) if backend.PanicOnFatal { panic("fatal error") } }