本文介紹Go運(yùn)行環(huán)境的鏈路追蹤相關(guān)內(nèi)容。
背景信息
阿里云鏈路追蹤服務(wù)(Tracing Analysis)基于OpenTracing標(biāo)準(zhǔn),兼容開(kāi)源社區(qū),為分布式應(yīng)用的開(kāi)發(fā)者提供了完整地分布式調(diào)用鏈查詢(xún)和診斷、分布式拓?fù)鋭?dòng)態(tài)發(fā)現(xiàn)、應(yīng)用性能實(shí)時(shí)匯總等功能。
函數(shù)計(jì)算與鏈路追蹤集成后,支持使用Jaeger SDK和OpenTelemetry上傳鏈路信息,使您能夠跟蹤函數(shù)的執(zhí)行,幫助您快速分析和診斷Serverless架構(gòu)下的性能瓶頸,提高Serverless場(chǎng)景的開(kāi)發(fā)診斷效率。
功能簡(jiǎn)介
您可以在函數(shù)計(jì)算控制臺(tái)配置鏈路追蹤。具體操作,請(qǐng)參見(jiàn)配置鏈路追蹤。
如您還需查看函數(shù)內(nèi)業(yè)務(wù)側(cè)的耗時(shí),例如,在函數(shù)內(nèi)訪問(wèn)RDS,NAS等服務(wù)的耗時(shí),可以通過(guò)創(chuàng)建自定義Span來(lái)實(shí)現(xiàn)。
示例代碼
函數(shù)計(jì)算的鏈路分析基于OpenTracing協(xié)議的Jaeger實(shí)現(xiàn),Go運(yùn)行時(shí)提供以下兩種創(chuàng)建自定義Span的方式。
使用OpenTelemetry(推薦)
在Go語(yǔ)言的代碼中,您可以通過(guò)OpenTelemetry SDK手動(dòng)埋點(diǎn),將數(shù)據(jù)上報(bào)到鏈路追蹤服務(wù)端。完整的示例代碼,請(qǐng)參見(jiàn)golang-tracing-openTelemetry。
- 添加依賴(lài)。
go get github.com/aliyun/fc-runtime-go-sdk go get go get go.opentelemetry.io/otel go get go.opentelemetry.io/otel/sdk go get go.opentelemetry.io/otel/exporters/jaeger
- 上報(bào)數(shù)據(jù)到鏈路追蹤服務(wù)端。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // 獲取函數(shù)計(jì)算上下文Tracing信息 fctx, ok := fccontext.FromContext(ctx) if !ok { return "", fmt.Errorf("failed to get FcContext") } spanCtx, endpoint, err := getFcTracingInfo(fctx) if err != nil { return "", fmt.Errorf("failed to getFcTracingInfo, error: %v", err) } // 創(chuàng)建Tracer Provider tp, err := NewTracerProvider(endpoint) if err != nil { return "", fmt.Errorf("OpenTracingJaegerEndpoint: %s, error: %v", fctx.Tracing.JaegerEndpoint, err) } // 設(shè)置為全局 otel.SetTracerProvider(tp) if err != nil { return "", fmt.Errorf("failed to getFcSpanCtx, error: %v", err) } // 創(chuàng)建自定義Span startMySpan(trace.ContextWithSpanContext(ctx, spanCtx)) return fmt.Sprintf("hello world! 你好,%s!", event.Name), nil }
- 創(chuàng)建一個(gè)
tracerProvider
,提供對(duì)Tracers的訪問(wèn)。func tracerProvider(url string) (*tracesdk.TracerProvider, error) { // 創(chuàng)建Jaeger exporter exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) if err != nil { return nil, err } tp := tracesdk.NewTracerProvider( // 注冊(cè)exporter tracesdk.WithBatcher(exp), // 在Resource里記錄應(yīng)用信息 tracesdk.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("FCTracer"), attribute.String("environment", "production"), attribute.Int64("ID", 1), )), ) return tp, nil }
- 獲取上下文的Tracing信息,將OpenTracingSpanContext轉(zhuǎn)換為OpenTelemetrySpanContext。
func getFcTracingInfo(fctx *fccontext.FcContext) (trace.SpanContext, string, error) { // 獲取Tracing信息 spanContext := trace.SpanContext{} endpoint := fctx.Tracing.JaegerEndpoint OpenTracingSpanContext := fctx.Tracing.OpenTracingSpanContext if len(endpoint) == 0 { return spanContext, endpoint, fmt.Errorf("invalid jaeger endpoint") } spanContextSlice := strings.Split(OpenTracingSpanContext, ":") // 填充TracingID高位 tid, err := trace.TraceIDFromHex("0000000000000000" + spanContextSlice[0]) if err != nil { return spanContext, endpoint, err } fid := trace.FlagsSampled spanContext = spanContext.WithTraceID(tid).WithTraceFlags(fid).WithRemote(true) return spanContext, endpoint, nil }
- 創(chuàng)建
tracer
并通過(guò)轉(zhuǎn)換的OpenTelemetrySpanContext創(chuàng)建子Span。每一個(gè)Span代表調(diào)用鏈中被命名并計(jì)時(shí)的連續(xù)性執(zhí)行片段,您也可以基于該Span繼續(xù)創(chuàng)建子Span。func startMySpan(ctx context.Context) { // 使用全局TracerProvider. tr := otel.Tracer("fc-Trace") ctx, parentSpan := tr.Start(ctx, "fc-operation") defer parentSpan.End() parentSpan.SetAttributes(attribute.Key("version").String("fc-v1")) time.Sleep(150 * time.Millisecond) child(ctx) } func child(ctx context.Context) { tr := otel.Tracer("fc-Trace") _, childSpan := tr.Start(ctx, "fc-operation-child") defer childSpan.End() time.Sleep(100 * time.Millisecond) childSpan.AddEvent("timeout") }
使用Jaeger SDK
您可以通過(guò)Jaeger SDK埋點(diǎn),將數(shù)據(jù)上報(bào)到鏈路追蹤服務(wù)端。完整的示例代碼,請(qǐng)參見(jiàn)golang-tracing。
- 添加依賴(lài)。
go get github.com/aliyun/fc-runtime-go-sdk go get github.com/opentracing/opentracing-go go get github.com/uber/jaeger-client-go
- 上報(bào)數(shù)據(jù)到鏈路追蹤服務(wù)端。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // 獲取函數(shù)計(jì)算上下文Tracing信息 fctx, _ := fccontext.FromContext(ctx) endpoint := fctx.Tracing.JaegerEndpoint OpenTracingSpanContext := fctx.Tracing.OpenTracingSpanContext if len(endpoint) == 0 { return "", fmt.Errorf("invalid jaeger endpoint") } // 創(chuàng)建Tracer tracer, closer := NewJaegerTracer("FCTracer", endpoint) defer closer.Close() // 恢復(fù)spanContext spanContext, err := jaeger.ContextFromString(OpenTracingSpanContext) if err != nil { return "", fmt.Errorf("OpenTracingSpanContext: %s, error: %v", fctx.Tracing.OpenTracingSpanContext, err) } // 創(chuàng)建自定義Span startMySpan(spanContext, tracer) return fmt.Sprintf("hello world! 你好,%s!", event.Name), nil }
- 創(chuàng)建一個(gè)
tracer
對(duì)象,提供對(duì)Tracers的訪問(wèn)。func NewJaegerTracer(service, endpoint string) (opentracing.Tracer, io.Closer) { sender := transport.NewHTTPTransport(endpoint) tracer, closer := jaeger.NewTracer(service, jaeger.NewConstSampler(true), jaeger.NewRemoteReporter(sender)) return tracer, closer }
- 轉(zhuǎn)換spanContext并創(chuàng)建自定義Span,您也可以基于該Span繼續(xù)創(chuàng)建子Span。
func startMySpan(context jaeger.SpanContext, tracer opentracing.Tracer) { parentSpan := tracer.StartSpan("MyFCSpan", opentracing.ChildOf(context)) defer parentSpan.Finish() parentSpan.SetOperationName("fc-operation") parentSpan.SetTag("version", "fc-v1") time.Sleep(150 * time.Millisecond) // 開(kāi)啟子Span childSpan := tracer.StartSpan("fc-operation-child", opentracing.ChildOf(parentSpan.Context())) defer childSpan.Finish() time.Sleep(100 * time.Millisecond) childSpan.LogFields( log.String("type", "cache timeout"), log.Int("waited.millis", 100)) }