OpenTelemetry-2.Go接入Jaeger(grpc,gin-http)

目录

1.什么是OpenTelemetry

2.搭建jaeger

3.链路追踪

本地调用

远程调用

GRPC

proto

server端

client端

Gin-HTTP

调用流程

api1

api2

grpc

4.完整代码


1.什么是OpenTelemetry

参考:OpenTelemetry-1.介绍-CSDN博客

2.搭建jaeger

参考:Jaeger分布式链路追踪工具-CSDN博客

3.链路追踪

本地调用

package mainimport ("context""log""time""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)const (service     = "local"environment = "dev"id          = 1
)// tracerProvider returns an OpenTelemetry TracerProvider configured to use
// the Jaeger exporter that will send spans to the provided url. The returned
// TracerProvider will also use a Resource configured with all the information
// about the application.
func tracerProvider(url string) (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithBatcher(exp),// Record information about this application in a Resource.tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),)),)return tp, nil
}func main() {tp, err := tracerProvider("http://10.128.175.196:14268/api/traces")if err != nil {log.Fatal(err)}// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)ctx, cancel := context.WithCancel(context.Background())defer cancel()// Cleanly shutdown and flush telemetry when the application exits.defer func(ctx context.Context) {// Do not make the application hang when it is shutdown.ctx, cancel = context.WithTimeout(ctx, time.Second*5)defer cancel()if err := tp.Shutdown(ctx); err != nil {log.Fatal(err)}}(ctx)tr := tp.Tracer("component-main")ctx, span := tr.Start(ctx, "foo")defer span.End()bar(ctx)
}func bar(ctx context.Context) {// Use the global TracerProvider.tr := otel.Tracer("component-bar")_, span := tr.Start(ctx, "bar")span.SetAttributes(attribute.Key("testset").String("value"))defer span.End()// Do bar...
}

 参考:opentelemetry-go/example/jaeger/main.go at v1.16.0 · open-telemetry/opentelemetry-go · GitHub

远程调用

grpc

