package loggerx // author:黄新云 // lastTime:2023年6月30日21:28:04 // desc: 日志封装类 // 优化方向 // 可以写盘的时候添加缓存,但是有个难点就是如果采用缓存的方式必须保证这个是最后关闭的,如果不是则退出的时候会丢失日志(写在缓存里还没刷盘) // TODO:自动清除过期日志 import ( "bytes" "context" "encoding/json" "fmt" "io" "log" "os" "path/filepath" "runtime" "strings" "sync" // "sync_log/global" "time" "github.com/gin-gonic/gin" ) // 需要实现io.Writer接口 type Logger struct { file *os.File fileName string mu sync.Mutex // channel string } func InitLogger(prefix string) *Logger { l := &Logger{} // 打开文件 err := l.createNewFile(true) if err != nil { panic(err) } log.SetOutput(l) log.SetFlags(log.LstdFlags | log.Llongfile | log.Lmicroseconds) // log.Lshortfile | log.LUTC // 保存Gin日志写入到文件+控制台 gin.DefaultWriter = io.MultiWriter(l, os.Stdout) gin.DefaultErrorWriter = io.MultiWriter(l, os.Stdout) // 赋值前缀 if prefix != "" { log.SetPrefix(fmt.Sprintf("[%s]", prefix)) } return l } // func (l *Logger) Channel(ch string) (r *Logger) { // rr := l // rr.channel = ch // return rr // } // 超时删除 func (l *Logger) Write(b []byte) (n int, err error) { if l.file == nil { // 新建一个file连接 l.createNewFile(false) } if l.fileName != nowFileName() { l.createNewFile(false) } n, err = l.file.Write(b) if err == nil && n < len(b) { err = io.ErrShortWrite } if err != nil { // 强制更新 l.createNewFile(true) } return n, err } // 获取最新的文件名 func nowFileName() string { // ioc, _ := time.LoadLocation("Asia/Shanghai") // timeDir := fmt.Sprint(time.Now().In(ioc).Format("2006/01/02/15")) // 2006-01-02 15:04:05 timeDir := fmt.Sprint(time.Now().Local().Format("2006/01/02")) // 2006-01-02 15:04:05 path := "./log/" + timeDir + ".log" // fmt.Println(filepath.Abs(path)) return path } // 新建文件 func (l *Logger) createNewFile(isMust bool) error { l.mu.Lock() defer l.mu.Unlock() fileName := nowFileName() if !isMust { if l.file != nil && l.fileName == fileName { return nil } } dir, _ := filepath.Split(fileName) // 识别目录与文件 os.MkdirAll(dir, os.ModePerm) // 创建多层目录,如果存在不会报错 // 打开该文件,如果不存在则创建 file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if err != nil { // 打开失败,尝试创建 fmt.Println("打开日志文件失败") return err } // 关闭原来的文件 if l.file != nil { l.closeFile() } l.file = file l.fileName = fileName return nil } // 关闭文件 func (l *Logger) closeFile() error { l.file.Sync() return l.file.Close() } func (l *Logger) Info(ctx context.Context, v ...any) { l.logger(ctx, "info", v...) } func (l *Logger) Infof(ctx context.Context, format string, v ...any) { s := fmt.Sprintf(format, v...) l.logger(ctx, "info", s) } func (l *Logger) Error(ctx context.Context, v ...any) { l.logger(ctx, "error", v...) } func (l *Logger) Errorf(ctx context.Context, format string, v ...any) { s := fmt.Sprintf(format, v...) l.logger(ctx, "error", s) } // 添加固定的内容 // func (l *Logger) ContextWithFields(ctx context.Context, v ...any) { // l.logger(ctx, "add", v...) // } // func (l *Logger) Field(key,val string) { // l.logger(nil, "add", key,val) // } func (l *Logger) logger(ctx context.Context, action string, v ...any) { pc, file, line, _ := runtime.Caller(2) // fmt.Println("runtime.Caller", pc, file, line, ok) funcName := runtime.FuncForPC(pc).Name() funcName = filepath.Ext(funcName) funcName = strings.TrimPrefix(funcName, ".") by, _ := json.Marshal(v) nowTime := time.Now().Local().Format("20060102 15:04:05.000000") traceId, _ := ctx.Value("trace_id").(string) writeStr := "[" + action + "]" + nowTime + " " + file + ":" + fmt.Sprintf("%d", line) + " " + funcName + " gid:" + getGID() + " " + traceId + " @data@: " + string(by) + "\n\n" l.Write([]byte(writeStr)) // log.Println("" + string(by)) } func getGID() string { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] b = bytes.TrimPrefix(b, []byte("goroutine ")) b = b[:bytes.IndexByte(b, ' ')] return string(b) }