Files
doc_ai_backed/pkg/log/logger.go
2025-12-10 23:17:24 +08:00

153 lines
3.2 KiB
Go

package log
import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"time"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
)
type LogType string
const (
TypeAccess LogType = "access"
TypeBusiness LogType = "business"
TypeError LogType = "error"
)
var (
logger zerolog.Logger
)
// Setup 初始化日志配置
func Setup(conf LogConfig) error {
// 确保日志目录存在
if err := os.MkdirAll(filepath.Dir(conf.OutputPath), 0755); err != nil {
return fmt.Errorf("create log directory failed: %v", err)
}
// 配置日志轮转
writer := &lumberjack.Logger{
Filename: conf.OutputPath,
MaxSize: conf.MaxSize, // MB
MaxAge: conf.MaxAge, // days
MaxBackups: conf.MaxBackups,
Compress: conf.Compress,
}
// 设置日志级别
level, err := zerolog.ParseLevel(conf.Level)
if err != nil {
level = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(level)
// 初始化logger 并添加 app_name
logger = zerolog.New(writer).With().
Timestamp().
Str("app_name", conf.AppName). // 添加 app_name
Logger()
return nil
}
// log 统一的日志记录函数
func log(ctx context.Context, level zerolog.Level, logType LogType, kv ...interface{}) {
if len(kv)%2 != 0 {
kv = append(kv, "MISSING")
}
event := logger.WithLevel(level)
// 添加日志类型
event.Str("type", string(logType))
// 添加请求ID
if reqID, exists := ctx.Value("request_id").(string); exists {
event.Str("request_id", reqID)
}
// 添加调用位置
if pc, file, line, ok := runtime.Caller(2); ok {
event.Str("caller", fmt.Sprintf("%s:%d %s", filepath.Base(file), line, runtime.FuncForPC(pc).Name()))
}
// 处理key-value对
for i := 0; i < len(kv); i += 2 {
key, ok := kv[i].(string)
if !ok {
continue
}
value := kv[i+1]
switch v := value.(type) {
case error:
event.AnErr(key, v)
case int:
event.Int(key, v)
case int64:
event.Int64(key, v)
case float64:
event.Float64(key, v)
case bool:
event.Bool(key, v)
case time.Duration:
event.Dur(key, v)
case time.Time:
event.Time(key, v)
case []byte:
event.Bytes(key, v)
case string:
event.Str(key, v)
default:
event.Interface(key, v)
}
}
event.Send()
}
// Debug 记录调试日志
func Debug(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.DebugLevel, TypeBusiness, kv...)
}
// Info 记录信息日志
func Info(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.InfoLevel, TypeBusiness, kv...)
}
// Warn 记录警告日志
func Warn(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.WarnLevel, TypeError, kv...)
}
// Error 记录错误日志
func Error(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.ErrorLevel, TypeError, kv...)
}
func Fatal(ctx context.Context, kv ...interface{}) {
// 获取错误堆栈
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
// 添加堆栈信息到kv
newKv := make([]interface{}, 0, len(kv)+2)
newKv = append(newKv, kv...)
newKv = append(newKv, "stack", string(buf[:n]))
log(ctx, zerolog.FatalLevel, TypeError, newKv...)
}
// Access 记录访问日志
func Access(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.InfoLevel, TypeAccess, kv...)
}