215 lines
5.7 KiB
Go
215 lines
5.7 KiB
Go
|
|
package logger
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"go.uber.org/zap"
|
|||
|
|
"go.uber.org/zap/zapcore"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// LogLevel 日志级别
|
|||
|
|
type LogLevel string
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
DebugLevel LogLevel = "debug"
|
|||
|
|
InfoLevel LogLevel = "info"
|
|||
|
|
WarnLevel LogLevel = "warn"
|
|||
|
|
ErrorLevel LogLevel = "error"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// LogContext 日志上下文
|
|||
|
|
type LogContext struct {
|
|||
|
|
RequestID string
|
|||
|
|
UserID string
|
|||
|
|
TraceID string
|
|||
|
|
OperationName string
|
|||
|
|
Layer string // repository/service/handler
|
|||
|
|
Component string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ContextualLogger 上下文感知的日志器
|
|||
|
|
type ContextualLogger struct {
|
|||
|
|
logger *zap.Logger
|
|||
|
|
ctx LogContext
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewContextualLogger 创建上下文日志器
|
|||
|
|
func NewContextualLogger(logger *zap.Logger) *ContextualLogger {
|
|||
|
|
return &ContextualLogger{
|
|||
|
|
logger: logger,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WithContext 添加上下文信息
|
|||
|
|
func (l *ContextualLogger) WithContext(ctx context.Context) *ContextualLogger {
|
|||
|
|
logCtx := LogContext{}
|
|||
|
|
|
|||
|
|
// 从context中提取常用字段
|
|||
|
|
if requestID := getStringFromContext(ctx, "request_id"); requestID != "" {
|
|||
|
|
logCtx.RequestID = requestID
|
|||
|
|
}
|
|||
|
|
if userID := getStringFromContext(ctx, "user_id"); userID != "" {
|
|||
|
|
logCtx.UserID = userID
|
|||
|
|
}
|
|||
|
|
if traceID := getStringFromContext(ctx, "trace_id"); traceID != "" {
|
|||
|
|
logCtx.TraceID = traceID
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &ContextualLogger{
|
|||
|
|
logger: l.logger,
|
|||
|
|
ctx: logCtx,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WithLayer 设置层级信息
|
|||
|
|
func (l *ContextualLogger) WithLayer(layer string) *ContextualLogger {
|
|||
|
|
newCtx := l.ctx
|
|||
|
|
newCtx.Layer = layer
|
|||
|
|
return &ContextualLogger{
|
|||
|
|
logger: l.logger,
|
|||
|
|
ctx: newCtx,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WithComponent 设置组件信息
|
|||
|
|
func (l *ContextualLogger) WithComponent(component string) *ContextualLogger {
|
|||
|
|
newCtx := l.ctx
|
|||
|
|
newCtx.Component = component
|
|||
|
|
return &ContextualLogger{
|
|||
|
|
logger: l.logger,
|
|||
|
|
ctx: newCtx,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WithOperation 设置操作名称
|
|||
|
|
func (l *ContextualLogger) WithOperation(operation string) *ContextualLogger {
|
|||
|
|
newCtx := l.ctx
|
|||
|
|
newCtx.OperationName = operation
|
|||
|
|
return &ContextualLogger{
|
|||
|
|
logger: l.logger,
|
|||
|
|
ctx: newCtx,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建基础字段
|
|||
|
|
func (l *ContextualLogger) buildBaseFields() []zapcore.Field {
|
|||
|
|
fields := []zapcore.Field{}
|
|||
|
|
|
|||
|
|
if l.ctx.RequestID != "" {
|
|||
|
|
fields = append(fields, zap.String("request_id", l.ctx.RequestID))
|
|||
|
|
}
|
|||
|
|
if l.ctx.UserID != "" {
|
|||
|
|
fields = append(fields, zap.String("user_id", l.ctx.UserID))
|
|||
|
|
}
|
|||
|
|
if l.ctx.TraceID != "" {
|
|||
|
|
fields = append(fields, zap.String("trace_id", l.ctx.TraceID))
|
|||
|
|
}
|
|||
|
|
if l.ctx.Layer != "" {
|
|||
|
|
fields = append(fields, zap.String("layer", l.ctx.Layer))
|
|||
|
|
}
|
|||
|
|
if l.ctx.Component != "" {
|
|||
|
|
fields = append(fields, zap.String("component", l.ctx.Component))
|
|||
|
|
}
|
|||
|
|
if l.ctx.OperationName != "" {
|
|||
|
|
fields = append(fields, zap.String("operation", l.ctx.OperationName))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return fields
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LogTechnicalError 记录技术性错误(Repository层)
|
|||
|
|
func (l *ContextualLogger) LogTechnicalError(msg string, err error, fields ...zapcore.Field) {
|
|||
|
|
allFields := l.buildBaseFields()
|
|||
|
|
allFields = append(allFields, zap.Error(err))
|
|||
|
|
allFields = append(allFields, zap.String("error_type", "technical"))
|
|||
|
|
allFields = append(allFields, fields...)
|
|||
|
|
|
|||
|
|
l.logger.Error(msg, allFields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LogBusinessWarn 记录业务警告(Service层)
|
|||
|
|
func (l *ContextualLogger) LogBusinessWarn(msg string, fields ...zapcore.Field) {
|
|||
|
|
allFields := l.buildBaseFields()
|
|||
|
|
allFields = append(allFields, zap.String("log_type", "business"))
|
|||
|
|
allFields = append(allFields, fields...)
|
|||
|
|
|
|||
|
|
l.logger.Warn(msg, allFields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LogBusinessInfo 记录业务信息(Service层)
|
|||
|
|
func (l *ContextualLogger) LogBusinessInfo(msg string, fields ...zapcore.Field) {
|
|||
|
|
allFields := l.buildBaseFields()
|
|||
|
|
allFields = append(allFields, zap.String("log_type", "business"))
|
|||
|
|
allFields = append(allFields, fields...)
|
|||
|
|
|
|||
|
|
l.logger.Info(msg, allFields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LogUserAction 记录用户行为(Handler层)
|
|||
|
|
func (l *ContextualLogger) LogUserAction(msg string, fields ...zapcore.Field) {
|
|||
|
|
allFields := l.buildBaseFields()
|
|||
|
|
allFields = append(allFields, zap.String("log_type", "user_action"))
|
|||
|
|
allFields = append(allFields, fields...)
|
|||
|
|
|
|||
|
|
l.logger.Info(msg, allFields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LogRequestFailed 记录请求失败(Handler层)
|
|||
|
|
func (l *ContextualLogger) LogRequestFailed(msg string, errorType string, fields ...zapcore.Field) {
|
|||
|
|
allFields := l.buildBaseFields()
|
|||
|
|
allFields = append(allFields, zap.String("log_type", "request_failed"))
|
|||
|
|
allFields = append(allFields, zap.String("error_category", errorType))
|
|||
|
|
allFields = append(allFields, fields...)
|
|||
|
|
|
|||
|
|
l.logger.Info(msg, allFields...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// getStringFromContext 从上下文获取字符串值
|
|||
|
|
func getStringFromContext(ctx context.Context, key string) string {
|
|||
|
|
if value := ctx.Value(key); value != nil {
|
|||
|
|
if str, ok := value.(string); ok {
|
|||
|
|
return str
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ErrorCategory 错误分类
|
|||
|
|
type ErrorCategory string
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
DatabaseError ErrorCategory = "database"
|
|||
|
|
NetworkError ErrorCategory = "network"
|
|||
|
|
ValidationError ErrorCategory = "validation"
|
|||
|
|
BusinessError ErrorCategory = "business"
|
|||
|
|
AuthError ErrorCategory = "auth"
|
|||
|
|
ExternalAPIError ErrorCategory = "external_api"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// CategorizeError 错误分类
|
|||
|
|
func CategorizeError(err error) ErrorCategory {
|
|||
|
|
errMsg := strings.ToLower(err.Error())
|
|||
|
|
|
|||
|
|
switch {
|
|||
|
|
case strings.Contains(errMsg, "database") ||
|
|||
|
|
strings.Contains(errMsg, "sql") ||
|
|||
|
|
strings.Contains(errMsg, "gorm"):
|
|||
|
|
return DatabaseError
|
|||
|
|
case strings.Contains(errMsg, "network") ||
|
|||
|
|
strings.Contains(errMsg, "connection") ||
|
|||
|
|
strings.Contains(errMsg, "timeout"):
|
|||
|
|
return NetworkError
|
|||
|
|
case strings.Contains(errMsg, "validation") ||
|
|||
|
|
strings.Contains(errMsg, "invalid") ||
|
|||
|
|
strings.Contains(errMsg, "format"):
|
|||
|
|
return ValidationError
|
|||
|
|
case strings.Contains(errMsg, "unauthorized") ||
|
|||
|
|
strings.Contains(errMsg, "forbidden") ||
|
|||
|
|
strings.Contains(errMsg, "token"):
|
|||
|
|
return AuthError
|
|||
|
|
default:
|
|||
|
|
return BusinessError
|
|||
|
|
}
|
|||
|
|
}
|