feat(架构): 完善基础架构设计
This commit is contained in:
292
internal/shared/tracing/decorators.go
Normal file
292
internal/shared/tracing/decorators.go
Normal file
@@ -0,0 +1,292 @@
|
||||
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]
|
||||
}
|
||||
Reference in New Issue
Block a user