go2sky的封装及使用

发布于:2025-08-05 ⋅ 阅读:(14) ⋅ 点赞:(0)

初始化go2sky

package sky

import (
	"encoding/json"
	"github.com/SkyAPM/go2sky"
	"github.com/SkyAPM/go2sky/reporter"
	"log"
	"sync"
)

var once sync.Once

var globalTracer *go2sky.Tracer

func NewGlobalTracer(addr, srvName string, ops ...ReporterOption) {
	once.Do(func() {
		globalTracer = NewTracer(addr, srvName, ops...)
	})
}

func NewTracer(addr, srvName string, ops ...ReporterOption) *go2sky.Tracer {
	r, err := reporter.NewGRPCReporter(addr)
	if err != nil {
		log.Printf("new reporter error %v \n", err)
		return nil
	}

	// new warped report
	nr := &Report{Reporter: r}
	for _, op := range ops {
		op(nr)
	}
	tracer, err := go2sky.NewTracer(srvName, go2sky.WithReporter(nr))
	if err != nil {
		log.Printf("create tracer error %v \n", err)
	}
	return tracer
}

// Report warp go2sky.GRPCReporter
type Report struct {
	go2sky.Reporter
	debug bool
}

type ReporterOption func(r *Report)

func WithDebug(debug bool) ReporterOption {
	return func(r *Report) {
		r.debug = debug
	}
}

func (t *Report) Send(spans []go2sky.ReportedSpan) {
	t.Reporter.Send(spans)
	if t.debug {
		bs, _ := json.Marshal(spans)
		log.Printf("trace span:%s", string(bs))
	}
}

操作封装

package sky

import (
	"context"
	"github.com/SkyAPM/go2sky"
	"github.com/SkyAPM/go2sky/propagation"
	v3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
	"time"
)

// Trace 封装 go2sky 的 Span,提供了创建入口、出口和本地 Trace 的方法
// 简化使用 go2sky 的 Span 的流程,屏蔽组件未初始化、创建span报错、span判空,提供了更友好的接口
// DefaultTrace 是一个实现了 Trace 接口的结构体,代表一个有效的 Trace
// NonTrace 是一个实现了 Trace 接口的空结构体,代表一个无效的 Trace
// 当go2sky没有初始化时,返回 NonTrace,避免代码中出现空指针异常
type Trace interface {
	Context() context.Context                                    // 返回携带链路信息的 context.Context,由 go2sky 生成
	End()                                                        // 结束当前的 Trace
	Tag(key, value string)                                       // 设置tag,在ui上可以进行过滤
	SetComponent(int32)                                          // 设置组件ID,skywalking oap-server的component-libraries.yml文件规范了中间件组件的ID,如dubbo=3
	SetSpanLayer(v3.SpanLayer)                                   // 设置span的类型,例如 v3.SpanLayer_RPCFramework
	ChildLocalTrace(optName string) Trace                        // 创建local类型的子Trace
	ChildExitTrace(optName, peer string) (Trace, string, string) // 创建exit类型的子Trace,peer是
	GetTraceId() string                                          // 获取当前Trace的TraceId
	Error(time.Time, ...string)                                  // 记录错误信息,time.Time是错误发生的时间,...string是错误信息
	Log(time.Time, ...string)                                    // 记录错误信息,time.Time是错误发生的时间,...string是错误信息
	IsExit() bool                                                // 判断是否Exit
	IsEntry() bool                                               // 判断是否Entry
	IsNon() bool                                                 // Trace 是 NonTrace 实例 返回 true
}

// nonTrace 判读组件是否可用
func noTracer() bool {
	return globalTracer == nil
}

// CreateInnerEntry 自动创建sw8的入口span
func CreateInnerEntry(ctx context.Context, optName string) Trace {
	if noTracer() {
		return nonTrace
	}
	// CreateEntrySpan 的回调函数返回""时,底层会自动生成相关的内容
	s, c, err := globalTracer.CreateEntrySpan(ctx, optName, func(headerKey string) (string, error) {
		return "", nil
	})
	return evalTrace(s, c, err)
}

// CreateEntry 创建指定sw8 header入口的trace
func CreateEntry(ctx context.Context, optName string, sw8, sw8correlation string) Trace {
	if noTracer() {
		return nonTrace
	}

	// sw8 header必须符合规范,否则底层会报错
	s, c, err := globalTracer.CreateEntrySpan(ctx, optName, func(headerKey string) (string, error) {
		if headerKey == propagation.Header {
			return sw8, nil
		}
		if headerKey == propagation.HeaderCorrelation {
			return sw8correlation, nil
		}
		return "", nil
	})
	return evalTrace(s, c, err)
}

// CreateExit 创建出口类型的trace,同时返回sw8、sw8correlation,便于开发者透传给下游服务
func CreateExit(ctx context.Context, optName, peer string) (t Trace, sw8, sw8correlation string) {
	if noTracer() {
		return nonTrace, "", ""
	}

	s, err := globalTracer.CreateExitSpan(ctx, optName, peer, func(headerKey, headerValue string) error {
		if headerKey == propagation.Header {
			sw8 = headerValue
		}
		if headerKey == propagation.HeaderCorrelation {
			sw8correlation = headerValue
		}
		return nil
	})
	return evalTrace(s, ctx, err), sw8, sw8correlation
}

