Files
tyapi-server/internal/shared/tracing/decorators.go

293 lines
7.4 KiB
Go

package tracing
import (
"context"
"fmt"
"reflect"
"runtime"
"strings"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)
// TracableService 可追踪的服务接口
type TracableService interface {
Name() string
}
// ServiceDecorator 服务装饰器
type ServiceDecorator struct {
tracer *Tracer
logger *zap.Logger
config DecoratorConfig
}
// DecoratorConfig 装饰器配置
type DecoratorConfig struct {
EnableMethodTracing bool
ExcludePatterns []string
IncludeArguments bool
IncludeResults bool
SlowMethodThreshold time.Duration
}
// DefaultDecoratorConfig 默认装饰器配置
func DefaultDecoratorConfig() DecoratorConfig {
return DecoratorConfig{
EnableMethodTracing: true,
ExcludePatterns: []string{"Health", "Ping", "Name"},
IncludeArguments: true,
IncludeResults: false,
SlowMethodThreshold: 100 * time.Millisecond,
}
}
// NewServiceDecorator 创建服务装饰器
func NewServiceDecorator(tracer *Tracer, logger *zap.Logger) *ServiceDecorator {
return &ServiceDecorator{
tracer: tracer,
logger: logger,
config: DefaultDecoratorConfig(),
}
}
// WrapService 自动包装服务,为所有方法添加链路追踪
func (d *ServiceDecorator) WrapService(service interface{}) interface{} {
serviceValue := reflect.ValueOf(service)
serviceType := reflect.TypeOf(service)
if serviceType.Kind() == reflect.Ptr {
serviceType = serviceType.Elem()
serviceValue = serviceValue.Elem()
}
// 创建代理结构
proxyType := d.createProxyType(serviceType)
proxyValue := reflect.New(proxyType).Elem()
// 设置原始服务字段
proxyValue.FieldByName("target").Set(reflect.ValueOf(service))
proxyValue.FieldByName("decorator").Set(reflect.ValueOf(d))
return proxyValue.Addr().Interface()
}
// createProxyType 创建代理类型
func (d *ServiceDecorator) createProxyType(serviceType reflect.Type) reflect.Type {
// 获取服务名称
serviceName := d.getServiceName(serviceType)
// 创建代理结构字段
fields := []reflect.StructField{
{
Name: "target",
Type: reflect.PtrTo(serviceType),
},
{
Name: "decorator",
Type: reflect.TypeOf(d),
},
}
// 为每个方法创建包装器方法
for i := 0; i < serviceType.NumMethod(); i++ {
method := serviceType.Method(i)
if d.shouldTraceMethod(method.Name) {
// 创建方法字段(用于存储方法实现)
fields = append(fields, reflect.StructField{
Name: method.Name,
Type: method.Type,
})
}
}
// 创建新的结构类型
proxyType := reflect.StructOf(fields)
// 实现接口方法
d.implementMethods(proxyType, serviceType, serviceName)
return proxyType
}
// shouldTraceMethod 判断是否应该追踪方法
func (d *ServiceDecorator) shouldTraceMethod(methodName string) bool {
if !d.config.EnableMethodTracing {
return false
}
for _, pattern := range d.config.ExcludePatterns {
if strings.Contains(methodName, pattern) {
return false
}
}
return true
}
// getServiceName 获取服务名称
func (d *ServiceDecorator) getServiceName(serviceType reflect.Type) string {
serviceName := serviceType.Name()
// 移除Service后缀
if strings.HasSuffix(serviceName, "Service") {
serviceName = strings.TrimSuffix(serviceName, "Service")
}
return strings.ToLower(serviceName)
}
// TraceMethodCall 追踪方法调用
func (d *ServiceDecorator) TraceMethodCall(
ctx context.Context,
serviceName, methodName string,
fn func(context.Context) ([]reflect.Value, error),
args []reflect.Value,
) ([]reflect.Value, error) {
// 创建span名称
spanName := fmt.Sprintf("%s.%s", serviceName, methodName)
// 开始追踪
ctx, span := d.tracer.StartSpan(ctx, spanName)
defer span.End()
// 添加基础属性
d.tracer.AddSpanAttributes(span,
attribute.String("service.name", serviceName),
attribute.String("service.method", methodName),
attribute.String("service.type", "business"),
)
// 添加参数信息(如果启用)
if d.config.IncludeArguments {
d.addArgumentAttributes(span, args)
}
// 记录开始时间
startTime := time.Now()
// 执行原始方法
results, err := fn(ctx)
// 计算执行时间
duration := time.Since(startTime)
d.tracer.AddSpanAttributes(span,
attribute.Int64("service.duration_ms", duration.Milliseconds()),
)
// 标记慢方法
if duration > d.config.SlowMethodThreshold {
d.tracer.AddSpanAttributes(span,
attribute.Bool("service.slow_method", true),
)
d.logger.Warn("慢方法检测",
zap.String("service", serviceName),
zap.String("method", methodName),
zap.Duration("duration", duration),
zap.String("trace_id", d.tracer.GetTraceID(ctx)),
)
}
// 处理错误
if err != nil {
d.tracer.SetSpanError(span, err)
d.logger.Error("服务方法执行失败",
zap.String("service", serviceName),
zap.String("method", methodName),
zap.Error(err),
zap.String("trace_id", d.tracer.GetTraceID(ctx)),
)
} else {
d.tracer.SetSpanSuccess(span)
// 添加结果信息(如果启用)
if d.config.IncludeResults {
d.addResultAttributes(span, results)
}
}
return results, err
}
// addArgumentAttributes 添加参数属性
func (d *ServiceDecorator) addArgumentAttributes(span trace.Span, args []reflect.Value) {
for i, arg := range args {
if i == 0 && arg.Type().String() == "context.Context" {
continue // 跳过context参数
}
argName := fmt.Sprintf("service.arg_%d", i)
argValue := d.extractValue(arg)
if argValue != "" && len(argValue) < 1000 { // 限制长度避免性能问题
d.tracer.AddSpanAttributes(span,
attribute.String(argName, argValue),
)
}
}
}
// addResultAttributes 添加结果属性
func (d *ServiceDecorator) addResultAttributes(span trace.Span, results []reflect.Value) {
for i, result := range results {
if result.Type().String() == "error" {
continue // 错误在其他地方处理
}
resultName := fmt.Sprintf("service.result_%d", i)
resultValue := d.extractValue(result)
if resultValue != "" && len(resultValue) < 1000 {
d.tracer.AddSpanAttributes(span,
attribute.String(resultName, resultValue),
)
}
}
}
// extractValue 提取值的字符串表示
func (d *ServiceDecorator) extractValue(value reflect.Value) string {
if !value.IsValid() {
return ""
}
switch value.Kind() {
case reflect.String:
return value.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fmt.Sprintf("%d", value.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return fmt.Sprintf("%d", value.Uint())
case reflect.Float32, reflect.Float64:
return fmt.Sprintf("%.2f", value.Float())
case reflect.Bool:
return fmt.Sprintf("%t", value.Bool())
case reflect.Ptr:
if value.IsNil() {
return "nil"
}
return d.extractValue(value.Elem())
case reflect.Struct:
// 对于结构体,只返回类型名
return value.Type().Name()
case reflect.Slice, reflect.Array:
return fmt.Sprintf("[%d items]", value.Len())
default:
return value.Type().Name()
}
}
// implementMethods 实现接口方法(占位符,实际需要运行时代理)
func (d *ServiceDecorator) implementMethods(proxyType, serviceType reflect.Type, serviceName string) {
// 这里是运行时方法实现的占位符
// 实际实现需要使用reflect.MakeFunc或其他运行时代理技术
}
// GetFunctionName 获取函数名称
func GetFunctionName(fn interface{}) string {
name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
parts := strings.Split(name, ".")
return parts[len(parts)-1]
}