190 lines
4.7 KiB
Go
190 lines
4.7 KiB
Go
|
|
package tracing
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"go.opentelemetry.io/otel/attribute"
|
||
|
|
"go.uber.org/zap"
|
||
|
|
|
||
|
|
"tyapi-server/internal/domains/user/dto"
|
||
|
|
"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 *dto.RegisterRequest) (*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 *dto.LoginWithPasswordRequest) (*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 *dto.LoginWithSMSRequest) (*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 *dto.ChangePasswordRequest) 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
|
||
|
|
}
|