Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Built-in instrumentation modules does not mention asynchronous tracing using apm-agent-go. #1551

Closed
shubhamsharma7867 opened this issue Dec 14, 2023 · 3 comments
Labels

Comments

@shubhamsharma7867
Copy link
Contributor

Hey,
I noticed that the documentation https://www.elastic.co/guide/en/apm/guide/8.11/apm-distributed-tracing.html - highlights distributed tracing but no information on asynchronous tracing is covered. By asynchronous tracing, I mean; when spinning a routine with a context opposed to the context received from the server, and priority is given to the latter (server's context) there is a possibility of the overall context being cancelled before another service call from that spun routine. This process is carried out using built-in instrumentation module, apm-grpc.

Below is the server side code which is being called by a grpc simple client having a interceptor apmgrpc.NewUnaryClientInterceptor()
and ideally it show me a span
Screenshot from 2023-12-15 01-36-13

`
type Server struct {
pb.AddServiceServer
clientConn pb.AddServiceClient
}

func main() {
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(lis)
}
log.Print("listening at port", addr)
cc, err := grpc.Dial("localhost:8080", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(apmgrpc.NewUnaryClientInterceptor()))
if err != nil {
panic(err)
}
client := pb.NewAddServiceClient(cc)
server := Server{
clientConn: client,
}

s := grpc.NewServer(grpc.UnaryInterceptor(apmgrpc.NewUnaryServerInterceptor()))
pb.RegisterAddServiceServer(s, &server)

if s.Serve(lis); err != nil {
	log.Fatal("problem in starting the server", lis)
}

}

func (s *Server) Add(ctx context.Context, in *pb.NumbersRequest) (*pb.NumberResponse, error) {
var tx *apm.Transaction
if md, ok := metadata.FromIncomingContext(ctx); ok {
tx = apm.TransactionFromContext(ctx)
for _, header := range []string{"elastic-apm-traceparent", "traceparent", "tracestate"} {
if values := md.Get(header); len(values) > 0 {
fmt.Println("inside for")
tx.Context.SetCustom(header, strings.Join(values, " "))
}
}
}
span, ctx := apm.StartSpan(ctx, "server_span", "type")
if tracestate := span.TraceContext().State.String(); tracestate != "" {
span.Name = tracestate
}
var res *pb.NumberResponse
time.Sleep(time.Millisecond * 50)
go func() {
res, _ = s.clientConn.Add(context.Background(), &pb.NumbersRequest{Num_1: 1, Num_2: 2})
fmt.Printf("res: %v\n", res)
}()
return &pb.NumberResponse{
Result: 9,
}, nil
}
`

@axw
Copy link
Member

axw commented Dec 18, 2023

This kind of pattern should ideally be covered by #385.

There is a function for propagating trace context without the cancellation/deadline, which may be useful here: https://pkg.go.dev/go.elastic.co/apm/v2#DetachedContext

@axw
Copy link
Member

axw commented Dec 18, 2023

Closing since we have the other docs issue. Please comment if I misunderstood something.

@axw axw closed this as not planned Won't fix, can't repro, duplicate, stale Dec 18, 2023
@shubhamsharma7867
Copy link
Contributor Author

Hey! I got it. using the apm package functions

`
type Server struct {
pb.AddServiceServer
clientConn1 pb.AddServiceClient
}

func main() {
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(lis)
}
log.Print("listening at port", addr)
cc, err := grpc.Dial("localhost:8080", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(apmgrpc.NewUnaryClientInterceptor()))
if err != nil {
panic(err)
}
// creating the client to call the different server
client := pb.NewAddServiceClient(cc)
server := Server{
clientConn1: client,
}
s := grpc.NewServer(grpc.UnaryInterceptor(apmgrpc.NewUnaryServerInterceptor()))
pb.RegisterAddServiceServer(s, &server)

if s.Serve(lis); err != nil {
	log.Fatal("problem in starting the server", lis)
}

}

func (s *Server) Add(ctx context.Context, in *pb.NumbersRequest) (*pb.NumberResponse, error) {
trx := apm.TransactionFromContext(ctx)
if trx == nil {
fmt.Print("it is not nil")
}
//checking for the header in the metadata
var tx *apm.Transaction
if md, ok := metadata.FromIncomingContext(ctx); ok {
tx = apm.TransactionFromContext(ctx)
for _, header := range []string{"elastic-apm-traceparent", "traceparent", "tracestate"} {
if values := md.Get(header); len(values) > 0 {
fmt.Println("inside for")
tx.Context.SetCustom(header, strings.Join(values, " "))
}
}
}

span, ctx := apm.StartSpan(ctx, "server_span", "type")
if tracestate := span.TraceContext().State.String(); tracestate != "" {
	span.Name = tracestate
}
// Spawning the new go-routine with value of the old context
go func() {
	span := apm.SpanFromContext(ctx)
	fmt.Printf("span: %+v\n", span)
	ctx = apm.ContextWithSpan(context.Background(), span)
	ctx := apm.ContextWithTransaction(context.Background(), trx)
	s.clientConn1.Add(ctx, &pb.NumbersRequest{Num_1: 1, Num_2: 2})

}()
return &pb.NumberResponse{
	Result: 9,
}, nil

}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants