-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzaplog.go
More file actions
147 lines (121 loc) · 4.87 KB
/
Copy pathzaplog.go
File metadata and controls
147 lines (121 loc) · 4.87 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package zaplog
import (
"context"
"os"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// TraceIDKey نوع اختصاصی برای جلوگیری از تداخل کلیدها در Context
type contextKey string
const TraceIDKey contextKey = "trace_id"
// Config تنظیمات اولیه لاگر را نگه میدارد
type Config struct {
Level string // "debug", "info", "warn", "error"
Encoding string // "json" یا "console"
Output string // "stdout" یا "file"
FilePath string // مسیر و نام پایه فایل (مثلا: "logs/app")
LinkFile bool // اگر true باشد، یک symlink به آخرین فایل لاگ ایجاد میشود (app.log)
}
// Logger اینترفیسی که تمام سرویسها و هندلرهای شما به آن وابستهاند
type Logger interface {
Debug(ctx context.Context, msg string, fields ...zap.Field)
Info(ctx context.Context, msg string, fields ...zap.Field)
Warn(ctx context.Context, msg string, fields ...zap.Field)
Error(ctx context.Context, msg string, fields ...zap.Field)
Fatal(ctx context.Context, msg string, fields ...zap.Field)
Sync() error
}
type zapLogger struct {
logger *zap.Logger
}
var loggerForErrorGeneral Logger
// NewZapLogger یک لاگر جدید بر اساس تنظیمات میسازد
func NewZapLogger(cfg Config) (Logger, error) {
// 1. تنظیم سطح لاگ (Default: Info)
level, err := zapcore.ParseLevel(cfg.Level)
if err != nil {
level = zapcore.InfoLevel
}
// 2. تنظیمات ساختار خروجی
var encoder zapcore.Encoder
if cfg.Encoding == "console" {
encCfg := zap.NewDevelopmentEncoderConfig()
encCfg.EncodeTime = zapcore.RFC3339NanoTimeEncoder
encoder = zapcore.NewConsoleEncoder(encCfg)
} else {
encCfg := zap.NewProductionEncoderConfig()
encCfg.TimeKey = "timestamp"
encCfg.EncodeTime = zapcore.RFC3339NanoTimeEncoder
encoder = zapcore.NewJSONEncoder(encCfg)
}
// 3. تعیین خروجی (فایل هوشمند یا کنسول)
var writeSyncer zapcore.WriteSyncer
if cfg.Output == "file" {
opts := []rotatelogs.Option{
rotatelogs.WithMaxAge(30 * 24 * time.Hour),
rotatelogs.WithRotationTime(24 * time.Hour),
rotatelogs.WithRotationSize(50 * 1024 * 1024),
}
if cfg.LinkFile {
opts = append(opts, rotatelogs.WithLinkName(cfg.FilePath+".log"))
}
rotator, err := rotatelogs.New(cfg.FilePath+"-%Y-%m-%d.log", opts...)
if err != nil {
return nil, err
}
writeSyncer = zapcore.AddSync(rotator)
} else {
writeSyncer = zapcore.AddSync(os.Stdout)
}
// 4. ساخت هسته لاگر
core := zapcore.NewCore(encoder, writeSyncer, level)
// 5. ساخت نمونه نهایی Zap
// CallerSkip(1): چون ما متدهای Zap را داخل متدهای خودمان (مثلا Info) فراخوانی میکنیم،
// باید یک لایه به عقب برگردد تا نام فایل و خطِ هندلر اصلی را چاپ کند، نه نام این فایل را.
zLog := zap.New(core,
zap.AddCaller(),
zap.AddCallerSkip(1),
zap.AddStacktrace(zapcore.ErrorLevel), // استکتریس فقط برای ارورها
)
logger := &zapLogger{logger: zLog}
loggerForErrorGeneral = logger
return logger, nil
}
// extractContextFields در صورت وجود TraceID در کانتکست، آن را به عنوان یک Field استخراج میکند
func (z *zapLogger) extractContextFields(ctx context.Context, fields []zap.Field) []zap.Field {
if ctx == nil {
return fields
}
if traceID, ok := ctx.Value(TraceIDKey).(string); ok && traceID != "" {
// TraceID را به ابتدای فیلدها اضافه میکند تا در JSON اول چاپ شود
return append([]zap.Field{zap.String("trace_id", traceID)}, fields...)
}
return fields
}
// --- پیادهسازی متدهای اینترفیس ---
func (z *zapLogger) Debug(ctx context.Context, msg string, fields ...zap.Field) {
z.logger.Debug(msg, z.extractContextFields(ctx, fields)...)
}
func (z *zapLogger) Info(ctx context.Context, msg string, fields ...zap.Field) {
z.logger.Info(msg, z.extractContextFields(ctx, fields)...)
}
func (z *zapLogger) Warn(ctx context.Context, msg string, fields ...zap.Field) {
z.logger.Warn(msg, z.extractContextFields(ctx, fields)...)
}
func (z *zapLogger) Error(ctx context.Context, msg string, fields ...zap.Field) {
z.logger.Error(msg, z.extractContextFields(ctx, fields)...)
}
func (z *zapLogger) Fatal(ctx context.Context, msg string, fields ...zap.Field) {
z.logger.Fatal(msg, z.extractContextFields(ctx, fields)...)
}
func (z *zapLogger) Sync() error {
return z.logger.Sync()
}
func ErrorGeneral(ctx context.Context, err error, fields ...zap.Field) {
if loggerForErrorGeneral == nil {
NewZapLogger(Config{Level: "error", Encoding: "console", Output: "stdout"})
}
loggerForErrorGeneral.Error(ctx, err.Error(), fields...)
}