10/05/2021 - GO
This example demonstrates how you can attach OpenTelemetry tracing to outgoing gRPC client request and server response. As this is a follow up to the previous example linked below I am cutting it short.
Add this file to the previous post - Implementing OpenTelemetry and Jaeger tracing in Golang HTTP API. Also make sure to run go get -u go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
command as well.
// grpc.go
package trace
import (
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
)
// NewGRPUnaryClientInterceptor returns unary client interceptor. It is used
// with `grpc.WithUnaryInterceptor` method.
func NewGRPUnaryClientInterceptor() grpc.UnaryClientInterceptor {
return otelgrpc.UnaryClientInterceptor()
}
// NewGRPUnaryServerInterceptor returns unary server interceptor. It is used
// with `grpc.UnaryInterceptor` method.
func NewGRPUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return otelgrpc.UnaryServerInterceptor()
}
// NewGRPCStreamClientInterceptor returns stream client interceptor. It is used
// with `grpc.WithStreamInterceptor` method.
func NewGRPCStreamClientInterceptor() grpc.StreamClientInterceptor {
return otelgrpc.StreamClientInterceptor()
}
// NewGRPCStreamClientInterceptor returns stream server interceptor. It is used
// with `grpc.StreamInterceptor` method.
func NewGRPCStreamServerInterceptor() grpc.StreamServerInterceptor {
return otelgrpc.StreamServerInterceptor()
}
syntax = "proto3";
package bank;
option go_package = "github.com/you/{client|server}/pkg/protobuf/bank;bankpb";
service AccountService {
rpc Create(CreateRequest) returns (CreateResponse) {}
}
message CreateRequest {
string name = 1;
}
message CreateResponse {
bool success = 1;
}
package main
import (
"context"
"log"
"github.com/you/client/internal/bank"
"github.com/you/client/internal/pkg/trace"
"google.golang.org/grpc"
)
func main() {
ctx := context.Background()
// Bootstrap tracer.
prv, err := trace.NewProvider(trace.ProviderConfig{
JaegerEndpoint: "http://localhost:14268/api/traces",
ServiceName: "client",
ServiceVersion: "1.0.0",
Environment: "dev",
Disabled: false,
})
if err != nil {
log.Fatalln(err)
}
defer prv.Close(ctx)
// Bootstrap gRPC client.
conn, err := grpc.Dial(":50051", grpc.WithInsecure(),
grpc.WithUnaryInterceptor(trace.NewGRPUnaryClientInterceptor()),
)
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
// Bootstrap bank gRPC client and issue requests.
bankAccServie := bank.NewAccountService(conn)
if err := bankAccServie.Create(context.Background()); err != nil {
log.Println(err)
}
}
package bank
import (
"context"
"fmt"
"google.golang.org/grpc"
bankpb "github.com/you/client/pkg/protobuf/bank"
)
type AccountService struct {
client bankpb.AccountServiceClient
}
func NewAccountService(conn grpc.ClientConnInterface) AccountService {
return AccountService{
client: bankpb.NewAccountServiceClient(conn),
}
}
func (a AccountService) Create(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
res, err := a.client.Create(ctx, &bankpb.CreateRequest{Name: "Joe"})
if err != nil {
return err
}
fmt.Println(res)
return nil
}
package main
import (
"context"
"log"
"net"
"github.com/you/server/internal/bank"
"github.com/you/server/internal/pkg/trace"
"google.golang.org/grpc"
bankpb "github.com/you/server/pkg/protobuf/bank"
)
func main() {
ctx := context.Background()
// Bootstrap tracer.
prv, err := trace.NewProvider(trace.ProviderConfig{
JaegerEndpoint: "http://localhost:14268/api/traces",
ServiceName: "server",
ServiceVersion: "2.0.0",
Environment: "dev",
Disabled: false,
})
if err != nil {
log.Fatalln(err)
}
defer prv.Close(ctx)
// Bootstrap listener.
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalln(err)
}
defer listener.Close()
// Bootstrap gRPC server.
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(trace.NewGRPUnaryServerInterceptor()),
)
// Bootstrap bank gRPC service server and respond to requests.
bankAccService := bank.AccountService{}
bankpb.RegisterAccountServiceServer(grpcServer, bankAccService)
log.Fatalln(grpcServer.Serve(listener))
}
package bank
import (
"context"
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
bankpb "github.com/you/server/pkg/protobuf/bank"
)
type AccountService struct{}
func (AccountService) Create(ctx context.Context, req *bankpb.CreateRequest) (*bankpb.CreateResponse, error) {
if req.GetName() != "Joe" {
return nil, status.Errorf(codes.InvalidArgument, "create account: unexpected name: %s", req.GetName())
}
fmt.Println(req)
return &bankpb.CreateResponse{Success: true}, nil
}