instagrpc

package module
v1.29.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2024 License: MIT Imports: 10 Imported by: 1

README

Instana instrumentation for go-grpc library

This module contains instrumentation code for GRPC servers and clients that use google.golang.org/grpc library.

GoDoc

Installation

To add the module to your go.mod file, run the following command in your project directory:

$ go get github.com/instana/go-sensor/instrumentation/instagrpc

Note

From instagrpc v1.11.0, the minimum version of grpc package required has been changed to v1.55.0. For working with older versions of grpc, one can use instagrpc v1.10.0.

Usage

For detailed usage example see the documentation or example_test.go.

This instrumentation requires an instana.Sensor to initialize spans and handle the trace context propagation. You can create a new instance of Instana tracer using instana.NewSensor().

Instrumenting a server

To instrument your GRPC server instance include instagrpc.UnaryServerInterceptor() and instagrpc.StreamServerInterceptor() into the list of server options passed to grpc.NewServer(). These interceptors will use the provided instana.Sensor to handle the OpenTracing headers, start a new span for each incoming request and inject it into the handler:

// initialize a new tracer instance
sensor := instana.NewSensor("my-server")

// instrument the server
srv := grpc.NewServer(
	grpc.UnaryInterceptor(instagrpc.UnaryServerInterceptor(sensor)),
	grpc.StreamInterceptor(instagrpc.StreamServerInterceptor(sensor)),
	// ...
)

The parent span can be than retrieved inside the handler using instana.SpanFromContext():

func (s MyServer) SampleCall(ctx context.Context, req *MyRequest) (*MyResponse, error) {
	parentSpan, ok := instana.SpanFromContext(ctx)
	// ...
}
Instrumenting a client

Similar to the server instrumentation, to instrument a GRPC client add instagrpc.UnaryClientInterceptor() and instagrpc.StreamClientInterceptor() to the list of dial options passed to the grpc.Dial() call. The interceptor will inject the trace context into each outgoing request made with this connection:

conn, err := grpc.Dial(
	serverAddr,
	grpc.WithUnaryInterceptor(instagrpc.UnaryClientInterceptor(sensor)),
	grpc.WithStreamInterceptor(instagrpc.StreamClientInterceptor(sensor)),
	// ...
)

If the context contains an active span stored using instana.ContextWithSpan(), the tracer of this span will be used instead.

Documentation

Overview

Package instagrpc provides Instana tracing instrumentation for GRPC servers and clients that use google.golang.org/grpc.

Example
// (c) Copyright IBM Corp. 2021
// (c) Copyright Instana Inc. 2020

//go:build go1.19
// +build go1.19

package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"time"

	instana "github.com/instana/go-sensor"
	"github.com/instana/go-sensor/instrumentation/instagrpc"
	"github.com/opentracing/opentracing-go/ext"
	"google.golang.org/grpc"
	grpctest "google.golang.org/grpc/interop/grpc_testing"
)

// EchoServer is an implementation of GRPC server
type TestServiceServer struct {
	grpctest.UnimplementedTestServiceServer
}

// UnaryCall responds with a static greeting from server
func (s TestServiceServer) UnaryCall(ctx context.Context, req *grpctest.SimpleRequest) (*grpctest.SimpleResponse, error) {
	// Extract the parent span and use its tracer to initialize any child spans to trace the calls
	// inside the handler, e.g. database queries, 3rd-party API requests, etc.
	if parent, ok := instana.SpanFromContext(ctx); ok {
		sp := parent.Tracer().StartSpan("unary-call")
		defer sp.Finish()
	}

	time.Sleep(100 * time.Microsecond)

	return &grpctest.SimpleResponse{
		Payload: &grpctest.Payload{
			Body: []byte("hello from server"),
		},
	}, nil
}

// setupServer starts a new instrumented GRPC server instance and returns
// the server address
func setupServer() (net.Addr, error) {
	// Initialize server sensor to instrument request handlers
	sensor := instana.NewSensor("grpc-server")

	ln, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, fmt.Errorf("failed to start listener: %s", err)
	}

	// To instrument server calls add instagrpc.UnaryServerInterceptor(sensor) and
	// instagrpc.StreamServerInterceptor(sensor) to the list of server options when
	// initializing the server
	srv := grpc.NewServer(
		grpc.UnaryInterceptor(instagrpc.UnaryServerInterceptor(sensor)),
		grpc.StreamInterceptor(instagrpc.StreamServerInterceptor(sensor)),
	)

	grpctest.RegisterTestServiceServer(srv, &TestServiceServer{})
	go func() {
		if err := srv.Serve(ln); err != nil {
			log.Fatalf("failed to start server: %s", err)
		}
	}()

	return ln.Addr(), nil
}

