通过OpenTelemetry上报Go应用数据
在监控Go应用之前,您需要通过客户端将应用数据上报至APM服务端。本小节介绍如何通过OpenTelemetry Go SDK上报Go应用数据。
前提条件
完成vpce接入。
背景信息
OpenTelemetry Go SDK提供了Go语言的分布式链路追踪能力,您可以直接使用OTLP gRPC或者HTTP协议向APM服务端上报数据。
接入步骤
1. 添加OpenTelemetry Go依赖。
package main import ( "context" "flag" "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "go.opentelemetry.io/otel/trace" "io" "log" "net/http" "net/http/httptrace" "time" )
2. 查看接入点信息。
应用列表的接入指引会根据您所在资源池提供“通过 HTTP 上报数据”和“通过 gRPC 上报数据”的ENDPOINT(天翼云vpc网络接入点)、鉴权TOKEN信息。
3. 初始化OpenTelemetry Go SDK。
方式1:使用HTTP协议上报数据。(注意需将<token>和<endpoint>替换成相应地域的接入点信息。)
func initProvider() func() {
ctx := context.Background()
auth := map[string]string{
"x-ctg-authorization": "<token>", // 替换成token信息
}
traceClient := otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint("<endpoint>"), //替换成otel http的上报地址。
otlptracehttp.WithURLPath("/api/traces"),
otlptracegrpc.WithHeaders(auth),
otlptracegrpc.WithDialOption(grpc.WithBlock()))
log.Println("start to connect to server")
traceExp, err := otlptrace.New(ctx, traceClient)
handleErr(err, "Failed to create the collector trace exporter")
res, err := resource.New(ctx,
resource.WithFromEnv(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithAttributes(
// 在可观测链路 OpenTelemetry 版后端显示的服务名称。
semconv.ServiceNameKey.String("otel-go-client-demo"),
semconv.HostNameKey.String(""),
),
)
handleErr(err, "failed to create resource")
bsp := sdktrace.NewBatchSpanProcessor(traceExp)
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
)
otel.SetTracerProvider(tracerProvider)
return func() {
cxt, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := traceExp.Shutdown(cxt); err != nil {
otel.Handle(err)
}
}
}方式2:使用gRPC协议上报数据。(注意需将<token>和<endpoint>替换成相应地域的接入点信息。)
func initProviderGrpc() func() {
ctx := context.Background()
auth := map[string]string{
"x-ctg-authorization": "<token>", //接入流程里面获取token信息
}
traceClient := otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint("<endpoint>"), //替换成grpc接入信息
otlptracegrpc.WithHeaders(auth),
otlptracegrpc.WithDialOption(grpc.WithBlock()))
log.Println("start to connect to server")
traceExp, err := otlptrace.New(ctx, traceClient)
handleErr(err, "Failed to create the collector trace exporter")
res, err := resource.New(ctx,
resource.WithFromEnv(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithAttributes(
// 在可观测链路 OpenTelemetry 版后端显示的服务名称。
semconv.ServiceNameKey.String("otel-go-client-demo_provider"),
semconv.HostNameKey.String(""),
),
)
handleErr(err, "failed to create resource")
bsp := sdktrace.NewBatchSpanProcessor(traceExp)
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
)
otel.SetTracerProvider(tracerProvider)
return func() {
cxt, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := traceExp.Shutdown(cxt); err != nil {
otel.Handle(err)
}
}
}4. client端上报例子。
func sendReq(url string, ctx context.Context) {
// 构造一个trace client
client := http.Client{
Transport: otelhttp.NewTransport(
http.DefaultTransport,
otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
return otelhttptrace.NewClientTrace(ctx)
}),
),
}
tr := otel.Tracer("example/client")
err := func(ctx context.Context) error {
ctx, span := tr.Start(ctx, "say hello", trace.WithAttributes(semconv.PeerService("ExampleService")))
defer span.End()
ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
res, err := client.Do(req)
if err != nil {
panic(err)
}
_, err = io.ReadAll(res.Body)
_ = res.Body.Close()
return err
}(ctx)
if err != nil {
log.Fatal(err)
}
}
func doClient(url string, ctx context.Context) {
// 不断请求数据
for {
sendReq(url, ctx)
time.Sleep(3 * time.Second)
}
}
func main() {
shutdown := initProvider()
defer shutdown()
url := flag.String("server", "http://localhost:8070/hello", "server url")
flag.Parse()
bag, _ := baggage.Parse("username=xxx")
ctx := baggage.ContextWithBaggage(context.Background(), bag)
doClient(*url, ctx)
}5. 服务端上报例子。
func handler() {
uk := attribute.Key("username") //加点属性
helloHandler := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
//继续调用下一个服务最终效果是client-》hello-》hello1
sendReq("http://localhost:8071/hello1", ctx)
}
helloHandler1 := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
span := trace.SpanFromContext(ctx)
bag := baggage.FromContext(ctx)
span.AddEvent("handling this...", trace.WithAttributes(uk.String(bag.Member("username").Value())))
_, _ = io.WriteString(w, "Hello, world!\n")
}
otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello")
otelHandler1 := otelhttp.NewHandler(http.HandlerFunc(helloHandler1), "Hello1")
http.Handle("/hello", otelHandler)
http.Handle("/hello1", otelHandler1)
}
func main() {
flag.Parse()
shutdown := initProvider()
defer shutdown()
handler()
err := http.ListenAndServe(":" + *serverPort, nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts.
if err != nil {
log.Fatal(err)
}
}6、通过以上步骤,最后就在APM控制台的应用列表页面选择目标应用,查看监控数据。具体参考示例。
通过SkyWalking SDK上报Go应用数据
在监控Go应用之前,您需要通过客户端将应用数据上报至APM服务端。本小节介绍如何通过SkyWalking SDK上报Go应用数据。
接入步骤
1. 添加SkyWalking Go依赖。
package main import ( "context" "flag" "fmt" "github.com/SkyAPM/go2sky" httpPlugin "github.com/SkyAPM/go2sky/plugins/http" "github.com/SkyAPM/go2sky/reporter" "io/ioutil" "log" "net/http" "time" )
2. 查看接入点信息。
应用列表的接入指引会根据您所在资源池提供v3版本接入点(Skywalking 8.*)的ENDPOINT(天翼云vpc网络接入点)、鉴权TOKEN信息。
3. 初始化SkyWalking Go SDK。
使用gRPC协议上报数据。(注意需将<token>和<endpoint>替换成相应地域的接入点信息。)
serverPort := flag.String("port", "9891", "server port")
token := flag.String("token", "<token>", "token") // 鉴权信息
endpoint := flag.String("endpoint", "<endpoint>", "endpoint") // grpc端口
authMap := map[string]string{
"x-ctg-authorization": *token,
}
flag.Parse()
report, err := reporter.NewGRPCReporter(*endpoint, reporter.WithAuthHeader(authMap))
//report, err := reporter.NewLogReporter()
tracer, err := go2sky.NewTracer(service, go2sky.WithReporter(report))4. client端上报例子。
func do(tracer *go2sky.Tracer, serverPort string, client *http.Client) {
for {
time.Sleep(5*time.Second)
doClient(tracer, client, "client", "http://localhost:" + serverPort + "/helloserver")
}
}
func doClient(tracer *go2sky.Tracer, client *http.Client, spanName string, url string) {
clientReq, err1 := http.NewRequest(http.MethodGet, url, nil)
parentSpan, newCtx, _ := tracer.CreateLocalSpan(context.Background())
parentSpan.SetOperationName(spanName)
defer parentSpan.End()
clientReq = clientReq.WithContext(newCtx)
res, err1 := client.Do(clientReq)
if err1 != nil {
fmt.Println(err1, "send req error")
return
}
defer res.Body.Close()
body, err1 := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
}5. 服务端上报例子。
route := http.NewServeMux()
route.HandleFunc("/helloserver", func(writer http.ResponseWriter, request *http.Request) {
upstreamURL := "http://localhost:9891/hello1"
client, err := httpPlugin.NewClient(tracer)
clientReq, err1 := http.NewRequest(http.MethodGet, upstreamURL, nil)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("unable to create http request error: %v \n", err)
return
}
subSpan, newCtx, _ := tracer.CreateLocalSpan(request.Context())
subSpan.SetOperationName("server2")
defer subSpan.End()
clientReq = clientReq.WithContext(newCtx)
res, err1 := client.Do(clientReq)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("unable to do http request error: %v \n", err)
return
}
defer res.Body.Close()
body, err1 := ioutil.ReadAll(res.Body)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("read http response error: %v \n", err)
return
}
writer.WriteHeader(res.StatusCode)
_, _ = writer.Write(body)
})
sm, err := httpPlugin.NewServerMiddleware(tracer)
if err != nil {
log.Fatalf("create server middleware error %v \n", err)
}
err = http.ListenAndServe(":" + *serverPort, sm(route))
if err != nil {
log.Fatal(err)
}6. 通过以上步骤,最后就在APM控制台的应用列表页面选择目标应用,查看监控数据。
通过SkyWalking SDK上报Go应用数据
在监控Go应用之前,您需要通过客户端将应用数据上报至APM服务端。本小节介绍如何通过SkyWalking SDK上报Go应用数据。
接入步骤
1. 添加SkyWalking Go依赖。
package main import ( "context" "flag" "fmt" "github.com/SkyAPM/go2sky" httpPlugin "github.com/SkyAPM/go2sky/plugins/http" "github.com/SkyAPM/go2sky/reporter" "io/ioutil" "log" "net/http" "time" )
2. 查看接入点信息。
应用列表的接入指引会根据您所在资源池提供v3版本接入点(Skywalking 8.*)的ENDPOINT(天翼云vpc网络接入点)、鉴权TOKEN信息。
3. 初始化SkyWalking Go SDK。
使用gRPC协议上报数据。(注意需将<token>和<endpoint>替换成相应地域的接入点信息。)
serverPort := flag.String("port", "9891", "server port")
token := flag.String("token", "<token>", "token") // 鉴权信息
endpoint := flag.String("endpoint", "<endpoint>", "endpoint") // grpc端口
authMap := map[string]string{
"x-ctg-authorization": *token,
}
flag.Parse()
report, err := reporter.NewGRPCReporter(*endpoint, reporter.WithAuthHeader(authMap))
//report, err := reporter.NewLogReporter()
tracer, err := go2sky.NewTracer(service, go2sky.WithReporter(report))4. client端上报例子。
func do(tracer *go2sky.Tracer, serverPort string, client *http.Client) {
for {
time.Sleep(5*time.Second)
doClient(tracer, client, "client", "http://localhost:" + serverPort + "/helloserver")
}
}
func doClient(tracer *go2sky.Tracer, client *http.Client, spanName string, url string) {
clientReq, err1 := http.NewRequest(http.MethodGet, url, nil)
parentSpan, newCtx, _ := tracer.CreateLocalSpan(context.Background())
parentSpan.SetOperationName(spanName)
defer parentSpan.End()
clientReq = clientReq.WithContext(newCtx)
res, err1 := client.Do(clientReq)
if err1 != nil {
fmt.Println(err1, "send req error")
return
}
defer res.Body.Close()
body, err1 := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
}5. 服务端上报例子。
route := http.NewServeMux()
route.HandleFunc("/helloserver", func(writer http.ResponseWriter, request *http.Request) {
upstreamURL := "http://localhost:9891/hello1"
client, err := httpPlugin.NewClient(tracer)
clientReq, err1 := http.NewRequest(http.MethodGet, upstreamURL, nil)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("unable to create http request error: %v \n", err)
return
}
subSpan, newCtx, _ := tracer.CreateLocalSpan(request.Context())
subSpan.SetOperationName("server2")
defer subSpan.End()
clientReq = clientReq.WithContext(newCtx)
res, err1 := client.Do(clientReq)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("unable to do http request error: %v \n", err)
return
}
defer res.Body.Close()
body, err1 := ioutil.ReadAll(res.Body)
if err1 != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Printf("read http response error: %v \n", err)
return
}
writer.WriteHeader(res.StatusCode)
_, _ = writer.Write(body)
})
sm, err := httpPlugin.NewServerMiddleware(tracer)
if err != nil {
log.Fatalf("create server middleware error %v \n", err)
}
err = http.ListenAndServe(":" + *serverPort, sm(route))
if err != nil {
log.Fatal(err)
}6. 通过以上步骤,最后就在APM控制台的应用列表页面选择目标应用,查看监控数据。