proto
syntax = "proto3"; // 指定proto版本
package hello_grpc;     // 指定默认包名// 指定golang包名
option go_package = "/hello_grpc";//定义rpc服务
service HelloService {// 定义函数rpc SayHello (HelloRequest) returns (HelloResponse) {}
}// HelloRequest 请求内容
message HelloRequest {string name = 1;string message = 2;
}// HelloResponse 响应内容
message HelloResponse{string name = 1;string message = 2;
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.33.0
// 	protoc        v3.9.0
// source: grpc_proto/hello.protopackage hello_grpcimport (context "context"grpc "google.golang.org/grpc"codes "google.golang.org/grpc/codes"status "google.golang.org/grpc/status"protoreflect "google.golang.org/protobuf/reflect/protoreflect"protoimpl "google.golang.org/protobuf/runtime/protoimpl"reflect "reflect"sync "sync"
)const (// Verify that this generated code is sufficiently up-to-date._ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)// Verify that runtime/protoimpl is sufficiently up-to-date._ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)// HelloRequest 请求内容
type HelloRequest struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsName    string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
}func (x *HelloRequest) Reset() {*x = HelloRequest{}if protoimpl.UnsafeEnabled {mi := &file_grpc_proto_hello_proto_msgTypes[0]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *HelloRequest) String() string {return protoimpl.X.MessageStringOf(x)
}func (*HelloRequest) ProtoMessage() {}func (x *HelloRequest) ProtoReflect() protoreflect.Message {mi := &file_grpc_proto_hello_proto_msgTypes[0]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
func (*HelloRequest) Descriptor() ([]byte, []int) {return file_grpc_proto_hello_proto_rawDescGZIP(), []int{0}
}func (x *HelloRequest) GetName() string {if x != nil {return x.Name}return ""
}func (x *HelloRequest) GetMessage() string {if x != nil {return x.Message}return ""
}// HelloResponse 响应内容
type HelloResponse struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsName    string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
}func (x *HelloResponse) Reset() {*x = HelloResponse{}if protoimpl.UnsafeEnabled {mi := &file_grpc_proto_hello_proto_msgTypes[1]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *HelloResponse) String() string {return protoimpl.X.MessageStringOf(x)
}func (*HelloResponse) ProtoMessage() {}func (x *HelloResponse) ProtoReflect() protoreflect.Message {mi := &file_grpc_proto_hello_proto_msgTypes[1]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead.
func (*HelloResponse) Descriptor() ([]byte, []int) {return file_grpc_proto_hello_proto_rawDescGZIP(), []int{1}
}func (x *HelloResponse) GetName() string {if x != nil {return x.Name}return ""
}func (x *HelloResponse) GetMessage() string {if x != nil {return x.Message}return ""
}var File_grpc_proto_hello_proto protoreflect.FileDescriptorvar file_grpc_proto_hello_proto_rawDesc = []byte{0x0a, 0x16, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x68, 0x65, 0x6c,0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x5f,0x67, 0x72, 0x70, 0x63, 0x22, 0x3c, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,0x67, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,0x65, 0x32, 0x51, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,0x65, 0x12, 0x41, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e,0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x5f,0x67, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,0x73, 0x65, 0x22, 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x5f, 0x67,0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}var (file_grpc_proto_hello_proto_rawDescOnce sync.Oncefile_grpc_proto_hello_proto_rawDescData = file_grpc_proto_hello_proto_rawDesc
)func file_grpc_proto_hello_proto_rawDescGZIP() []byte {file_grpc_proto_hello_proto_rawDescOnce.Do(func() {file_grpc_proto_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpc_proto_hello_proto_rawDescData)})return file_grpc_proto_hello_proto_rawDescData
}var file_grpc_proto_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_grpc_proto_hello_proto_goTypes = []interface{}{(*HelloRequest)(nil),  // 0: hello_grpc.HelloRequest(*HelloResponse)(nil), // 1: hello_grpc.HelloResponse
}
var file_grpc_proto_hello_proto_depIdxs = []int32{0, // 0: hello_grpc.HelloService.SayHello:input_type -> hello_grpc.HelloRequest1, // 1: hello_grpc.HelloService.SayHello:output_type -> hello_grpc.HelloResponse1, // [1:2] is the sub-list for method output_type0, // [0:1] is the sub-list for method input_type0, // [0:0] is the sub-list for extension type_name0, // [0:0] is the sub-list for extension extendee0, // [0:0] is the sub-list for field type_name
}func init() { file_grpc_proto_hello_proto_init() }
func file_grpc_proto_hello_proto_init() {if File_grpc_proto_hello_proto != nil {return}if !protoimpl.UnsafeEnabled {file_grpc_proto_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*HelloRequest); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}file_grpc_proto_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*HelloResponse); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}}type x struct{}out := protoimpl.TypeBuilder{File: protoimpl.DescBuilder{GoPackagePath: reflect.TypeOf(x{}).PkgPath(),RawDescriptor: file_grpc_proto_hello_proto_rawDesc,NumEnums:      0,NumMessages:   2,NumExtensions: 0,NumServices:   1,},GoTypes:           file_grpc_proto_hello_proto_goTypes,DependencyIndexes: file_grpc_proto_hello_proto_depIdxs,MessageInfos:      file_grpc_proto_hello_proto_msgTypes,}.Build()File_grpc_proto_hello_proto = out.Filefile_grpc_proto_hello_proto_rawDesc = nilfile_grpc_proto_hello_proto_goTypes = nilfile_grpc_proto_hello_proto_depIdxs = nil
}// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6// HelloServiceClient is the client API for HelloService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloServiceClient interface {// 定义函数SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
}type helloServiceClient struct {cc grpc.ClientConnInterface
}func NewHelloServiceClient(cc grpc.ClientConnInterface) HelloServiceClient {return &helloServiceClient{cc}
}func (c *helloServiceClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {out := new(HelloResponse)err := c.cc.Invoke(ctx, "/hello_grpc.HelloService/SayHello", in, out, opts...)if err != nil {return nil, err}return out, nil
}// HelloServiceServer is the server API for HelloService service.
type HelloServiceServer interface {// 定义函数SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
}// UnimplementedHelloServiceServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServiceServer struct {
}func (*UnimplementedHelloServiceServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) {return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) {s.RegisterService(&_HelloService_serviceDesc, srv)
}func _HelloService_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {in := new(HelloRequest)if err := dec(in); err != nil {return nil, err}if interceptor == nil {return srv.(HelloServiceServer).SayHello(ctx, in)}info := &grpc.UnaryServerInfo{Server:     srv,FullMethod: "/hello_grpc.HelloService/SayHello",}handler := func(ctx context.Context, req interface{}) (interface{}, error) {return srv.(HelloServiceServer).SayHello(ctx, req.(*HelloRequest))}return interceptor(ctx, in, info, handler)
}var _HelloService_serviceDesc = grpc.ServiceDesc{ServiceName: "hello_grpc.HelloService",HandlerType: (*HelloServiceServer)(nil),Methods: []grpc.MethodDesc{{MethodName: "SayHello",Handler:    _HelloService_SayHello_Handler,},},Streams:  []grpc.StreamDesc{},Metadata: "grpc_proto/hello.proto",
}
server端
package mainimport ("context""fmt""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0""go_otel/2go_grpc/grpc_proto/hello_grpc""google.golang.org/grpc""google.golang.org/grpc/grpclog""log""net"
)const (service     = "grpc_server"environment = "dev"id          = 2
)var tracer = otel.Tracer(service)// HelloServer 得有一个结构体,需要实现这个服务的全部方法,叫什么名字不重要
type HelloServer struct {
}func (HelloServer) SayHello(ctx context.Context, request *hello_grpc.HelloRequest) (*hello_grpc.HelloResponse, error) {fmt.Println("入参:", request.Name, request.Message)_, span := tracer.Start(ctx, "grpc")span.SetAttributes(attribute.Key("request.Name").String(request.Name))span.SetAttributes(attribute.Key("request.Message").String(request.Message))defer span.End()return &hello_grpc.HelloResponse{Name:    "server",Message: "hello " + request.Name,}, nil
}func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://10.128.175.196:14268/api/traces")))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Cleanly shutdown and flush telemetry when the application exits.defer func() {if err := tp.Shutdown(context.Background()); err != nil {log.Fatal(err)}}()// grpclisten, err := net.Listen("tcp", ":8080")if err != nil {grpclog.Fatalf("Failed to listen: %v", err)}// 创建一个gRPC服务器实例。s := grpc.NewServer(//引入grpc-middleware定义的拦截器grpc.ChainUnaryInterceptor(//openTelemetry 链路追踪otelgrpc.UnaryServerInterceptor(otelgrpc.WithTracerProvider(tp)),),)server := HelloServer{}// 将server结构体注册为gRPC服务。hello_grpc.RegisterHelloServiceServer(s, &server)fmt.Println("grpc server running :8080")// 开始处理客户端请求。err = s.Serve(listen)
}
client端
package mainimport ("context""fmt""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace""go.opentelemetry.io/otel/semconv/v1.17.0""go_otel/2go_grpc/grpc_proto/hello_grpc""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""os""time"
)const (service     = "grpc_client"environment = "dev"id          = 2
)func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://10.128.175.196:14268/api/traces")))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {//创建traceProvidertp, err := tracerProvider()if err != nil {fmt.Println("NewTracerProvider err:", err)os.Exit(1)}conn, err := grpc.Dial("localhost:8080", grpc.WithTransportCredentials(insecure.NewCredentials()),//Unary拦截器grpc.WithChainUnaryInterceptor(//openTelemetry 链路追踪otelgrpc.UnaryClientInterceptor(otelgrpc.WithTracerProvider(tp)),),//Stream拦截器grpc.WithChainStreamInterceptor(func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {// Pre-processing logics := time.Now()cs, err := streamer(ctx, desc, cc, method, opts...)// Post-processing logiclog.Printf("method: %s, latency: %s\n", method, time.Now().Sub(s))return cs, err}),)defer func() {println("关闭TracerProvider。所有注册的跨度处理器都会按照它们注册的顺序关闭,并释放所有持有的计算资源。")if err := tp.Shutdown(context.Background()); err != nil {panic(err)}}()if err != nil {log.Fatalf("connection failed,err:%s", err)}// 初始化客户端client := hello_grpc.NewHelloServiceClient(conn)result, err := client.SayHello(context.Background(), &hello_grpc.HelloRequest{Name:    "client",Message: "hello",})fmt.Println(result, err)}

