SkyWalking Go Toolkit Trace 详解

背景介绍

SkyWalking Go是一个开源的非侵入式Golang代理程序,用于监控、追踪和在分布式系统中进行数据收集。它使用户能够观察系统内请求的流程和延迟,从各个系统组件收集性能数据以进行性能监控,并通过追踪请求的完整路径来解决问题。

在版本v0.3.0中,Skywalking Go引入了 toolkit-trace 工具。Trace APIs 允许用户在插件不支持的情况下将关键操作、函数或服务添加到追踪范围。从而实现追踪和监控这些操作,并可用于故障分析、诊断和性能监控。

在深入了解之前,您可以参考SkyWalking Go Agent快速开始指南来学习如何使用SkyWalking Go Agent。

下面将会介绍如何在特定场景中使用这些接口。

导入 Trace Toolkit

在项目的根目录中执行以下命令:

bash 复制代码
go get github.com/apache/skywalking-go/toolkit

使用 toolkit trace 接口前,需要将该包导入到您的项目中:

arduino 复制代码
"github.com/apache/skywalking-go/toolkit/trace"

手动追踪

Span 是 Tracing 中单个操作的基本单元。它代表在特定时间范围内的操作,比如一个请求、一个函数调用或特定动作。Span记录了特定操作的关键信息,包括开始和结束时间、操作名称、标签(键-值对)以及操作之间的关系。多个 Span 可以形成层次结构。

在遇到 Skywalking Go 不支持的框架的情况下,用户可以手动创建 Span 以获取追踪信息。

(为了作为示例,我删除了已支持的框架。以下仅为示例。请在使用私有或不支持的框架的 API 时参考)

例如,当需要追踪HTTP响应时,可以在处理请求的方法内部使用 trace.CreateEntrySpan() 来创建一个 span,在处理完成后使用 trace.StopSpan() 来结束这个 span。在发送HTTP请求时,使用 trace.CreateExitSpan() 来创建一个 span,在请求返回后结束这个 span。

这里有两个名为 consumer 和 provider 的HTTP服务。当用户访问 consumer 服务时,它在内部接收用户的请求,然后访问 provider 以获取资源。

go 复制代码
// consumer.go
package main

import (
	"io"
	"net/http"

	_ "github.com/apache/skywalking-go"
	"github.com/apache/skywalking-go/toolkit/trace"
)

func getProvider() (*http.Response, error) {
	// 新建 HTTP 请求
	req, err := http.NewRequest("GET", "http://localhost:9998/provider", http.NoBody)
	// 在发送 HTTP 请求之前创建 ExitSpan
	trace.CreateExitSpan("GET:/provider", "localhost:9999",
		func(headerKey, headerValue string) error {
			// Injector 向请求中添加特定的 header 信息
			req.Header.Add(headerKey, headerValue)
			return nil
		})
	// 结束 ExitSpan,使用 defer 确保在函数返回时执行
	defer trace.StopSpan()

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func consumerHandler(w http.ResponseWriter, r *http.Request) {
	// 创建 EntrySpan 来追踪 consumerHandler 方法的执行
	trace.CreateEntrySpan(r.Method+"/consumer", func(headerKey string) (string, error) {
		// Extractor 获取请求中添加的 header 信息
		return r.Header.Get(headerKey), nil
	})
	// 结束 EntrySpan
	defer trace.StopSpan()

	// 准备发送 HTTP 请求
	resp, err := getProvider()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return
	}
	_, _ = w.Write(body)
}

func main() {
	http.HandleFunc("/consumer", consumerHandler)

	_ = http.ListenAndServe(":9999", nil)
}
go 复制代码
// provider.go
package main

import (
	"net/http"

	_ "github.com/apache/skywalking-go"
	"github.com/apache/skywalking-go/toolkit/trace"
)

func providerHandler(w http.ResponseWriter, r *http.Request) {
	// 创建 EntrySpan 来追踪 providerHandler 方法的执行
	trace.CreateEntrySpan("GET:/provider", func(headerKey string) (string, error) {
		return r.Header.Get(headerKey), nil
	})
	// 结束 EntrySpan
	defer trace.StopSpan()

	_, _ = w.Write([]byte("success from provider"))
}

func main() {
	http.HandleFunc("/provider", providerHandler)

	_ = http.ListenAndServe(":9998", nil)
}

然后中终端中执行:

bash 复制代码
go build -toolexec="/path/to/go-agent" -a -o consumer ./consumer.go
./consumer
bash 复制代码
go build -toolexec="/path/to/go-agent" -a -o provider ./provider.go
./provider
bash 复制代码
curl 127.0.0.1:9999/consumer

此时 UI 中将会显示你所创建的span信息

如果需要追踪仅在本地执行的方法,可以使用 trace.CreateLocalSpan()。如果不需要监控来自另一端的信息或状态,可以将 ExitSpanEntrySpan 更改为 LocalSpan

以上方法仅作为示例,用户可以决定追踪的粒度以及程序中需要进行追踪的位置。

注意,如果程序结束得太快,可能会导致 Tracing 数据无法异步发送到 SkyWalking 后端。

填充 Span

