293 lines
7.4 KiB
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]
|
|
}
|