0%

【Go】使用 logrus 日志库

相比起 zap 日志库,logrus 性能会低一些,但在很多场景,并不需要太高性能,而 logrus 库的自定义能力较为强大,可以任意定义日志格式,较为方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main

import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"io"
"os"
"path/filepath"
"runtime"
"strings"
)

const logPath = "./log.txt"

var log = New()

func New() *Logger {
return &Logger{logger: logrus.New()}
}

type Logger struct {
ctx context.Context
logger *logrus.Logger
}

func (l *Logger) SetFormatter(formatter logrus.Formatter) {
l.logger.SetFormatter(formatter)
}

func (l *Logger) SetOutput(output io.Writer) {
l.logger.SetOutput(output)
}

func (l *Logger) SetLevel(level logrus.Level) {
l.logger.SetLevel(level)
}

func (l *Logger) WithContext(ctx context.Context) *Logger {
return &Logger{ctx: ctx, logger: l.logger}
}

func (l *Logger) Debug(format string, args ...any) {
l.log(logrus.DebugLevel, format, args...)
}

func (l *Logger) Info(format string, args ...any) {
l.log(logrus.InfoLevel, format, args...)
}

func (l *Logger) Warn(format string, args ...any) {
l.log(logrus.WarnLevel, format, args...)
}

func (l *Logger) Error(format string, args ...any) {
l.log(logrus.ErrorLevel, format, args...)
}

func (l *Logger) Fatal(format string, args ...any) {
l.log(logrus.FatalLevel, format, args...)
}

func (l *Logger) log(level logrus.Level, format string, args ...any) {
format = fmt.Sprintf("[%s] %s", GetCallerShort(3), format)
l.logger.WithContext(l.ctx).Logf(level, format, args...)
}

type Formatter struct{}

func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
pid := os.Getpid()
time := entry.Time.Format("2006-01-02T15:04:05.000000")
level := strings.ToUpper(entry.Level.String())
traceId := GetTraceIdFromCtx(entry.Context)

content := fmt.Sprintf("%s %s %d %s %s\n", time, level, pid, traceId, entry.Message)
return []byte(content), nil
}

// GetCallerShort 获取调用点文件名 + 行号
func GetCallerShort(skip int) string {
_, file, line, ok := runtime.Caller(skip + 1)
if !ok {
return ""
}
_, file = filepath.Split(file)
return fmt.Sprintf("%s:%d", file, line)
}

// CtxSetTraceId 在 ctx 中设置 trace id
func CtxSetTraceId(ctx context.Context) context.Context {
return context.WithValue(ctx, "trace_id", "id1")
}

// GetTraceIdFromCtx 打印日志时,从 ctx 中获取 trace id 打印
func GetTraceIdFromCtx(ctx context.Context) string {
if ctx == nil {
return "-"
}

val := ctx.Value("trace_id")
if traceId, ok := val.(string); ok {
return fmt.Sprintf("trace-%s", traceId)
} else {
return "-"
}
}

func init() {
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640)
if err != nil {
panic(err)
}

log.SetFormatter(&Formatter{})
log.SetOutput(io.MultiWriter(os.Stdout, f))
log.SetLevel(logrus.DebugLevel)
}

func main() {
ctx := context.Background()
ctx = CtxSetTraceId(ctx)
log.WithContext(ctx).Info("main failed: %s", "detail")
}