熟悉Opentelemery接口
-
Resources For example, a process producing telemetry that is running in a container on Kubernetes has a process name, a pod name, a namespace, and possibly a deployment name. All four of these attributes can be included in the resource.
-
Semantic Conventions语义约定 一种特殊的Attribute, key值是固定的属性, 如key必须是service.name,约定好的.
resource配置
import (
"context"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"log"
"time"
)
func initTracer() func() {
// 创建一个输出导出器
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(sdkresource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("test-svc"),
attribute.String("environment", "stag"),
attribute.Int64("ID", 1),
)),
)
// 设置全局TracerProvider
otel.SetTracerProvider(tp)
return func() {
_ = tp.Shutdown(context.Background())
}
}
resource其它方法
r, _ := resource.New(
context.Background(),
resource.WithFromEnv(), // Discover and provide attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
resource.WithTelemetrySDK(), // Discover and provide information about the OpenTelemetry SDK used.
resource.WithProcess(), // Discover and provide process information.
resource.WithOS(), // Discover and provide OS information.
resource.WithContainer(), // Discover and provide container information.
resource.WithHost(), // Discover and provide host information.
resource.WithAttributes(attribute.String("foo", "bar")), // Add custom resource attributes.
// resource.WithDetectors(thirdparty.Detector{}), // Bring your own external Detector implementation.
)
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter,
// Default is 5s. Set to 1s for demonstrative purposes.
trace.WithBatchTimeout(time.Second)),
trace.WithResource(r),
)
func newResource() (*resource.Resource, error) {
return resource.Merge(resource.Default(),
resource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("my-service"),
semconv.ServiceVersion("0.1.0"),
))
}
生成span
ctx, span := tracer.Start(
ctx,
name,
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attr...)
)
startOpts := append([]trace.SpanStartOption{
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attr...),
},
cfg.SpanStartOptions...,
)
ctx, span := tracer.Start(
ctx,
name,
startOpts...,
)
单进程集成Opentelemery
span添加属性和event
package main
import (
"context"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"log"
"time"
)
func initTracer() func() {
// 创建一个输出导出器
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(
exporter,
// Default is 5s. Set to 1s for demonstrative purposes.
sdktrace.WithBatchTimeout(time.Second),
),
sdktrace.WithSampler(sdktrace.AlwaysSample()), //设置全量采样(默认)
//sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.5)), //设置采样率为50%
sdktrace.WithResource(sdkresource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("lcoal-test"),
attribute.String("env", "dev"),
)),
)
// 设置全局TracerProvider
otel.SetTracerProvider(tp)
return func() {
_ = tp.Shutdown(context.Background())
}
}
var tracer = otel.Tracer("example-local")
func main() {
cleanup := initTracer()
defer cleanup()
ctx := context.WithValue(context.Background(), "userID", 1)
// 开始一个新的 span
ctx, span := tracer.Start(ctx, "client-span")
defer span.End()
var msg string = funcA(ctx)
fmt.Println(msg)
time.Sleep(time.Second)
}
func funcA(ctx context.Context) string {
ctx, span := tracer.Start(trace.ContextWithRemoteSpanContext(ctx), trace.WithLinks())
defer span.End()
var attrs []attribute.KeyValue
attrs = append(attrs, attribute.String("k1", "v1"))
attrs = append(attrs, attribute.Bool("k2", false))
attrs = append(attrs, attribute.StringSlice("k2", []string{"v1", "v2"}))
span.SetAttributes(attrs...)
span.AddEvent("this is an event")
time.Sleep(time.Second)
return "VIP"
}
export OTEL_RESOURCE_ATTRIBUTES='service.version=1.0,deployment.environment=dev,service.name=local-test-svcname,newrelic.service=local-test-svc,dd.service=local-test-svc'
go sdk trace采样率支持
type Sampler
func AlwaysSample() Sampler
func NeverSample() Sampler
func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler
func TraceIDRatioBased(fraction float64) Sampler
Resouce自动携带其它选项
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
A base endpoint URL for any signal type, with an optionally-specified port number. Helpful for when you’re sending more than one signal to the same endpoint and want one environment variable to control the endpoint.
Default value:
* gRPC: "http://localhost:4317"
* HTTP: "http://localhost:4318"
package main
import (
"context"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"log"
"time"
)
func newResource() (*sdkresource.Resource, error) {
return sdkresource.Merge(sdkresource.Default(),
sdkresource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("my-service"),
semconv.ServiceVersion("0.1.0"),
))
}
func initTracer() func() {
// 创建一个输出导出器
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
log.Fatal(err)
}
r, _ := sdkresource.New(
context.Background(),
sdkresource.WithFromEnv(), // Discover and provide attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
sdkresource.WithTelemetrySDK(), // Discover and provide information about the OpenTelemetry SDK used.
sdkresource.WithProcess(), // Discover and provide process information.
sdkresource.WithOS(), // Discover and provide OS information.
sdkresource.WithContainer(), // Discover and provide container information.
sdkresource.WithHost(), // Discover and provide host information.
sdkresource.WithAttributes(attribute.String("foo", "bar")), // Add custom resource attributes.
// resource.WithDetectors(thirdparty.Detector{}), // Bring your own external Detector implementation.
)
// 创建 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter,
// Default is 5s. Set to 1s for demonstrative purposes.
sdktrace.WithBatchTimeout(time.Second)),
sdktrace.WithResource(r),
)
// 设置全局TracerProvider
otel.SetTracerProvider(tp)
return func() {
_ = tp.Shutdown(context.Background())
}
}
var tracer = otel.Tracer("example-local")
func main() {
cleanup := initTracer()
defer cleanup()
ctx := context.WithValue(context.Background(), "userID", 1)
// 开始一个新的 span
ctx, span := tracer.Start(ctx, "client-span")
defer span.End()
var msg string = funcA(ctx)
fmt.Println(msg)
time.Sleep(time.Second)
}
func funcA(ctx context.Context) string {
ctx, span := tracer.Start(ctx, "funcA")
defer span.End()
span.SetAttributes(attribute.String("age", "22"))
span.AddEvent("this is an event")
time.Sleep(time.Second)
return "VIP"
}
http集成Opentelemery携带Baggage
- client/main.go
package main
import (
"context"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"log"
"net/http"
)
func initTracer() func() {
// 创建一个输出导出器
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
log.Fatal(err)
}
// 创建 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(sdkresource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("my_http_server"))),
)
// 设置全局TracerProvider
otel.SetTracerProvider(tp)
// 创建 TraceContext 和 Baggage Propagator
tracePropagator := propagation.TraceContext{}
baggagePropagator := propagation.Baggage{}
compositePropagator := propagation.NewCompositeTextMapPropagator(tracePropagator, baggagePropagator)
// 设置全局 Propagator
otel.SetTextMapPropagator(compositePropagator)
return func() {
_ = tp.Shutdown(context.Background())
}
}
func main() {
cleanup := initTracer()
defer cleanup()
tracer := otel.Tracer("example-client")
// 创建一个自定义的 Baggage
userIDMember, _ := baggage.NewMember("userID", "1")
sessionIDMember, _ := baggage.NewMember("sessionID", "xxxx")
bag, _ := baggage.New(
userIDMember,
sessionIDMember,
)
// 将 Baggage 添加到上下文中
ctx := baggage.ContextWithBaggage(context.Background(), bag)
// 开始一个新的 span
ctx, span := tracer.Start(ctx, "client-span")
defer span.End()
// 创建HTTP请求
req, _ := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:8080", nil)
// 将上下文中的追踪信息和 Baggage 注入到HTTP请求的头部
propagator := otel.GetTextMapPropagator()
propagator.Inject(ctx, propagation.HeaderCarrier(req.Header))
// 发送HTTP请求
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
log.Fatalf("Failed to make request: %v", err)
}
defer res.Body.Close()
fmt.Printf("Response status: %s\n", res.Status)
}
curl -X GET "http://localhost:8080" -H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-3c9e1d1a0d80c9b5-01"
curl -X GET "http://localhost:8080" -H "baggage: userID=12345,sessionID=abcde"
- server/main.go
package main
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/baggage"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"log"
"net/http"
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
type contextKey string
const (
userIDKey contextKey = "userID"
sessionIDKey contextKey = "sessionID"
)
func initTracer() func() {
// 创建 Jaeger 导出器
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
log.Fatal(err)
}
// 创建 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(sdkresource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("my_http_server"))),
)
// 设置全局 TracerProvider
otel.SetTracerProvider(tp)
// 创建 TraceContext 和 Baggage Propagator
tracePropagator := propagation.TraceContext{}
baggagePropagator := propagation.Baggage{}
compositePropagator := propagation.NewCompositeTextMapPropagator(tracePropagator, baggagePropagator)
// 设置全局 Propagator
otel.SetTextMapPropagator(compositePropagator)
return func() {
_ = tp.Shutdown(context.Background())
}
}
func traceMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头中提取追踪信息
ctx := otel.GetTextMapPropagator().Extract(c.Request.Context(), propagation.HeaderCarrier(c.Request.Header))
// 开始一个新的 Span
tracer := otel.Tracer("example-server")
ctx, span := tracer.Start(ctx, "server-span")
defer span.End()
// 获取 Baggage 项目
reqBaggage := baggage.FromContext(ctx)
userID := bag.Member("userID").Value()
sessionID := bag.Member("sessionID").Value()
// 将自定义值存储在 Gin 的上下文中
c.Set(string(userIDKey), userID)
c.Set(string(sessionIDKey), sessionID)
span.SetAttributes(attribute.String(
"userID",
reqBaggage.Member("userID").Value()),
)
// 将上下文和 Span 传递到 Gin 的上下文中
c.Request = c.Request.WithContext(ctx)
// 继续处理请求
c.Next()
}
}
func main() {
cleanup := initTracer()
defer cleanup()
// 创建 Gin 引擎
r := gin.Default()
// 使用追踪中间件
r.Use(traceMiddleware())
// 定义一个处理函数
r.GET("/", func(c *gin.Context) {
// 从 Gin 上下文中提取自定义值
userID, _ := c.Get(string(userIDKey))
sessionID, _ := c.Get(string(sessionIDKey))
// 响应客户端
c.JSON(http.StatusOK, gin.H{
"userID": userID,
"sessionID": sessionID,
})
})
// 启动 HTTP 服务器
if err := r.Run("localhost:8080"); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
grpc集成Opentelemery
- proto/hello.proto
syntax = "proto3";
option go_package = ".;proto";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
//protoc --go_out=./proto --go-grpc_out=./proto ./proto/*
- client/main.go
package main
import (
"context"
"flag"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "otel-start/grpc-start/proto"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func initTracer() func() {
exp, _ := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp), sdktrace.WithResource(
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("myserver"),
),
))
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return func() {
tp.Shutdown(context.Background())
}
}
func main() {
cleanUp := initTracer()
defer cleanUp()
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithChainUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
- server/main.go
package main
import (
"context"
"flag"
"fmt"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
"google.golang.org/grpc"
"log"
"net"
pb "otel-start/grpc-start/proto"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func initTracer() func() {
exp, _ := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp), sdktrace.WithResource(
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("myserver"),
),
))
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return func() {
tp.Shutdown(context.Background())
}
}
func main() {
cleanUp := initTracer()
defer cleanUp()
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(
grpc.ChainUnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
)
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}