diff --git a/filePath.go b/filePath.go index 2296fe9..a1ead24 100644 --- a/filePath.go +++ b/filePath.go @@ -10,27 +10,29 @@ import ( // 日志文件的计算 // 获取最新的文件名 -func nowFileName() string { +func (l *Logger) nowFileName(event string) 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" + if l.channel != "" { + timeDir = l.channel + "/" + timeDir + } + path := l.option.dir + "/" + timeDir + "_"+event + ".log" // fmt.Println(filepath.Abs(path)) return path } // 新建文件 -func (l *Logger) createNewFile(isMust bool) error { +func (l *Logger) getFile(event string,isRefresh bool) (*os.File, error) { + f := l.loadFile(event) + if f != nil && !isRefresh { + return f, nil + } + l.mu.Lock() defer l.mu.Unlock() - fileName := nowFileName() - - if !isMust { - if l.file != nil && l.fileName == fileName { - return nil - } - } + fileName := l.nowFileName(event) dir, _ := filepath.Split(fileName) // 识别目录与文件 os.MkdirAll(dir, os.ModePerm) // 创建多层目录,如果存在不会报错 @@ -40,19 +42,41 @@ func (l *Logger) createNewFile(isMust bool) error { if err != nil { // 打开失败,尝试创建 fmt.Println("打开日志文件失败") - return err + return nil, err } - // 关闭原来的文件 - if l.file != nil { - l.closeFile() + // 关闭原来的 + if f != nil { + closeFile(f) } - l.file = file - l.fileName = fileName - return nil + + l.filePath.Store(l.channel, &filePath{ + file: file, + fileName: fileName, + }) + + return file, nil +} + +// 加载文件 +func (l *Logger) loadFile(event string) *os.File { + val, ok := l.filePath.Load(l.channel) + if !ok { + return nil + } + f := val.(*filePath) + if f == nil { + return nil + } + if f.fileName != l.nowFileName(event) { + // 原来的文件需关闭 + closeFile(f.file) + return nil + } + return f.file } // 关闭文件 -func (l *Logger) closeFile() error { - l.file.Sync() - return l.file.Close() +func closeFile(f *os.File) error { + f.Sync() + return f.Close() } diff --git a/format.go b/format.go index 742410c..ae75bdc 100644 --- a/format.go +++ b/format.go @@ -10,23 +10,33 @@ import ( "time" ) -func (l *Logger) logger(ctx context.Context, action string, v ...any) { +func (l *Logger) logger(ctx context.Context, event string, v ...any) { pc, file, line, _ := runtime.Caller(2) // fmt.Println("runtime.Caller", pc, file, line, ok) + basePath, _ := filepath.Abs("./") + basePath = strings.ReplaceAll(basePath, "\\", "/") + // fmt.Println("basePath", basePath) + + file = strings.TrimPrefix(file, basePath) + 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") + nowTime := time.Now().Local().Format("2006-01-02 15:04:05.000000") - traceId, _ := ctx.Value("trace_id").(string) + traceId, _ := ctx.Value(l.option.traceField).(string) - writeStr := "[" + action + "]" + nowTime + " " + file + ":" + fmt.Sprintf("%d", line) + " " + funcName + " gid:" + getGID() + " " + traceId + " @data@: " + string(by) + "\n\n" + writeStr := "[" + event + "]" + nowTime + " " + file + ":" + fmt.Sprintf("%d", line) + " " + funcName + " gid:" + getGID() + " " + traceId + " @data@: " + string(by) + "\n\n" - l.Write([]byte(writeStr)) + l.write(event, []byte(writeStr)) + + if l.option.errorToInfo && event == "error" { + l.write("info", []byte(writeStr)) + } // log.Println("" + string(by)) } diff --git a/loggerx.go b/loggerx.go index 37f9533..b5a0a5b 100644 --- a/loggerx.go +++ b/loggerx.go @@ -4,11 +4,6 @@ package loggerx // lastTime:2023年6月30日21:28:04 // desc: 日志封装类 -// 优化方向 -// 可以写盘的时候添加缓存,但是有个难点就是如果采用缓存的方式必须保证这个是最后关闭的,如果不是则退出的时候会丢失日志(写在缓存里还没刷盘) - -// TODO:自动清除过期日志 - import ( "bytes" "context" @@ -26,10 +21,15 @@ import ( // 需要实现io.Writer接口 type Logger struct { + filePath *sync.Map // filePath + mu *sync.Mutex + option loggerOption + channel string +} + +type filePath struct { file *os.File fileName string - mu sync.Mutex - // channel string } func NewLogger(opts ...Option) *Logger { @@ -38,55 +38,41 @@ func NewLogger(opts ...Option) *Logger { apply(&opt) } - l := &Logger{} - - // 打开文件 - err := l.createNewFile(true) - if err != nil { - panic(err) + l := &Logger{ + filePath: &sync.Map{}, + mu: &sync.Mutex{}, + option: opt, } + 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)) + if opt.isGinLog { + gin.DefaultWriter = io.MultiWriter(l, os.Stdout) + gin.DefaultErrorWriter = io.MultiWriter(l, os.Stdout) } return l } -// func (l *Logger) Channel(ch string) (r *Logger) { -// rr := l -// rr.channel = ch -// return rr -// } +// 强制刷盘 +func (l *Logger) MustSync() { + l.filePath.Range(func(key, value any) bool { + f := value.(*filePath) + f.file.Sync() + return true + }) +} -// 超时删除 +func (l *Logger) Channel(ch string) (r *Logger) { + rr := *l + rr.channel = ch + return &rr +} +// 实现io.Writer接口 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 + return l.write("info", b) } func (l *Logger) Info(ctx context.Context, v ...any) { diff --git a/loggerx_test.go b/loggerx_test.go index c55a9ba..85f22b8 100644 --- a/loggerx_test.go +++ b/loggerx_test.go @@ -12,8 +12,12 @@ import ( func TestLogger(t *testing.T) { - l := loggerx.InitLogger("profix") + l := loggerx.NewLogger(loggerx.SetErrorToInfo()) l.Error(context.Background(), "test error") + l.Channel("test").Error(context.Background(), "test error") + + l.Info(context.Background(), "test info") + } diff --git a/options.go b/options.go index f047f58..596146d 100644 --- a/options.go +++ b/options.go @@ -1,20 +1,39 @@ package loggerx type loggerOption struct { - prefix string - format string - isGinLog bool - isGid bool + prefix string // 日志前缀 + format string // text json + dir string // 文件目录 + isGinLog bool + isGid bool + traceField string // trace字段 + errorToInfo bool // } func defaultOptions() loggerOption { return loggerOption{ - format: "json", + format: "json", + dir: "./log", + traceField: "trace_id", } } type Option func(*loggerOption) +// trace字段 +func SetTraceField(traceField string) Option { + return func(o *loggerOption) { + o.traceField = traceField + } +} + +// 错误日志是否写入info日志 +func SetErrorToInfo() Option { + return func(o *loggerOption) { + o.errorToInfo = true + } +} + // 日志的前缀 func SetPrefix(prefix string) Option { return func(o *loggerOption) { @@ -36,10 +55,10 @@ func SetGinLog() Option { } } -// 文件规则 -func SetFilePath(path string) Option { +// 文件路径 +func SetDir(dir string) Option { return func(o *loggerOption) { - // o.isGinLog = true + o.dir = dir } } @@ -51,4 +70,11 @@ func SetGID() Option { } // 文件切割规则 +// 1.文件大小 +// 2.时间A(年/月/日/时) +// 3.时间B(年/月-日) +// 4.时间C(年-月-日-时) +// 5.时间D(年-月-日) // func SetFileSplit() + +// diff --git a/storage.go b/storage.go new file mode 100644 index 0000000..f1aff5f --- /dev/null +++ b/storage.go @@ -0,0 +1,20 @@ +package loggerx + +import "io" + +func (l *Logger) write(event string, b []byte) (n int, err error) { + f, err := l.getFile(event, false) + if err != nil { + return 0, err + } + + n, err = f.Write(b) + if err == nil && n < len(b) { + err = io.ErrShortWrite + } + if err != nil { + // 强制更新 + l.getFile(event, true) + } + return f.Write(b) +}