// CreateLocal 创建本地类型的trace
func CreateLocal(ctx context.Context, optName string) Trace {
	if noTracer() {
		return nonTrace
	}
	s, c, err := globalTracer.CreateLocalSpan(ctx, go2sky.WithOperationName(optName))
	return evalTrace(s, c, err)
}

// evalTrace 评估并返回一个 Trace 对象
func evalTrace(s go2sky.Span, c context.Context, err error) Trace {
	if noTracer() {
		return nonTrace
	}
	if err != nil {
		return nonTrace
	}
	if s == nil {
		return nonTrace
	}
	id := go2sky.TraceID(c)
	t := &DefaultTrace{
		ctx:     c,
		span:    s,
		traceId: id,
	}
	return t
}

Trace实现

NonTrace兜底实现

package sky

import (
	"context"
	"github.com/SkyAPM/go2sky"
	v3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
	"time"
)

var nonTrace = &NonTrace{}

type NonTrace struct {
}

func NewNonTrace() *NonTrace {
	return &NonTrace{}
}

func (n NonTrace) End() {
}

func (n NonTrace) Tag(key, value string) {
}

func (n NonTrace) SetComponent(i int32) {
}

func (n NonTrace) SetSpanLayer(layer v3.SpanLayer) {
}

func (n NonTrace) Context() context.Context {
	return context.Background()
}

func (n NonTrace) ChildLocalTrace(optName string) Trace {
	return n
}

func (n NonTrace) ChildExitTrace(optName, peer string) (Trace, string, string) {
	return n, "", ""
}

func (n NonTrace) GetTraceId() string {
	return go2sky.EmptyTraceID
}

func (n NonTrace) IsExit() bool {
	return false
}

func (n NonTrace) IsEntry() bool {
	return false
}

func (n NonTrace) IsNon() bool {
	return true
}

func (n NonTrace) Error(time time.Time, s ...string) {
}

func (n NonTrace) Log(t time.Time, s ...string) {
}

DefaultTrace

package sky

import (
	"context"
	"github.com/SkyAPM/go2sky"
	v3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
	"sync"
	"time"
)

type DefaultTrace struct {
	ctx     context.Context
	span    go2sky.Span
	once    sync.Once
	traceId string
}

// End 结束当前的 DefaultTrace
func (t *DefaultTrace) End() {
	if t == nil {
		return
	}
	if t.span != nil {
		t.once.Do(func() {
			t.span.End()
		})
	}
}

func (t *DefaultTrace) Tag(key, value string) {
	if t.span != nil {
		t.span.Tag(go2sky.Tag(key), value)
	}
}

func (t *DefaultTrace) SetComponent(i int32) {
	if t.span != nil {
		t.span.SetComponent(i)
	}
}

func (t *DefaultTrace) SetSpanLayer(layer v3.SpanLayer) {
	if t.span != nil {
		t.span.SetSpanLayer(layer)
	}
}

func (t *DefaultTrace) Context() context.Context {
	return t.ctx
}

func (t *DefaultTrace) ChildLocalTrace(optName string) Trace {
	return CreateLocal(t.ctx, optName)
}

func (t *DefaultTrace) ChildExitTrace(optName, peer string) (Trace, string, string) {
	return CreateExit(t.ctx, optName, peer)
}

func (t *DefaultTrace) GetTraceId() string {
	return t.traceId
}

func (t *DefaultTrace) IsExit() bool {
	return t.span != nil && t.span.IsExit()
}

func (t *DefaultTrace) IsEntry() bool {
	return t.span != nil && t.span.IsEntry()
}

func (t *DefaultTrace) IsNon() bool {
	return false
}

func (t *DefaultTrace) Error(time time.Time, s ...string) {
	if t.span != nil {
		t.span.Error(time, s...)
	}
}

func (t *DefaultTrace) Log(time time.Time, s ...string) {
	if t.span != nil {
		t.span.Log(time, s...)
	}
}

测试代码

package main

import (
	"context"
	"demo/farmer/trace/sky"
	"fmt"
	"time"
)

func main() {
	sky.NewGlobalTracer("10.202.244.31:11800", "a-chat-server")

	entryA := sky.CreateInnerEntry(context.Background(), "test")
	tid := entryA.GetTraceId()
	fmt.Println("entryA-traceId:", tid)
	exit, sw8, _ := sky.CreateExit(entryA.Context(), "call goroutine", "nope")
	go func(sw8 string) {
		entryB := sky.CreateEntry(context.Background(), "goroutine", sw8, "")
		tid := entryB.GetTraceId()
		fmt.Println("entryB-traceId:", tid)
		entryB.End()
	}(sw8)
	exit.End()
	entryA.End()
	time.Sleep(5 * time.Second)
}

jaeger ui展示
在这里插入图片描述
提示:

  1. 父子span的end顺序要有保证,如果父span先end,那么父子span会变成同级的关系,因为它们的segment_id不再相同。
  2. 跨协程要像跨进程一样处理链路追踪

网站公告

今日签到

点亮在社区的每一天
去签到