package logsdk import ( "context" "fmt" "time" "git.blauwelle.com/go/crate/runtimehelper" ) // Entry 包含日志所需的全部中间信息并负责输出日志 type Entry struct { logger *Logger time time.Time fields []KV callerSkip int reportCaller bool isReportCallerSet bool reportStack bool isReportStackSet bool initialized bool } func (entry Entry) copy() Entry { if entry.initialized { return entry } return entry.logger.newEntry() } // AddCallerSkip 增加调用 [runtime.Callers] 时的 skip 参数, // 当通过装饰器等方式封装 Entry 导致增加调用 Entry 方法的深度时使用 AddCallerSkip 调整 skip, // 直接在需要日志的地方调用 Entry 的方法时不需要 AddCallerSkip. func (entry Entry) AddCallerSkip(n int) Entry { newEntry := entry.copy() newEntry.callerSkip += n return newEntry } // WithField 增加1组键值对 func (entry Entry) WithField(key string, value any) Entry { return entry.WithFields(KV{Key: key, Value: value}) } // WithFields 增加键值对 func (entry Entry) WithFields(fields ...KV) Entry { newEntry := entry.copy() newEntry.fields = make([]KV, len(entry.fields)+len(fields)) copy(newEntry.fields, entry.fields) copy(newEntry.fields, fields) return newEntry } // WithTime 设置日志的时间, // Entry 默认使用调用 Log 等最终方法的时间作为日志的时间. func (entry Entry) WithTime(t time.Time) Entry { newEntry := entry.copy() newEntry.time = t return newEntry } // GetReportCaller 获取是否收集调用记录, // 默认使用 Logger 上的对应配置. func (entry Entry) GetReportCaller() bool { if entry.isReportCallerSet { return entry.reportCaller } return entry.logger.GetReportCaller() } // WithReportCaller 设置是否收集调用记录 func (entry Entry) WithReportCaller(reportCaller bool) Entry { newEntry := entry.copy() newEntry.reportCaller = reportCaller newEntry.isReportCallerSet = true return newEntry } // GetReportStack 获取是否收集调用栈, // 默认使用 Logger 上的对应配置. func (entry Entry) GetReportStack() bool { if entry.isReportStackSet { return entry.reportStack } return entry.logger.GetReportStack() } // WithReportStack 设置是否收集调用栈 func (entry Entry) WithReportStack(reportStack bool) Entry { newEntry := entry.copy() newEntry.reportStack = reportStack newEntry.isReportStackSet = true return newEntry } // Log 输出日志 func (entry Entry) Log(ctx context.Context, level Level, args ...any) { entry.log(ctx, level, fmt.Sprint(args...)) } // Trace 输出 LevelTrace 等级的日志 func (entry Entry) Trace(ctx context.Context, args ...any) { entry.log(ctx, LevelTrace, fmt.Sprint(args...)) } // Debug 输出 LevelDebug 等级的日志 func (entry Entry) Debug(ctx context.Context, args ...any) { entry.log(ctx, LevelDebug, fmt.Sprint(args...)) } // Info 输出 LevelInfo 等级的日志 func (entry Entry) Info(ctx context.Context, args ...any) { entry.log(ctx, LevelInfo, fmt.Sprint(args...)) } // Warn 输出 LevelWarn 等级的日志 func (entry Entry) Warn(ctx context.Context, args ...any) { entry.log(ctx, LevelWarn, fmt.Sprint(args...)) } // Error 输出 LevelError 等级的日志 func (entry Entry) Error(ctx context.Context, args ...any) { entry.log(ctx, LevelError, fmt.Sprint(args...)) } // Fatal 输出 LevelFatal 等级的日志并调用 Logger.Exit func (entry Entry) Fatal(ctx context.Context, args ...any) { entry.log(ctx, LevelFatal, fmt.Sprint(args...)) entry.logger.Exit(1) } // Panic 输出 LevelPanic 等级的日志后执行 panic, // 即使 Logger 日志等级高于 LevelPanic 也会 panic. func (entry Entry) Panic(ctx context.Context, args ...any) { entry.log(ctx, LevelPanic, fmt.Sprint(args...)) } // Logf 格式化输出日志 func (entry Entry) Logf(ctx context.Context, level Level, format string, args ...any) { entry.log(ctx, level, fmt.Sprintf(format, args...)) } // Tracef 格式化输出 LevelTrace 等级的日志 func (entry Entry) Tracef(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelTrace, fmt.Sprintf(format, args...)) } // Debugf 格式化输出 LevelDebug 等级的日志 func (entry Entry) Debugf(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelDebug, fmt.Sprintf(format, args...)) } // Infof 格式化输出 LevelInfo 等级的日志 func (entry Entry) Infof(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelInfo, fmt.Sprintf(format, args...)) } // Warnf 格式化输出 LevelWarn 等级的日志 func (entry Entry) Warnf(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelWarn, fmt.Sprintf(format, args...)) } // Errorf 格式化输出 LevelError 等级的日志 func (entry Entry) Errorf(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelError, fmt.Sprintf(format, args...)) } // Fatalf 格式化输出 LevelFatal 等级的日志并调用 Logger.Exit func (entry Entry) Fatalf(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelFatal, fmt.Sprintf(format, args...)) entry.logger.Exit(1) } // Panicf 格式化输出 LevelPanic 等级的日志 // 即使 Logger 日志等级高于 LevelPanic 也会 panic. func (entry Entry) Panicf(ctx context.Context, format string, args ...any) { entry.log(ctx, LevelPanic, fmt.Sprintf(format, args...)) } func (entry Entry) log(ctx context.Context, level Level, message string) { newEntry := entry.copy() defer func() { if level == LevelPanic { panic(message) } }() if newEntry.logger.GetLevel() < level { return } readonlyEntry := ReadonlyEntry{ Context: ctx, Fields: newEntry.fields, Message: message, Time: newEntry.time, Level: level, } if readonlyEntry.Time.IsZero() { readonlyEntry.Time = time.Now() } if newEntry.GetReportCaller() { readonlyEntry.Caller = runtimehelper.Caller(newEntry.callerSkip) } if newEntry.GetReportStack() { readonlyEntry.Stack = runtimehelper.Stack(newEntry.callerSkip, 32) } for _, processor := range newEntry.logger.getLevelProcessors(level) { processor.Process(readonlyEntry) } } // ReadonlyEntry 是日志系统收集到1条日志记录 type ReadonlyEntry struct { Context context.Context `json:"-"` Caller runtimehelper.Frame Stack []runtimehelper.Frame Time time.Time Message string Fields []KV Level Level }