{"id":340861,"date":"2022-11-07T09:00:32","date_gmt":"2022-11-07T09:00:32","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=340861"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=340861","title":{"rendered":"<span>golang, GRPC, Jaeger \u0438 distributed tracing<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0421\u0440\u0430\u0437\u0443 \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0446\u0435\u043b\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f\u0445, \u0430 \u0441\u043a\u043e\u0440\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e how to \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u044d\u0442\u043e \u0432\u0441\u0435 \u0443 \u0441\u0435\u0431\u044f \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0441\u0435\u043b \u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442 &#8212; \u043a\u043e\u0433\u0434\u0430 \u043c\u043d\u0435 \u0431\u044b\u043b\u043e \u043d\u0443\u0436\u043d\u043e \u044f \u043d\u0435 \u0441\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u043f\u043b\u043e\u0445\u043e \u0438\u0441\u043a\u0430\u043b). \u0422\u0430\u043a \u0436\u0435 \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0433\u0430\u0439\u0434\u0430\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Jaeger \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0441\u0442\u0430\u0440\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043e\u0442 uber, \u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0443\u0436\u0435 \u0441\u043e\u0432\u0435\u0442\u0443\u044e\u0442 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0435\u0435 \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 opentelementry \u043a\u043b\u0438\u0435\u043d\u0442\u0430. \u0415\u0449\u0435 \u044f \u0442\u043e\u043b\u043a\u043e\u043c \u043d\u0435 c\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043a\u0430\u043a \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c trace-id \u0447\u0435\u0440\u0435\u0437 context \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e \u043e\u043f\u0438\u0448\u0443 \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c grpc \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c grpc \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439, \u043a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c tracing \u0438 \u0432 \u043a\u043e\u043d\u0446\u0435 \u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e\u0431\u044b tracing \u0431\u044b\u043b \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443.<\/p>\n<hr\/>\n<h2>GRPC \u0441\u0435\u0440\u0432\u0435\u0440<\/h2>\n<h3>\u041e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c proto \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u043f\u0443\u0442\u0438 pkg\/server\/server.proto<\/h3>\n<pre><code>syntax = \"proto3\"; package server; option go_package = \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\";  service Server {     rpc Test(TestRequest) returns (TestResponse)     {     } }  message TestRequest { } message TestResponse {     string message = 1; }<\/code><\/pre>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u0434\u043b\u044f golang, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043b\u0435\u0436\u0430\u043b\u0438 \u0440\u044f\u0434\u043e\u043c \u0441 proto \u0444\u0430\u0439\u043b\u043e\u043c<\/p>\n<pre><code>        protoc -I.\/pkg\/server \\                 --go_out .\/pkg\/server \\                 --go_opt=paths=source_relative \\                 --go-grpc_out .\/pkg\/server \\                 --go-grpc_opt=paths=source_relative \\                 .\/pkg\/server\/*.proto<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0447\u043d\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0442\u043a\u0440\u043e\u0435\u043c TCP \u0441\u043e\u043a\u0435\u0442 \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 9000<\/p>\n<pre><code class=\"go\">lis, err := net.Listen(\"tcp\", \":9000\") if err != nil { log.Fatal(err) }<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c grpc \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<pre><code class=\"go\">grpcServer := grpc.NewServer()<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u0434\u043e\u0431\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c reflect<\/p>\n<pre><code class=\"go\">reflection.Register(grpcServer)<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432 proto \u0444\u0430\u0439\u043b\u0435<\/p>\n<pre><code class=\"go\">type ServerServeObject struct { pb.UnimplementedServerServer }  func (s *ServerServeObject) Test(_ context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { return &amp;pb.TestResponse{ Message: \"Server response\", }, nil } <\/code><\/pre>\n<p>\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0432 grpc \u0441\u0435\u0440\u0432\u0435\u0440\u0435<\/p>\n<pre><code class=\"go\">pb.RegisterServerServer(grpcServer, &amp;ServerServeObject{})<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u043a\u0435\u0442\u0430<\/p>\n<pre><code class=\"go\">err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) }<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\" )  const ( grpcPort = \":9000\" )  func main() { \/\/ Create a TCP connection lis, err := net.Listen(\"tcp\", grpcPort) if err != nil { log.Fatal(err) }  \/\/ Create the GRPC server grpcServer := grpc.NewServer()  \/\/ Allows us to use a 'list' call to list all available APIs reflection.Register(grpcServer)  \/\/ We register an object that should implement all the described APIs pb.RegisterServerServer(grpcServer, &amp;ServerServeObject{})  \/\/ Serving the GRPC server on a created TCP socket log.Println(\"GRPC server listening on \" + grpcPort) err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) } }  \/\/ ServerServeObject Describe the structure that should implement the interface described in the proto file type ServerServeObject struct { pb.UnimplementedServerServer }  \/\/ Test Implement a only endpoint func (s *ServerServeObject) Test(_ context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { return &amp;pb.TestResponse{ Message: \"Server response\", }, nil }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043e\u0442\u0432\u0435\u0442<\/p>\n<pre><code>grpcurl -plaintext localhost:9000 server.Server.Test {   \"message\": \"Server response\" }<\/code><\/pre>\n<h2>GRPC \u043a\u043b\u0438\u0435\u043d\u0442<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u043d\u0435 \u043e\u0434\u0438\u043d \u0441\u0435\u0440\u0432\u0438\u0441, \u0430 \u043e\u043d\u0438 \u043e\u0431\u0449\u0430\u043b\u0438\u0441\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d. \u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442.<\/p>\n<p>\u0422\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 proto \u0444\u0430\u0439\u043b pkg\/client\/client.proto<\/p>\n<pre><code>syntax = \"proto3\"; package client; option go_package = \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/client\";  service Client {     rpc Test(TestRequest) returns (TestResponse)     {     } }  message TestRequest { } message TestResponse {     string message = 1; }<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441 Server \u0432 \u0438\u043c\u0435\u043d\u0438 \u043d\u0430 Client, \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0432 API, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u043b \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<pre><code class=\"go\">func (s *ClientServeObject) Test(ctx context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { conn, err := grpc.DialContext(ctx, \"localhost:9000\", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } serv := server.NewServerClient(conn)   resp, err := serv.Test(ctx, &amp;server.TestRequest{}) if err != nil { return nil, err } return &amp;pb.TestResponse{ Message: resp.GetMessage(), }, nil }<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/client\" )  const ( grpcPort = \":9001\" )  func main() { \/\/ Create a TCP connection lis, err := net.Listen(\"tcp\", grpcPort) if err != nil { log.Fatal(err) }  \/\/ Create the GRPC server grpcServer := grpc.NewServer()  \/\/ Allows us to use a 'list' call to list all available APIs reflection.Register(grpcServer)  \/\/ We register an object that should implement all the described APIs pb.RegisterClientServer(grpcServer, &amp;ClientServeObject{})  \/\/ Serving the GRPC server on a created TCP socket log.Println(\"GRPC server listening on \" + grpcPort) err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) } }  \/\/ ClientServeObject Describe the structure that should implement the interface described in the proto file type ClientServeObject struct { pb.UnimplementedClientServer }  \/\/ Test Implement a only endpoint func (s *ClientServeObject) Test(ctx context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { \/\/ Create a connection to the server conn, err := grpc.DialContext(ctx, \"localhost:9000\", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err }  \/\/ Create a client object with connection serv := server.NewServerClient(conn)  \/\/ Calling a method on the server side resp, err := serv.Test(ctx, &amp;server.TestRequest{}) if err != nil { return nil, err }  \/\/ Forwarding the response from the server to the client return &amp;pb.TestResponse{ Message: resp.GetMessage(), }, nil }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c<\/p>\n<pre><code>grpcurl -plaintext localhost:9001 client.Client.Test {   \"message\": \"Server response\" }<\/code><\/pre>\n<hr\/>\n<h2>GRPC header<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0432 grpc \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u043a\u0430\u043a \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0432\u044b\u0437\u043e\u0432, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0444\u043b\u0430\u0433\u0430 -H<\/p>\n<pre><code>grpcurl -plaintext -H 'username: habr' localhost:9001 client.Client.Test<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<pre><code class=\"go\">md, ok := metadata.FromIncomingContext(ctx) if ok { \/\/ \u0423\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0432\u044b\u0437\u043e\u0432\u0430 \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 }<\/code><\/pre>\n<p>md &#8212; \u044d\u0442\u043e map \u0441\u043b\u0430\u0439\u0441\u043e\u0432. \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u0435\u0441\u0442\u044c<\/p>\n<pre><code class=\"go\">var username string md, ok := metadata.FromIncomingContext(ctx) if ok {       val := md[\"username\"] if len(val) > 0 { username = val[0] } }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<pre><code class=\"go\">ctx = metadata.AppendToOutgoingContext(ctx, \"username\", username)<\/code><\/pre>\n<p>\u0410 \u0434\u0430\u043b\u044c\u0448\u0435 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 ctx \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0438 \u043e\u043d \u0441\u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<hr\/>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a Jaeger<\/h2>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c jaeger \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 docker \u043e\u0431\u0440\u0430\u0437<\/p>\n<pre><code>docker run --rm -d \\ -p 16686:16686 \\ -p 14268:14268 --name jaeger jaegertracing\/all-in-one<\/code><\/pre>\n<p>\u041d\u0430 \u043f\u043e\u0440\u0442\u0443 16686 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 web \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0430 \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 14268 \u0441\u0438\u0434\u0438\u0442 collector, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445.<\/p>\n<h2>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u043a Jaeger<\/h2>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 jaeger \u0432 main.go \u0444\u0430\u0439\u043b\u044b. \u041d\u0430 \u0432\u0445\u043e\u0434 \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 http \u0430\u0434\u0440\u0435\u0441 \u0434\u043e \u043a\u043e\u043b\u043b\u0435\u043a\u0442\u043e\u0440\u0430 \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<pre><code class=\"go\">var ( \/\/ Store a global trace provider variable to clear it before closing tracer *tracesdk.TracerProvider )  func NewTracer(url, name string) error { \/\/ Create the Jaeger exporter exp, err := jaegerExporter.New(jaegerExporter.WithCollectorEndpoint(jaegerExporter.WithEndpoint(url))) if err != nil { return err } tracer = 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.ServiceNameKey.String(name), )), ) return nil }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0440\u0435\u0439\u0441\u0435\u0440\u0430 \u0432 server \u0438 client \u0432 \u043d\u0430\u0447\u0430\u043b\u043e main() \u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/p>\n<pre><code class=\"go\">\/\/ server     err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"server\") if err != nil { log.Fatal(err) } defer tracer.Shutdown(context.Background())  \/\/ client err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"client\") if err != nil { log.Fatal(err) } defer tracer.Shutdown(context.Background())<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 API \u043c\u0435\u0442\u043e\u0434\u0430<\/p>\n<pre><code class=\"go\">ctx, span := tracer.Tracer(\"server\").Start(ctx, \"Test\") defer span.End()<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/metadata\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\"  jaegerExporter \"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.12.0\" )  const ( grpcPort = \":9000\" )  var ( \/\/ Store a global trace provider variable to clear it before closing tracer *tracesdk.TracerProvider )  func NewTracer(url, name string) error { \/\/ Create the Jaeger exporter exp, err := jaegerExporter.New(jaegerExporter.WithCollectorEndpoint(jaegerExporter.WithEndpoint(url))) if err != nil { return err } tracer = 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.ServiceNameKey.String(name), )), ) return nil }  func main() { err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"server\") if err != nil { log.Fatal(err) } defer tracer.Shutdown(context.Background())  \/\/ Create a TCP connection lis, err := net.Listen(\"tcp\", grpcPort) if err != nil { log.Fatal(err) }  \/\/ Create the GRPC server grpcServer := grpc.NewServer()  \/\/ Allows us to use a 'list' call to list all available APIs reflection.Register(grpcServer)  \/\/ We register an object that should implement all the described APIs pb.RegisterServerServer(grpcServer, &amp;ServerServeObject{})  \/\/ Serving the GRPC server on a created TCP socket log.Println(\"GRPC server listening on \" + grpcPort) err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) } }  \/\/ ServerServeObject Describe the structure that should implement the interface described in the proto file type ServerServeObject struct { pb.UnimplementedServerServer }  \/\/ Test Implement a only endpoint func (s *ServerServeObject) Test(ctx context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { ctx, span := tracer.Tracer(\"server\").Start(ctx, \"Test\") defer span.End()  return &amp;pb.TestResponse{ Message: \"Server response: \" + username, }, nil }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>Distributed tracing<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u043a\u0440\u043e\u043c\u0435 \u043e\u0434\u043d\u043e\u0433\u043e. \u0421\u0435\u0439\u0447\u0430\u0441, \u0435\u0441\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Jaeger, \u0442\u043e \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432\u0441\u044e \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043e\u0442 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e. \u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 span, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f TraceID \u0432\u044b\u0448\u0435\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u043b \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435.<\/p>\n<p>1 \u0448\u0430\u0433. \u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f span, \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043c TraceID \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 x-trace-id<\/p>\n<pre><code class=\"go\">traceId := fmt.Sprintf(\"%s\", span.SpanContext().TraceID()) ctx = metadata.AppendToOutgoingContext(ctx, \"x-trace-id\", traceId)<\/code><\/pre>\n<p>2 \u0448\u0430\u0433. \u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0438 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 span \u0432\u043d\u0443\u0442\u0440\u044c ctx, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e span \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 TraceID<\/p>\n<pre><code class=\"go\">\/\/ Extract TraceID from header md, _ := metadata.FromIncomingContext(ctx) traceIdString := md[\"x-trace-id\"][0] \/\/ Convert string to byte array traceId, err := trace.TraceIDFromHex(traceIdString) if err != nil { return nil, err } \/\/ Creating a span context with a predefined trace-id spanContext := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceId, }) \/\/ Embedding span config into the context ctx = trace.ContextWithSpanContext(ctx, spanContext)  ctx, span := tracer.Tracer(\"server\").Start(ctx, \"Test\") defer span.End()<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e! \u0422\u0435\u043f\u0435\u0440\u044c, \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0442\u043e \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u0432\u044b\u0437\u043e\u0432\u043e\u0432. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u0435\u0431 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 <a href=\"http:\/\/localhost:16686\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:16686<\/a><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b6c\/24c\/92a\/b6c24c92a1d238717a5c1630b6d8a17f.png\" width=\"1420\" height=\"129\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b6c\/24c\/92a\/b6c24c92a1d238717a5c1630b6d8a17f.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ecd\/1b9\/142\/ecd1b91420bac2f82f8f8c6a83b9670f.png\" width=\"1902\" height=\"737\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ecd\/1b9\/142\/ecd1b91420bac2f82f8f8c6a83b9670f.png\"\/><figcaption><\/figcaption><\/figure>\n<hr\/>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0442\u0443\u0442: <a href=\"https:\/\/github.com\/HardDie\/grpc_with_tracing_example\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/HardDie\/grpc_with_tracing_example<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/697496\/\"> https:\/\/habr.com\/ru\/post\/697496\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0421\u0440\u0430\u0437\u0443 \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0446\u0435\u043b\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f\u0445, \u0430 \u0441\u043a\u043e\u0440\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e how to \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u044d\u0442\u043e \u0432\u0441\u0435 \u0443 \u0441\u0435\u0431\u044f \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0441\u0435\u043b \u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442 &#8212; \u043a\u043e\u0433\u0434\u0430 \u043c\u043d\u0435 \u0431\u044b\u043b\u043e \u043d\u0443\u0436\u043d\u043e \u044f \u043d\u0435 \u0441\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u043f\u043b\u043e\u0445\u043e \u0438\u0441\u043a\u0430\u043b). \u0422\u0430\u043a \u0436\u0435 \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0433\u0430\u0439\u0434\u0430\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Jaeger \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0441\u0442\u0430\u0440\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043e\u0442 uber, \u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0443\u0436\u0435 \u0441\u043e\u0432\u0435\u0442\u0443\u044e\u0442 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0435\u0435 \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 opentelementry \u043a\u043b\u0438\u0435\u043d\u0442\u0430. \u0415\u0449\u0435 \u044f \u0442\u043e\u043b\u043a\u043e\u043c \u043d\u0435 c\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043a\u0430\u043a \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c trace-id \u0447\u0435\u0440\u0435\u0437 context \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e \u043e\u043f\u0438\u0448\u0443 \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c grpc \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c grpc \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439, \u043a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c tracing \u0438 \u0432 \u043a\u043e\u043d\u0446\u0435 \u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e\u0431\u044b tracing \u0431\u044b\u043b \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443.<\/p>\n<hr\/>\n<h2>GRPC \u0441\u0435\u0440\u0432\u0435\u0440<\/h2>\n<h3>\u041e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c proto \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u043f\u0443\u0442\u0438 pkg\/server\/server.proto<\/h3>\n<pre><code>syntax = \"proto3\"; package server; option go_package = \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\";  service Server {     rpc Test(TestRequest) returns (TestResponse)     {     } }  message TestRequest { } message TestResponse {     string message = 1; }<\/code><\/pre>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u0434\u043b\u044f golang, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043b\u0435\u0436\u0430\u043b\u0438 \u0440\u044f\u0434\u043e\u043c \u0441 proto \u0444\u0430\u0439\u043b\u043e\u043c<\/p>\n<pre><code>        protoc -I.\/pkg\/server \\                 --go_out .\/pkg\/server \\                 --go_opt=paths=source_relative \\                 --go-grpc_out .\/pkg\/server \\                 --go-grpc_opt=paths=source_relative \\                 .\/pkg\/server\/*.proto<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0447\u043d\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0442\u043a\u0440\u043e\u0435\u043c TCP \u0441\u043e\u043a\u0435\u0442 \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 9000<\/p>\n<pre><code class=\"go\">lis, err := net.Listen(\"tcp\", \":9000\") if err != nil { log.Fatal(err) }<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c grpc \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<pre><code class=\"go\">grpcServer := grpc.NewServer()<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u0434\u043e\u0431\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c reflect<\/p>\n<pre><code class=\"go\">reflection.Register(grpcServer)<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432 proto \u0444\u0430\u0439\u043b\u0435<\/p>\n<pre><code class=\"go\">type ServerServeObject struct { pb.UnimplementedServerServer }  func (s *ServerServeObject) Test(_ context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { return &amp;pb.TestResponse{ Message: \"Server response\", }, nil } <\/code><\/pre>\n<p>\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0432 grpc \u0441\u0435\u0440\u0432\u0435\u0440\u0435<\/p>\n<pre><code class=\"go\">pb.RegisterServerServer(grpcServer, &amp;ServerServeObject{})<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u043a\u0435\u0442\u0430<\/p>\n<pre><code class=\"go\">err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) }<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\" )  const ( grpcPort = \":9000\" )  func main() { \/\/ Create a TCP connection lis, err := net.Listen(\"tcp\", grpcPort) if err != nil { log.Fatal(err) }  \/\/ Create the GRPC server grpcServer := grpc.NewServer()  \/\/ Allows us to use a 'list' call to list all available APIs reflection.Register(grpcServer)  \/\/ We register an object that should implement all the described APIs pb.RegisterServerServer(grpcServer, &amp;ServerServeObject{})  \/\/ Serving the GRPC server on a created TCP socket log.Println(\"GRPC server listening on \" + grpcPort) err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) } }  \/\/ ServerServeObject Describe the structure that should implement the interface described in the proto file type ServerServeObject struct { pb.UnimplementedServerServer }  \/\/ Test Implement a only endpoint func (s *ServerServeObject) Test(_ context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { return &amp;pb.TestResponse{ Message: \"Server response\", }, nil }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043e\u0442\u0432\u0435\u0442<\/p>\n<pre><code>grpcurl -plaintext localhost:9000 server.Server.Test {   \"message\": \"Server response\" }<\/code><\/pre>\n<h2>GRPC \u043a\u043b\u0438\u0435\u043d\u0442<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u043d\u0435 \u043e\u0434\u0438\u043d \u0441\u0435\u0440\u0432\u0438\u0441, \u0430 \u043e\u043d\u0438 \u043e\u0431\u0449\u0430\u043b\u0438\u0441\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d. \u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442.<\/p>\n<p>\u0422\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 proto \u0444\u0430\u0439\u043b pkg\/client\/client.proto<\/p>\n<pre><code>syntax = \"proto3\"; package client; option go_package = \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/client\";  service Client {     rpc Test(TestRequest) returns (TestResponse)     {     } }  message TestRequest { } message TestResponse {     string message = 1; }<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441 Server \u0432 \u0438\u043c\u0435\u043d\u0438 \u043d\u0430 Client, \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0432 API, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u043b \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<pre><code class=\"go\">func (s *ClientServeObject) Test(ctx context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { conn, err := grpc.DialContext(ctx, \"localhost:9000\", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } serv := server.NewServerClient(conn)   resp, err := serv.Test(ctx, &amp;server.TestRequest{}) if err != nil { return nil, err } return &amp;pb.TestResponse{ Message: resp.GetMessage(), }, nil }<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/client\" )  const ( grpcPort = \":9001\" )  func main() { \/\/ Create a TCP connection lis, err := net.Listen(\"tcp\", grpcPort) if err != nil { log.Fatal(err) }  \/\/ Create the GRPC server grpcServer := grpc.NewServer()  \/\/ Allows us to use a 'list' call to list all available APIs reflection.Register(grpcServer)  \/\/ We register an object that should implement all the described APIs pb.RegisterClientServer(grpcServer, &amp;ClientServeObject{})  \/\/ Serving the GRPC server on a created TCP socket log.Println(\"GRPC server listening on \" + grpcPort) err = grpcServer.Serve(lis) if err != nil { log.Fatal(err) } }  \/\/ ClientServeObject Describe the structure that should implement the interface described in the proto file type ClientServeObject struct { pb.UnimplementedClientServer }  \/\/ Test Implement a only endpoint func (s *ClientServeObject) Test(ctx context.Context, _ *pb.TestRequest) (*pb.TestResponse, error) { \/\/ Create a connection to the server conn, err := grpc.DialContext(ctx, \"localhost:9000\", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err }  \/\/ Create a client object with connection serv := server.NewServerClient(conn)  \/\/ Calling a method on the server side resp, err := serv.Test(ctx, &amp;server.TestRequest{}) if err != nil { return nil, err }  \/\/ Forwarding the response from the server to the client return &amp;pb.TestResponse{ Message: resp.GetMessage(), }, nil }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c<\/p>\n<pre><code>grpcurl -plaintext localhost:9001 client.Client.Test {   \"message\": \"Server response\" }<\/code><\/pre>\n<hr\/>\n<h2>GRPC header<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0432 grpc \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u043a\u0430\u043a \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0432\u044b\u0437\u043e\u0432, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0444\u043b\u0430\u0433\u0430 -H<\/p>\n<pre><code>grpcurl -plaintext -H 'username: habr' localhost:9001 client.Client.Test<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<pre><code class=\"go\">md, ok := metadata.FromIncomingContext(ctx) if ok { \/\/ \u0423\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0432\u044b\u0437\u043e\u0432\u0430 \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 }<\/code><\/pre>\n<p>md &#8212; \u044d\u0442\u043e map \u0441\u043b\u0430\u0439\u0441\u043e\u0432. \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u0435\u0441\u0442\u044c<\/p>\n<pre><code class=\"go\">var username string md, ok := metadata.FromIncomingContext(ctx) if ok {       val := md[\"username\"] if len(val) > 0 { username = val[0] } }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<pre><code class=\"go\">ctx = metadata.AppendToOutgoingContext(ctx, \"username\", username)<\/code><\/pre>\n<p>\u0410 \u0434\u0430\u043b\u044c\u0448\u0435 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 ctx \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0438 \u043e\u043d \u0441\u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<hr\/>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a Jaeger<\/h2>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c jaeger \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 docker \u043e\u0431\u0440\u0430\u0437<\/p>\n<pre><code>docker run --rm -d \\ -p 16686:16686 \\ -p 14268:14268 --name jaeger jaegertracing\/all-in-one<\/code><\/pre>\n<p>\u041d\u0430 \u043f\u043e\u0440\u0442\u0443 16686 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 web \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0430 \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 14268 \u0441\u0438\u0434\u0438\u0442 collector, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445.<\/p>\n<h2>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u043a Jaeger<\/h2>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 jaeger \u0432 main.go \u0444\u0430\u0439\u043b\u044b. \u041d\u0430 \u0432\u0445\u043e\u0434 \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 http \u0430\u0434\u0440\u0435\u0441 \u0434\u043e \u043a\u043e\u043b\u043b\u0435\u043a\u0442\u043e\u0440\u0430 \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<pre><code class=\"go\">var ( \/\/ Store a global trace provider variable to clear it before closing tracer *tracesdk.TracerProvider )  func NewTracer(url, name string) error { \/\/ Create the Jaeger exporter exp, err := jaegerExporter.New(jaegerExporter.WithCollectorEndpoint(jaegerExporter.WithEndpoint(url))) if err != nil { return err } tracer = 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.ServiceNameKey.String(name), )), ) return nil }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0440\u0435\u0439\u0441\u0435\u0440\u0430 \u0432 server \u0438 client \u0432 \u043d\u0430\u0447\u0430\u043b\u043e main() \u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/p>\n<pre><code class=\"go\">\/\/ server     err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"server\") if err != nil { log.Fatal(err) } defer tracer.Shutdown(context.Background())  \/\/ client err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"client\") if err != nil { log.Fatal(err) } defer tracer.Shutdown(context.Background())<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 API \u043c\u0435\u0442\u043e\u0434\u0430<\/p>\n<pre><code class=\"go\">ctx, span := tracer.Tracer(\"server\").Start(ctx, \"Test\") defer span.End()<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package main  import ( \"context\" \"log\" \"net\"  \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/metadata\" \"google.golang.org\/grpc\/reflection\"  pb \"github.com\/HardDie\/grpc_with_tracing_example\/pkg\/server\"  jaegerExporter \"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.12.0\" )  const ( grpcPort = \":9000\" )  var ( \/\/ Store a global trace provider variable to clear it before closing tracer *tracesdk.TracerProvider )  func NewTracer(url, name string) error { \/\/ Create the Jaeger exporter exp, err := jaegerExporter.New(jaegerExporter.WithCollectorEndpoint(jaegerExporter.WithEndpoint(url))) if err != nil { return err } tracer = 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.ServiceNameKey.String(name), )), ) return nil }  func main() { err := NewTracer(\"http:\/\/localhost:14268\/api\/traces\", \"server\") if err<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-340861","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/340861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=340861"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/340861\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=340861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=340861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=340861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}