gin-http

调用流程

api1 ----> api2 ----> grpc

api1
package mainimport ("context""fmt""github.com/gin-gonic/gin""go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/codes""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"oteltrace "go.opentelemetry.io/otel/trace""log""net/http"modahttp "github.com/webws/go-moda/transport/http"
)const (service     = "api1"environment = "dev"id          = 3
)var tracer = otel.Tracer(service)func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://10.128.175.196:14268/api/traces")))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Cleanly shutdown and flush telemetry when the application exits.defer func() {if err := tp.Shutdown(context.Background()); err != nil {log.Fatal(err)}}()r := gin.New()r.Use(otelgin.Middleware("gin-api1"))loadRoutes(r)fmt.Println("api1 start 8081")r.Run(":8081")
}func loadRoutes(r *gin.Engine) {r.GET("/ping", pingFunc)
}func pingFunc(c *gin.Context) {tracerCtx, span := tracer.Start(c.Request.Context(), "pingFunc",oteltrace.WithAttributes(attribute.String("key-pingFunc", "val-pingFunc")))defer span.End()callApi2(tracerCtx)c.JSON(http.StatusOK, gin.H{"message": "pong",})
}func callApi2(ctx context.Context) {// tracerfmt.Println("on callApi2")tracerCtx, span := tracer.Start(ctx, "callApi2")span.AddEvent("starting callApi2")span.SetAttributes(attribute.Key("key_callApi2").String("value_callApi2"))defer span.End()// 打印TraceID SpanIDspanCtx := oteltrace.SpanContextFromContext(ctx)fmt.Println(spanCtx.TraceID())fmt.Println(spanCtx.SpanID())// 调用api2url := fmt.Sprintf("http://localhost:8082/ping")defer span.End()_, err := modahttp.CallAPI(tracerCtx, url, "GET", nil)if err != nil {fmt.Printf("call api2 error: %v", err)span.SetStatus(codes.Error, err.Error())}
}
api2
package mainimport ("context""fmt""github.com/gin-gonic/gin""go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"oteltrace "go.opentelemetry.io/otel/trace""go_otel/3Gin-HTTP/grpc/grpc_proto/hello_grpc""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""net/http"
)const (service     = "api2"environment = "dev"id          = 2
)var tracer = otel.Tracer(service)func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://10.128.175.196:14268/api/traces")))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Cleanly shutdown and flush telemetry when the application exits.defer func() {if err := tp.Shutdown(context.Background()); err != nil {log.Fatal(err)}}()r := gin.New()r.Use(otelgin.Middleware("gin-api2"))loadRoutes(r)fmt.Println("api2 start 8082")r.Run(":8082")
}func loadRoutes(r *gin.Engine) {r.GET("/ping", pingFunc)
}func pingFunc(c *gin.Context) {fmt.Println("on pingFunc")ctx, span := tracer.Start(c.Request.Context(), "pingFunc")defer span.End()callGrpc(ctx)c.JSON(http.StatusOK, gin.H{"message": "pong",})
}func callGrpc(ctx context.Context) {fmt.Println("on callGrpc")ctx, span := tracer.Start(ctx, "callGrpc")defer span.End()// 打印TraceID SpanIDspanCtx := oteltrace.SpanContextFromContext(ctx)fmt.Println(spanCtx.TraceID())fmt.Println(spanCtx.SpanID())// 连接服务器options := make([]grpc.DialOption, 0)options = append(options, grpc.WithTransportCredentials(insecure.NewCredentials()),grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()))conn, err := grpc.DialContext(ctx, "localhost:8083", options...)if err != nil {panic(err)}defer conn.Close()// 初始化客户端client := hello_grpc.NewHelloServiceClient(conn)result, err := client.SayHello(ctx, &hello_grpc.HelloRequest{Name:    "client",Message: "hello",})fmt.Println(result, err)
}
grpc
package mainimport ("context""fmt""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"oteltrace "go.opentelemetry.io/otel/trace""go_otel/3Gin-HTTP/grpc/grpc_proto/hello_grpc""google.golang.org/grpc""google.golang.org/grpc/grpclog""log""net"
)const (service     = "remote-grpc"environment = "dev"id          = 3
)var tracer = otel.Tracer(service)// HelloServer 得有一个结构体,需要实现这个服务的全部方法,叫什么名字不重要
type HelloServer struct {
}func (HelloServer) SayHello(ctx context.Context, request *hello_grpc.HelloRequest) (*hello_grpc.HelloResponse, error) {fmt.Println("on grpc SayHello")_, span := tracer.Start(ctx, "SayHello",oteltrace.WithAttributes(attribute.String("key-pingFunc", "val-pingFunc")))defer span.End()spanCtx := oteltrace.SpanContextFromContext(ctx)fmt.Println(spanCtx.SpanID())fmt.Println(spanCtx.TraceID())fmt.Println("入参:", request.Name, request.Message)return &hello_grpc.HelloResponse{Name:    "server",Message: "hello " + request.Name,}, nil
}func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://10.128.175.196:14268/api/traces")))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Cleanly shutdown and flush telemetry when the application exits.defer func() {if err := tp.Shutdown(context.Background()); err != nil {log.Fatal(err)}}()// grpcfmt.Println("grpc start 8083")listen, err := net.Listen("tcp", ":8083")if err != nil {grpclog.Fatalf("Failed to listen: %v", err)}// 创建一个gRPC服务器实例。// 添加拦截器(grpc集成OpenTelemetry主要是调用(otelgrpc.UnaryServerInterceptor())s := grpc.NewServer(grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()))server := HelloServer{}// 将server结构体注册为gRPC服务。hello_grpc.RegisterHelloServiceServer(s, &server)// 开始处理客户端请求。err = s.Serve(listen)
}