当需要记录额外信息时,包括创建/更新标签、追加日志和设置当前被追踪 Span 的新操作名称时,可以使用这些API。这些操作用于增强追踪信息,提供更详细的上下文描述,有助于更好地理解被追踪的事件或操作。

Toolkit trace APIs 提供了一种简便的方式来访问和操作 Trace 数据:

  • 设置标签:SetTag()
  • 添加日志:AddLog()
  • 设置 Span 名称:SetOperationName()
  • 获取各种ID:GetTraceID(), GetSegmentID(), GetSpanID()

例如,如果需要在一个 Span 中记录HTTP状态码,就可以在 Span 未结束时调用以下接口:

go 复制代码
trace.CreateExitSpan("GET:/provider", "localhost:9999", func(headerKey, headerValue string) error {
	r.Header.Add(headerKey, headerValue)
	return nil
})
resp, err := http.Get("http://localhost:9999/provider")
trace.SetTag("status_code", fmt.Sprintf("%d", resp.StatusCode))
spanID := trace.GetSpanID()
trace.StopSpan()

在调用这些方法时,当前线程需要有正在活跃的 span。

异步 APIs

异步API 用于跨 goroutines 操作 spans。包括以下情况:

  • 包含多个 goroutines 的程序,需要在不同上下文中中操作 Span。
  • 在异步操作时更新或记录 Span 的信息。
  • 延迟结束 Span。

按照以下步骤使用:

  • 获取 CreateSpan 的返回值 SpanRef
  • 调用 spanRef.PrepareAsync() ,准备在另一个 goroutine 中执行操作。
  • 当前 goroutine 工作结束后,调用 trace.StopSpan() 结束该 span(仅影响当前 goroutine)。
  • spanRef 传递给另一个 goroutine。
  • 完成工作后在任意 goroutine 中调用 spanRef.AsyncFinish()

以下为示例:

go 复制代码
spanRef, err := trace.CreateLocalSpan("LocalSpan")
if err != nil {
	return
}
spanRef.PrepareAsync()
go func(){
	// some work
	spanRef.AsyncFinish()
}()
// some work
trace.StopSpan()

Correlation Context

Correlation Context 用于在 Span 间传递参数,父 Span 会把 Correlation Context 递给其所有子 Spans。它允许在不同应用程序的 spans 之间传输信息。Correlation Context 的默认元素个数为3,其内容长度不能超过128字节。

Correlation Context 通常用于以下等情况:

  • 在 Spans 之间传递信息:它允许关键信息在不同 Span 之间传输,使上游和下游 Spans 能够获取彼此之间的关联和上下文。
  • 传递业务参数:在业务场景中,涉及在不同 Span 之间传输特定参数或信息,如认证令牌、交易ID等。

用户可以使用 trace.SetCorrelation(key, value) 设置 Correlation Context ,并可以使用 value := trace.GetCorrelation(key) 在下游 spans 中获取相应的值。

例如在下面的代码中,我们将值存储在 span 的标签中,以便观察结果:

go 复制代码
package main

import (
	_ "github.com/apache/skywalking-go"
	"github.com/apache/skywalking-go/toolkit/trace"
	"net/http"
)

func providerHandler(w http.ResponseWriter, r *http.Request) {
	ctxValue := trace.GetCorrelation("key")
	trace.SetTag("result", ctxValue)
}

func consumerHandler(w http.ResponseWriter, r *http.Request) {
	trace.SetCorrelation("key", "value")
	_, err := http.Get("http://localhost:9999/provider")
	if err != nil {
		return
	}
}

func main() {
	http.HandleFunc("/provider", providerHandler)

	http.HandleFunc("/consumer", consumerHandler)

	_ = http.ListenAndServe(":9999", nil)
}

然后在终端执行:

bash 复制代码
export SW_AGENT_NAME=server
go build -toolexec="/path/to/go-agent" -a -o server ./server.go
./server
bash 复制代码
curl 127.0.0.1:9999/consumer

最后在 providerHandler() 的 Span 中找到了 Correlation Context 的信息:

总结

本文讲述了Skywalking Go的 Trace APIs 及其应用。它为用户提供了自定义追踪的功能。

更多关于该接口的介绍见文档:Tracing APIs

欢迎大家来使用新版本。

相关推荐
蒙娜丽宁2 天前
Go语言错误处理详解
ios·golang·go·xcode·go1.19
qq_172805593 天前
GO Govaluate
开发语言·后端·golang·go
littleschemer3 天前
Go缓存系统
缓存·go·cache·bigcache
程序者王大川4 天前
【GO开发】MacOS上搭建GO的基础环境-Hello World
开发语言·后端·macos·golang·go
Grassto4 天前
Gitlab 中几种不同的认证机制(Access Tokens,SSH Keys,Deploy Tokens,Deploy Keys)
go·ssh·gitlab·ci
高兴的才哥5 天前
kubevpn 教程
kubernetes·go·开发工具·telepresence·bridge to k8s
少林码僧5 天前
sqlx1.3.4版本的问题
go
蒙娜丽宁6 天前
Go语言结构体和元组全面解析
开发语言·后端·golang·go
蒙娜丽宁6 天前
深入解析Go语言的类型方法、接口与反射
java·开发语言·golang·go
三里清风_6 天前
Docker概述
运维·docker·容器·go