前提条件
完成vpce接入。
背景信息
OpenTelemetry Go SDK提供了Go语言的分布式链路追踪能力,您可以直接使用OTLP gRPC或者HTTP协议向APM服务端上报数据。
接入步骤
- 添加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"
)
- 查看接入点信息。
应用列表的接入指引会根据您所在资源池提供“通过 HTTP 上报数据”和“通过 gRPC 上报数据”的ENDPOINT(天翼云vpc网络接入点)、鉴权TOKEN信息。
- 初始化OpenTelemetry Go SDK。
(注意需将token和endpoint替换成相应地域的接入点信息。)
注意需将token和endpoint替换成相应地域的接入点信息。
方式1:使用HTTP协议上报数据。
func initProvider() func() {
ctx := context.Background()
auth := map[string]string{
"x-ctg-authorization": "<token>", // 替换成token信息
}
path := flag.String("path", "/v1/traces", "path")
traceClient := otlptracehttp.NewClient(
otlptracehttp.WithEndpoint("<endpoint>"), // 替换成otel http的上报地址。
otlptracehttp.WithURLPath(*path),
otlptracehttp.WithHeaders(auth),
otlptracehttp.WithInsecure())
otlptracehttp.WithCompression(1)
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(
// 在APM控制台显示的服务名称。
semconv.ServiceNameKey.String("otel-go-client-demo"),
// 在APM控制台显示的主机名称。
semconv.HostNameKey.String("your-host-name"),
// 在APM控制台显示的服务端地址。
semconv.NetSockHostAddr("service_addr"),
// 在APM控制台显示的客户端地址。
semconv.NetSockPeerAddr("client_addr"),
),
)
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协议上报数据。
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(
// 在APM控制台显示的服务名称。
semconv.ServiceNameKey.String("otel-go-client-demo_provider"),
// 在APM控制台显示的主机名称。
semconv.HostNameKey.String("your-host-name"),
// 在APM控制台显示的服务端地址。
semconv.NetSockHostAddr("service_addr"),
// 在APM控制台显示的客户端地址。
semconv.NetSockPeerAddr("client_addr"),
),
)
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)
}
}
}
- 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)
}
- 服务端上报例子。
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)
}
}
- 通过以上步骤,最后就在APM控制台的应用列表页面选择目标应用,查看监控数据。