4.完整代码

go_opentelemetry_study: Golang使用OpenTelemetry

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/2489.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

齐护K210系列教程(八)_LCD显示图片

LCD显示图片 文章目录 LCD显示图片1,显示单张图片2,通过按键切换显示SD卡内的图片3,通过传感器切换图片4,画中画显示,并缩放5,课程资源 联系我们 AIstart 显示的图片的默认分辨率为:320*240 &am…

使用ROC指标100次盈利交易后,众汇才明白的道理

使用ROC指标100次盈利交易后才明白的道理,众汇外汇认为盈利的基本就是考虑这些指标。 ①.资产波动性 需要考虑到资产波动性,根据资产的波动性更改设置,设置的结果会告诉投资者这段时间的平均波动率。 ②添加过滤器。交易系统的主要指标是趋…

MySQL无法打开情况下读取frm文件的表结构

一、背景: 开发人员通过MySQL客户端工具,可以访问MySQL5.7.6,可以访问具体的DB,可以查看小写表的数据,但是无法查看大写表的数据,报错信息为“table does not exist”。 二、检查与分析: ssh登录…

圈子交友系统话题设置-免费圈子社区论坛交友系统-圈子交友系统功能介绍-APP小程序H5多端源码交付!

1. 圈子的独特创造与精心管理 源码赋予用户创造独特圈子的能力,为志同道合的人们打造一个分享兴趣、交流见解的平台。每个圈子都可以个性化定制主题、标签和规则,以确保圈子具备个性特点和强烈的社群感。作为圈子的创建者,您将享有自由编辑资…

