package tracing import ( "context" "fmt" "time" "go.opentelemetry.io/otel/attribute" "go.uber.org/zap" "tyapi-server/internal/application/user/dto/commands" "tyapi-server/internal/domains/user/entities" "tyapi-server/internal/shared/interfaces" ) // ServiceWrapper 服务包装器,提供自动追踪能力 type ServiceWrapper struct { tracer *Tracer logger *zap.Logger } // NewServiceWrapper 创建服务包装器 func NewServiceWrapper(tracer *Tracer, logger *zap.Logger) *ServiceWrapper { return &ServiceWrapper{ tracer: tracer, logger: logger, } } // TraceServiceCall 追踪服务调用的通用方法 func (w *ServiceWrapper) TraceServiceCall( ctx context.Context, serviceName, methodName string, fn func(context.Context) error, ) error { // 创建span名称 spanName := fmt.Sprintf("%s.%s", serviceName, methodName) // 开始追踪 ctx, span := w.tracer.StartSpan(ctx, spanName) defer span.End() // 添加基础属性 w.tracer.AddSpanAttributes(span, attribute.String("service.name", serviceName), attribute.String("service.method", methodName), attribute.String("service.type", "business"), ) // 记录开始时间 startTime := time.Now() // 执行原始方法 err := fn(ctx) // 计算执行时间 duration := time.Since(startTime) w.tracer.AddSpanAttributes(span, attribute.Int64("service.duration_ms", duration.Milliseconds()), ) // 标记慢方法 if duration > 100*time.Millisecond { w.tracer.AddSpanAttributes(span, attribute.Bool("service.slow_method", true), ) w.logger.Warn("慢方法检测", zap.String("service", serviceName), zap.String("method", methodName), zap.Duration("duration", duration), zap.String("trace_id", w.tracer.GetTraceID(ctx)), ) } // 处理错误 if err != nil { w.tracer.SetSpanError(span, err) w.logger.Error("服务方法执行失败", zap.String("service", serviceName), zap.String("method", methodName), zap.Error(err), zap.String("trace_id", w.tracer.GetTraceID(ctx)), ) } else { w.tracer.SetSpanSuccess(span) } return err } // TracedUserService 自动追踪的用户服务包装器 type TracedUserService struct { service interfaces.UserService wrapper *ServiceWrapper } // NewTracedUserService 创建带追踪的用户服务 func NewTracedUserService(service interfaces.UserService, wrapper *ServiceWrapper) interfaces.UserService { return &TracedUserService{ service: service, wrapper: wrapper, } } func (t *TracedUserService) Name() string { return "user-service" } func (t *TracedUserService) Initialize(ctx context.Context) error { return t.wrapper.TraceServiceCall(ctx, "user", "initialize", t.service.Initialize) } func (t *TracedUserService) HealthCheck(ctx context.Context) error { return t.service.HealthCheck(ctx) // 不追踪健康检查 } func (t *TracedUserService) Shutdown(ctx context.Context) error { return t.wrapper.TraceServiceCall(ctx, "user", "shutdown", t.service.Shutdown) } func (t *TracedUserService) Register(ctx context.Context, req *commands.RegisterUserCommand) (*entities.User, error) { var result *entities.User var err error traceErr := t.wrapper.TraceServiceCall(ctx, "user", "register", func(ctx context.Context) error { result, err = t.service.Register(ctx, req) return err }) if traceErr != nil { return nil, traceErr } return result, err } func (t *TracedUserService) LoginWithPassword(ctx context.Context, req *commands.LoginWithPasswordCommand) (*entities.User, error) { var result *entities.User var err error traceErr := t.wrapper.TraceServiceCall(ctx, "user", "login_password", func(ctx context.Context) error { result, err = t.service.LoginWithPassword(ctx, req) return err }) if traceErr != nil { return nil, traceErr } return result, err } func (t *TracedUserService) LoginWithSMS(ctx context.Context, req *commands.LoginWithSMSCommand) (*entities.User, error) { var result *entities.User var err error traceErr := t.wrapper.TraceServiceCall(ctx, "user", "login_sms", func(ctx context.Context) error { result, err = t.service.LoginWithSMS(ctx, req) return err }) if traceErr != nil { return nil, traceErr } return result, err } func (t *TracedUserService) ChangePassword(ctx context.Context, userID string, req *commands.ChangePasswordCommand) error { return t.wrapper.TraceServiceCall(ctx, "user", "change_password", func(ctx context.Context) error { return t.service.ChangePassword(ctx, userID, req) }) } func (t *TracedUserService) GetByID(ctx context.Context, id string) (*entities.User, error) { var result *entities.User var err error traceErr := t.wrapper.TraceServiceCall(ctx, "user", "get_by_id", func(ctx context.Context) error { result, err = t.service.GetByID(ctx, id) return err }) if traceErr != nil { return nil, traceErr } return result, err }