初步完成&测试可用
This commit is contained in:
+43
-19
@@ -10,27 +10,29 @@ import (
|
|||||||
// 日志文件的计算
|
// 日志文件的计算
|
||||||
|
|
||||||
// 获取最新的文件名
|
// 获取最新的文件名
|
||||||
func nowFileName() string {
|
func (l *Logger) nowFileName(event string) string {
|
||||||
// ioc, _ := time.LoadLocation("Asia/Shanghai")
|
// 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().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
|
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))
|
// fmt.Println(filepath.Abs(path))
|
||||||
return 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()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
fileName := nowFileName()
|
fileName := l.nowFileName(event)
|
||||||
|
|
||||||
if !isMust {
|
|
||||||
if l.file != nil && l.fileName == fileName {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, _ := filepath.Split(fileName) // 识别目录与文件
|
dir, _ := filepath.Split(fileName) // 识别目录与文件
|
||||||
os.MkdirAll(dir, os.ModePerm) // 创建多层目录,如果存在不会报错
|
os.MkdirAll(dir, os.ModePerm) // 创建多层目录,如果存在不会报错
|
||||||
@@ -40,19 +42,41 @@ func (l *Logger) createNewFile(isMust bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// 打开失败,尝试创建
|
// 打开失败,尝试创建
|
||||||
fmt.Println("打开日志文件失败")
|
fmt.Println("打开日志文件失败")
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
// 关闭原来的文件
|
// 关闭原来的
|
||||||
if l.file != nil {
|
if f != nil {
|
||||||
l.closeFile()
|
closeFile(f)
|
||||||
}
|
}
|
||||||
l.file = file
|
|
||||||
l.fileName = fileName
|
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
|
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 {
|
func closeFile(f *os.File) error {
|
||||||
l.file.Sync()
|
f.Sync()
|
||||||
return l.file.Close()
|
return f.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,23 +10,33 @@ import (
|
|||||||
"time"
|
"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)
|
pc, file, line, _ := runtime.Caller(2)
|
||||||
// fmt.Println("runtime.Caller", pc, file, line, ok)
|
// 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 := runtime.FuncForPC(pc).Name()
|
||||||
funcName = filepath.Ext(funcName)
|
funcName = filepath.Ext(funcName)
|
||||||
funcName = strings.TrimPrefix(funcName, ".")
|
funcName = strings.TrimPrefix(funcName, ".")
|
||||||
|
|
||||||
by, _ := json.Marshal(v)
|
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))
|
// log.Println("" + string(by))
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-42
@@ -4,11 +4,6 @@ package loggerx
|
|||||||
// lastTime:2023年6月30日21:28:04
|
// lastTime:2023年6月30日21:28:04
|
||||||
// desc: 日志封装类
|
// desc: 日志封装类
|
||||||
|
|
||||||
// 优化方向
|
|
||||||
// 可以写盘的时候添加缓存,但是有个难点就是如果采用缓存的方式必须保证这个是最后关闭的,如果不是则退出的时候会丢失日志(写在缓存里还没刷盘)
|
|
||||||
|
|
||||||
// TODO:自动清除过期日志
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
@@ -26,10 +21,15 @@ import (
|
|||||||
|
|
||||||
// 需要实现io.Writer接口
|
// 需要实现io.Writer接口
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
|
filePath *sync.Map // filePath
|
||||||
|
mu *sync.Mutex
|
||||||
|
option loggerOption
|
||||||
|
channel string
|
||||||
|
}
|
||||||
|
|
||||||
|
type filePath struct {
|
||||||
file *os.File
|
file *os.File
|
||||||
fileName string
|
fileName string
|
||||||
mu sync.Mutex
|
|
||||||
// channel string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogger(opts ...Option) *Logger {
|
func NewLogger(opts ...Option) *Logger {
|
||||||
@@ -38,55 +38,41 @@ func NewLogger(opts ...Option) *Logger {
|
|||||||
apply(&opt)
|
apply(&opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
l := &Logger{}
|
l := &Logger{
|
||||||
|
filePath: &sync.Map{},
|
||||||
// 打开文件
|
mu: &sync.Mutex{},
|
||||||
err := l.createNewFile(true)
|
option: opt,
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.SetOutput(l)
|
log.SetOutput(l)
|
||||||
log.SetFlags(log.LstdFlags | log.Llongfile | log.Lmicroseconds) // log.Lshortfile | log.LUTC
|
log.SetFlags(log.LstdFlags | log.Llongfile | log.Lmicroseconds) // log.Lshortfile | log.LUTC
|
||||||
|
|
||||||
// 保存Gin日志写入到文件+控制台
|
// 保存Gin日志写入到文件+控制台
|
||||||
|
if opt.isGinLog {
|
||||||
gin.DefaultWriter = io.MultiWriter(l, os.Stdout)
|
gin.DefaultWriter = io.MultiWriter(l, os.Stdout)
|
||||||
gin.DefaultErrorWriter = io.MultiWriter(l, os.Stdout)
|
gin.DefaultErrorWriter = io.MultiWriter(l, os.Stdout)
|
||||||
|
|
||||||
// 赋值前缀
|
|
||||||
if prefix != "" {
|
|
||||||
log.SetPrefix(fmt.Sprintf("[%s]", prefix))
|
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (l *Logger) Channel(ch string) (r *Logger) {
|
// 强制刷盘
|
||||||
// rr := l
|
func (l *Logger) MustSync() {
|
||||||
// rr.channel = ch
|
l.filePath.Range(func(key, value any) bool {
|
||||||
// return rr
|
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) {
|
func (l *Logger) Write(b []byte) (n int, err error) {
|
||||||
|
return l.write("info", b)
|
||||||
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 (l *Logger) Info(ctx context.Context, v ...any) {
|
func (l *Logger) Info(ctx context.Context, v ...any) {
|
||||||
|
|||||||
+5
-1
@@ -12,8 +12,12 @@ import (
|
|||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
func TestLogger(t *testing.T) {
|
||||||
|
|
||||||
l := loggerx.InitLogger("profix")
|
l := loggerx.NewLogger(loggerx.SetErrorToInfo())
|
||||||
|
|
||||||
l.Error(context.Background(), "test error")
|
l.Error(context.Background(), "test error")
|
||||||
|
|
||||||
|
l.Channel("test").Error(context.Background(), "test error")
|
||||||
|
|
||||||
|
l.Info(context.Background(), "test info")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-5
@@ -1,20 +1,39 @@
|
|||||||
package loggerx
|
package loggerx
|
||||||
|
|
||||||
type loggerOption struct {
|
type loggerOption struct {
|
||||||
prefix string
|
prefix string // 日志前缀
|
||||||
format string
|
format string // text json
|
||||||
|
dir string // 文件目录
|
||||||
isGinLog bool
|
isGinLog bool
|
||||||
isGid bool
|
isGid bool
|
||||||
|
traceField string // trace字段
|
||||||
|
errorToInfo bool //
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultOptions() loggerOption {
|
func defaultOptions() loggerOption {
|
||||||
return loggerOption{
|
return loggerOption{
|
||||||
format: "json",
|
format: "json",
|
||||||
|
dir: "./log",
|
||||||
|
traceField: "trace_id",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(*loggerOption)
|
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 {
|
func SetPrefix(prefix string) Option {
|
||||||
return func(o *loggerOption) {
|
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) {
|
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()
|
// func SetFileSplit()
|
||||||
|
|
||||||
|
//
|
||||||
|
|||||||
+20
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user