Ableton Live 12 Suite for Mac/Win:引领音乐制作新纪元的创意神器

在数字音乐制作领域,Ableton Live 12 Suite无疑是一颗璀璨的明星。这款强大的音乐制作工具,无论你是Mac用户还是Windows用户,都能为你带来前所未有的音乐创作体验。 Ableton Live 12 Suite的出色之处在于其全面而精细的功能设计。从音频录制…

计算机考研|25科软值得冲吗?会不会炸?

给大家看一串数字:3300 4300 1400 3300 3900 没错,这就是科软这几年的报考学生的实际情况,21年的时候,报考人数达到了峰值,有4300人,当年复试线388分真的可以说是炸穿地心。24年报考人数3900人&#xff0c…

485口还是网口?西门子PLC通讯方式到底怎么选?

西门子作为最早进入中国市场的工控厂家,其市场占有率一直非常高。 西门子PLC的品质非常好,其网络稳定性、开放性深受工控人员的喜爱,而且编程软件分类多,使用方便。在国内工控界具有显著地位。 虽然大家对西门子PLC都有一定的了…

酚类壳聚糖Dextran和F127-CHO胶束交联形成可注射水凝胶

酚类壳聚糖Dextran和F127-CHO胶束交联形成可注射水凝胶 形成水凝胶的过程 壳聚糖与酚类小分子的偶联:壳聚糖是一种天然的、具有多个氨基和羟基的多糖,这些功能基团使其能够与酚类小分子进行化学偶联。酚类小分子可以通过共价键(如酰胺键或醚…

vue3引入图片 无法使用require, vue3+vite构建项目使用require引入包出现问题需要用newURL来动态引入图片等静态资源

在vue3中 require引入图片的本地资源报错Uncaught (in promise) ReferenceError: require is not defined <template> <img :src"imageSrc" alt"My Image"> </template> <script> import imageSrc from /assets/image.png; export…

研究发现:提示中加入数百个示例显著提升大型语言模型的性能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

机器学习(XgBoost)预测顶和底

之前的文章中&#xff0c;我们对中证1000指数进行了顶和底的标注。这一篇我们将利用这份标注数据&#xff0c;实现机器学习预测顶和底&#xff0c;并探讨一些机器学习的原理。 我们选取的特征非常简单–上影线和WR&#xff08;William’s R&#xff09;的一个变种。选取这两个…

ElementUI RUOYI 深色适配

1. 切换按钮&#xff1a;随便找个页面放上去 页面触发逻辑如下 a. html 按钮结构&#xff08;可自定义&#xff09; <el-switchstyle"margin-top: 4px; margin-left: 8px; margin-right: 8px"v-model"isDark"inline-promptactive-icon"Moon"…

用阅读丈量世界 用标准突破边界——The Open Group“开放标准书单”推荐!

The Open Group 依托旗下多个论坛&#xff0c;不断推陈出新标准、指南、白皮书、案例分析等形式多样的发布物&#xff0c;为行业在数字能力赛道的良性高速发展提供先进的理念、方法、策略以及具体应用场景下的最佳实践。 正值第29个“世界读书日”&#xff0c;The Open Group精…

SQL Server指南:从入门到进阶实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Java面试必问题52:(高级面试篇)MongoDB的详细解释以及他的用法以及他跟Redis的区别以及优势

1. 非结构化数据存储&#xff1a;MongoDB是一个面向文档的NoSQL数据库&#xff0c;它不需要定义固定的数据模式&#xff0c;适用于存储不同结构和半结构化的数据&#xff0c;如日志、JSON文档、传感器数据等。 2. 可扩展性和高性能&#xff1a;MongoDB采用分布式架构&#xff0…

为什么堆排序的时间复杂度是O(N*logN)?

目录 前言&#xff1a; 堆排序&#xff08;以排升序为例&#xff09; 步骤&#xff08;用大根堆&#xff0c;倒这排&#xff0c;排升序&#xff09;&#xff1a; 1.先把要排列的数组建立成大根堆 2.堆顶元素&#xff08;82&#xff09;和最后一个元素交换&#xff08;2&…

HYBBS表白墙爆款源码!轻松搭建表白墙网站,更可一键封装成APP,让爱传递无界限

PHP表白墙网站源码&#xff0c;适用于校园内或校区间使用&#xff0c;同时支持封装成APP。告别使用QQ空间的表白墙。 简单安装&#xff0c;只需PHP版本5.6以上即可。 通过上传程序进行安装&#xff0c;并设置账号密码&#xff0c;登录后台后切换模板&#xff0c;适配手机和PC…

计算机网络——应用层协议(1)

在这篇文章初识网络中&#xff0c;我介绍了关于计算机网络的相关知识&#xff0c;以及在这两篇文章中Socket编程和Socket编程——tcp&#xff0c;介绍了使用套接字在两种协议下的网络间通信方式。本篇文章中我将会进一步介绍网络中网络协议的部分&#xff0c;而这将会从应用层开…

[SWPUCTF 2022 新生赛]ez_ez_unserialize

要绕过wakeup函数&#xff0c;只要序列化的中的成员数大于实际成员数&#xff0c;即可绕过。 <?php class X {public $x fllllllag.php; }$anew X(); echo serialize($a); O:1:“X”:1:{s:1:“x”;s:13:“fllllllag.php”;} 修改为 O:1:“X”:3:{s:1:“x”;s:13:“flllll…

【Java--数据结构】“从扑克到程序:深入探讨洗牌算法的原理与魅力“

前言 以下是学习Java顺序表的一个实例应用———简单的洗牌算法。 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 定义每张扑克牌的属性 生成一副扑克牌&#xff08;不包含大小王&#xff09; 洗牌方法 发牌方…