//go:build windows // +build windows package tlog import ( "bufio" "bytes" "fmt" "os" "runtime" "strings" "sync" "time" ) var l *Logger var mu sync.Mutex type Logger struct { fileSize int64 fileNum int fileName string host string debug bool tag string level LEVEL dir string ch chan *Atom f *os.File w *bufio.Writer bytePool *sync.Pool } type Atom struct { line int file string format string level LEVEL args []interface{} data map[string]interface{} } func newLogger(config Config) *Logger { logger := &Logger{ dir: config.Dir, fileSize: int64(config.FileSize * 1024 * 1024), fileNum: config.FileNum, fileName: filepath.Join(config.Dir, config.FileName+".log"), tag: config.Tag, debug: config.Debug, level: getLevel(config.Level), ch: make(chan *Atom, 102400), bytePool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}, } host, _ := os.Hostname() ss := strings.Split(host, "-") if len(ss) < 2 { logger.host = host } else { logger.host = ss[len(ss)-2] + ss[len(ss)-1] } if logger.debug { return logger } os.MkdirAll(logger.dir, 0755) logger.f, _ = os.OpenFile(logger.fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) logger.w = bufio.NewWriterSize(logger.f, 1024*1024) return logger } func (l *Logger) run() { if l.debug { return } go l.flush() go l.start() } func (l *Logger) start() { for { a := <-l.ch if a == nil { l.flushNow() continue } b := l.bytes(a) l.w.Write(b) } } func (l *Logger) flushNow() { l.w.Flush() info, err := os.Stat(l.fileName) if err == nil && info.Size() > l.fileSize { l.rotate() } } func (l *Logger) rotate() { l.f.Close() newName := l.logname() os.Rename(l.fileName, newName) l.f, _ = os.OpenFile(l.fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) l.w.Reset(l.f) // 简化版本,不做保留 N 个旧日志处理 } func (l *Logger) flush() { for range time.NewTicker(time.Second).C { l.ch <- nil } } func (l *Logger) stop() { if l != nil && l.w != nil { l.w.Flush() } } func (l *Logger) getTag() string { if l != nil { return l.tag } return "" } func (l *Logger) bytes(a *Atom) []byte { w := l.bytePool.Get().(*bytes.Buffer) defer func() { w.Reset() l.bytePool.Put(w) }() w.Write(l.genTime()) fmt.Fprintf(w, "%s %s %s %s:%d ", l.host, l.tag, levelText[a.level], a.file, a.line) if a.format == "" { for _, arg := range a.args { w.WriteByte(' ') fmt.Fprint(w, arg) } } else { fmt.Fprintf(w, a.format, a.args...) } w.WriteByte('\n') b := make([]byte, w.Len()) copy(b, w.Bytes()) return b } func (l *Logger) logname() string { now := time.Now().Format("20060102_150405") return fmt.Sprintf("%s.%s", l.fileName, now) } func (l *Logger) getFileNameAndLine() (string, int) { _, file, line, ok := runtime.Caller(3) if !ok { return "???", 1 } dirs := strings.Split(file, string(os.PathSeparator)) if len(dirs) >= 2 { return dirs[len(dirs)-2] + "/" + dirs[len(dirs)-1], line } return file, line } func (l *Logger) p(level LEVEL, args ...interface{}) { file, line := l.getFileNameAndLine() if l == nil || l.debug { mu.Lock() defer mu.Unlock() fmt.Printf("%s%s %s %s:%d ", l.genTime(), l.getTag(), levelText[level], file, line) fmt.Println(args...) return } if level >= l.level { select { case l.ch <- &Atom{file: file, line: line, level: level, args: args}: default: } } } func (l *Logger) pf(level LEVEL, format string, args ...interface{}) { file, line := l.getFileNameAndLine() if l == nil || l.debug { mu.Lock() defer mu.Unlock() fmt.Printf("%s%s %s %s:%d ", l.genTime(), l.getTag(), levelText[level], file, line) fmt.Printf(format, args...) fmt.Println() return } if level >= l.level { select { case l.ch <- &Atom{file: file, line: line, format: format, level: level, args: args}: default: } } } func (l *Logger) genTime() []byte { now := time.Now() return []byte(now.Format("01-02 15:04:05 ")) } func (l *Logger) pj(level LEVEL, m map[string]interface{}) { return }