func main() {
	serverAddr, err := setupServer()
	if err != nil {
		log.Fatalf("failed to setup a server: %s", err)
	}

	// Initialize client tracer
	sensor := instana.NewSensor("grpc-client")

	// To instrument client calls add instagrpc.UnaryClientInterceptor(sensor) and
	// instagrpc.StringClientInterceptor(sensor) to the DialOption list while dialing
	// the GRPC server.
	conn, err := grpc.Dial(
		serverAddr.String(),
		grpc.WithInsecure(),
		grpc.WithUnaryInterceptor(instagrpc.UnaryClientInterceptor(sensor)),
		grpc.WithStreamInterceptor(instagrpc.StreamClientInterceptor(sensor)),
	)
	if err != nil {
		log.Fatalf("failed to dial server on %s: %s", serverAddr.String(), err)
	}
	defer conn.Close()

	c := grpctest.NewTestServiceClient(conn)

	// The call should always start with an entry span (https://www.instana.com/docs/tracing/custom-best-practices/#start-new-traces-with-entry-spans)
	// Normally this would be your HTTP/GRPC/message queue request span, but here we need to
	// create it explicitly.
	sp := sensor.Tracer().StartSpan("client-call")
	sp.SetTag(string(ext.SpanKind), "entry")

	// Create a context that holds the parent entry span and pass it to the GRPC call
	resp, err := c.UnaryCall(
		instana.ContextWithSpan(context.Background(), sp),
		&grpctest.SimpleRequest{
			Payload: &grpctest.Payload{
				Body: []byte("hello from client"),
			},
		},
	)
	if err != nil {
		log.Fatalf("server responded with an error: %s", err)
	}
	fmt.Println(string(resp.GetPayload().GetBody()))
}
Output:

hello from server

Index

Examples

Constants

View Source
const Version = "1.29.0"

Version is the instrumentation module semantic version

Variables

This section is empty.

Functions

func StreamClientInterceptor

func StreamClientInterceptor(sensor instana.TracerLogger) grpc.StreamClientInterceptor

StreamClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls. It injects Instana OpenTracing headers into outgoing stream requests to ensure trace propagation throughout the call. The span is finished as soon as server closes the stream or returns an error. Any error occurred during the request is attached to the span logs.

func StreamServerInterceptor

func StreamServerInterceptor(sensor instana.TracerLogger) grpc.StreamServerInterceptor

StreamServerInterceptor returns a tracing interceptor to be used in grpc.NewServer() calls. This interceptor is responsible for extracting the Instana OpenTracing headers from incoming streaming requests and starting a new span that can later be accessed inside the handler:

if parent, ok := instana.SpanFromContext(srv.Context()); ok {
	sp := parent.Tracer().StartSpan("child-span")
	defer sp.Finish()
}

If the handler returns an error or panics, the error message is then attached to the span logs.

func UnaryClientInterceptor

func UnaryClientInterceptor(sensor instana.TracerLogger) grpc.UnaryClientInterceptor

UnaryClientInterceptor returns a tracing interceptor to be used in grpc.Dial() calls. It injects Instana OpenTracing headers into outgoing unary requests to ensure trace propagation throughout the call. If the server call results with an error, its message will be attached to the span logs.

func UnaryServerInterceptor

func UnaryServerInterceptor(sensor instana.TracerLogger) grpc.UnaryServerInterceptor

UnaryServerInterceptor returns a tracing interceptor to be used in grpc.NewServer() calls. This interceptor is responsible for extracting the Instana OpenTracing headers from incoming requests and staring a new span that can later be accessed inside the handler:

if parent, ok := instana.SpanFromContext(ctx); ok {
	sp := parent.Tracer().StartSpan("child-span")
	defer sp.Finish()
}

If the handler returns an error or panics, the error message is then attached to the span logs.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL