instaawsv2

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2024 License: MIT Imports: 23 Imported by: 0

README

Instana instrumentation of AWS SDK v2 for Go

GoDoc

This module contains the code for instrumenting AWS APIs which are based on the aws sdk v2 library for Go. The following services are currently instrumented:

[!IMPORTANT] From v0.3.0 onwards, this instrumentation module requires Go v1.19 as the minimum version.

Installation

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

Usage

To trace the AWS APIs, the user has to instrument the aws.Config object using the instaawsv2.Instrument function before starting to use the AWS service clients like s3, dynamodb etc.

Special cases

1. Instrumenting SQS consumers

An SQS client that uses instrumented aws.Config automatically creates entry spans for each incoming sqs.Message. To use this entry span context as a parent in your message handler use instaawsv2.SpanContextFromSQSMessage:

func handleMessage(ctx context.Context, msg *sqs.Message) {
	if parent, ok := instaawsv2.SpanContextFromSQSMessage(msg, tracer); ok {
		sp := tracer.StartSpan("handleMessage", opentracing.ChildOf(parent))
		defer sp.Finish()

		ctx = instana.ContextWithSpan(ctx, sp)
    }

    // ...
}
2. Instrumenting calls to AWS Lambda

When calls to AWS Lambda is instrumented using instaawsv2, the trace context is propagated inside a ClientContext.Custom field in the InvokeInput object. The reserved keys used for this are:

  1. x-instana-t
  2. x-instana-s
  3. x-instana-l

Hence, to avoid collisions, it is recommended to avoid these keys in your application code.

Limitations

  • Current instrumentation does not support asynchronous lambda invocation.
  • If the length of base64 encoded ClientContext will exceed 3582 bytes, tracing headers will be not propagated.

Documentation

Overview

Package instaawsv2 provides Instana instrumentation for the aws sdk v2 library.

Example (Lambda)
// (c) Copyright IBM Corp. 2023

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/lambda"
	"github.com/aws/aws-sdk-go-v2/service/lambda/types"
	instana "github.com/instana/go-sensor"
	"github.com/instana/go-sensor/instrumentation/instaawsv2"
)

func main() {
	mux := http.NewServeMux()

	tr := instana.InitCollector(instana.DefaultOptions())

	lambdaClient, err := getLambdaClientV2(tr)
	if err != nil {
		log.Fatal("Unable to create the lambda client. Check the config. Details: ", err.Error())
	}

	mux.HandleFunc("/testlambdainvoke",
		instana.TracingHandlerFunc(tr, "/testlambdainvoke",
			handleLambdaInvoke(lambdaClient)))

	log.Println("Starting service for testing lambda invocations ...")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

func getLambdaClientV2(tr instana.TracerLogger) (*lambda.Client, error) {
	var client *lambda.Client

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return nil, fmt.Errorf("error while loading aws config: %s", err.Error())
	}

	instaawsv2.Instrument(tr, &cfg)
	client = lambda.NewFromConfig(cfg)

	return client, nil
}

func handleLambdaInvoke(client *lambda.Client) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		// Provide lambda function name here
		functionName := ""

		_, err := client.Invoke(request.Context(), &lambda.InvokeInput{
			FunctionName:   &functionName,
			InvocationType: types.InvocationTypeRequestResponse,
			Payload:        []byte("{}"),
		})

		var errMsg string
		if err != nil {
			errMsg = fmt.Sprintf("Unable to invoke the lambda: %s. Error: %s",
				functionName, err.Error())
		} else {
			errMsg = fmt.Sprintf("Successfully invoked the lambda: %s", functionName)
		}
		_, err = writer.Write([]byte(errMsg))
		if err != nil {
			fmt.Println("Error while writing the response: ", err.Error())
			return
		}
	}
}
Output:

Example (S3)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"

	instana "github.com/instana/go-sensor"
	"github.com/instana/go-sensor/instrumentation/instaawsv2"
)

func main() {
	tr := instana.InitCollector(&instana.Options{
		Service: "s3-tracer",
	})

	s := tr.StartSpan("first-op")
	defer s.Finish()

	ctx := instana.ContextWithSpan(context.Background(), s)

	sdkConfig, err := config.LoadDefaultConfig(ctx)
	instaawsv2.Instrument(tr, &sdkConfig)

	if err != nil {
		fmt.Println("Couldn't load default configuration. Have you set up your AWS account?")
		fmt.Println(err)
		return
	}
	s3Client := s3.NewFromConfig(sdkConfig)

	count := 10
	fmt.Printf("Let's list up to %v buckets for your account.\n", count)

	var i int
	for i = 0; i < 10; i++ {

		result, err := s3Client.ListBuckets(ctx, &s3.ListBucketsInput{})
		if err != nil {
			fmt.Printf("Couldn't list buckets for your account. Here's why: %v\n", err)
			return
		}
		if len(result.Buckets) == 0 {
			fmt.Println("You don't have any buckets!")
		} else {
			if count > len(result.Buckets) {
				count = len(result.Buckets)
			}
			for _, bucket := range result.Buckets[:count] {
				fmt.Printf("\t%v\n", *bucket.Name)
			}
		}

		time.Sleep(1 * time.Minute)
	}

}
Output:

Example (Sns)
// (c) Copyright IBM Corp. 2023

package main

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

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/sns"
	instana "github.com/instana/go-sensor"
	"github.com/instana/go-sensor/instrumentation/instaawsv2"
)

var ()

func main() {
	mux := http.NewServeMux()

	tr := instana.InitCollector(instana.DefaultOptions())

	snsClient, err := getSNSClientV2(tr)
	if err != nil {
		log.Fatal("Unable to create the sns client. Check the config. Details: ", err.Error())
	}

	msg := "this is a test message for amazon sns at " + time.Now().String()
	mux.HandleFunc("/testsnsmessage",
		instana.TracingHandlerFunc(tr, "/testsnsmessage",
			handleSNSSendMessage(snsClient, msg)))

	log.Println("Starting service for testing sns messages ...")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

func getSNSClientV2(tr instana.TracerLogger) (*sns.Client, error) {
	var client *sns.Client

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return nil, fmt.Errorf("error while loading aws config: %s", err.Error())
	}

	instaawsv2.Instrument(tr, &cfg)

	client = sns.NewFromConfig(cfg)

	return client, nil
}

func handleSNSSendMessage(client *sns.Client, msg string) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		//Provide values based on the SNS topic
		topicArn := ""

		_, err := client.Publish(request.Context(), &sns.PublishInput{
			Message:  &msg,
			TopicArn: &topicArn,
		})

		var errMsg string
		if err != nil {
			errMsg = fmt.Sprintf("Unable to send message to the sns. Error: %s",
				err.Error())
		} else {
			errMsg = fmt.Sprintf("Successfully sent the message to the sns topic")
		}
		writer.Write([]byte(errMsg))
	}
}
Output:

Example (Sqs)
// (c) Copyright IBM Corp. 2023

package main

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

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/sqs"
	instana "github.com/instana/go-sensor"
	"github.com/instana/go-sensor/instrumentation/instaawsv2"
)

func main() {
	mux := http.NewServeMux()

	tr := instana.InitCollector(instana.DefaultOptions())

	sqsClient, err := getSQSClientV2(tr)
	if err != nil {
		log.Fatal("Unable to create the sqs client. Check the config. Details: ", err.Error())
	}

	msg := "this is a test message for amazon sqs at " + time.Now().String()
	mux.HandleFunc("/testsqsmessage",
		instana.TracingHandlerFunc(tr, "/testsqsmessage",
			handleSQSSendMessage(sqsClient, msg)))

	log.Println("Starting service v2 for testing sqs messages ...")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

func getSQSClientV2(tr instana.TracerLogger) (*sqs.Client, error) {
	var client *sqs.Client

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return nil, fmt.Errorf("error while loading aws config: %s", err.Error())
	}

	instaawsv2.Instrument(tr, &cfg)

	client = sqs.NewFromConfig(cfg)

	return client, nil
}

func handleSQSSendMessage(client *sqs.Client, msg string) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		// Provide SQS Queue url here
		queueUrl := ""

		_, err := client.SendMessage(request.Context(), &sqs.SendMessageInput{
			MessageBody:  &msg,
			QueueUrl:     &queueUrl,
			DelaySeconds: 0,
		})

		var errMsg string
		if err != nil {
			errMsg = fmt.Sprintf("Unable to send message to the sqs queue: %s. Error: %s",
				queueUrl, err.Error())
		} else {
			errMsg = fmt.Sprintf("Successfully sent the message to the queue: %s", queueUrl)
		}
		writer.Write([]byte(errMsg))
	}
}
Output:

Index

Examples

Constants

View Source
const Version = "0.9.0"

Version is the instrumentation module semantic version

Variables

This section is empty.

Functions

func Instrument

func Instrument(tr instana.TracerLogger, cfg *aws.Config)

Instrument adds instana instrumentation to the aws config object

func SpanContextFromSQSMessage

func SpanContextFromSQSMessage(msg sqsTypes.Message, tracer instana.TracerLogger) (opentracing.SpanContext, error)

SpanContextFromSQSMessage returns the trace context from an SQS message

Types

type AWSDynamoDBOperations

type AWSDynamoDBOperations struct{}

type AWSInvokeLambdaOperations

type AWSInvokeLambdaOperations struct{}

type AWSOperations

type AWSOperations interface {
	// contains filtered or unexported methods
}

type AWSS3Operations

type AWSS3Operations struct{}

type AWSSNSOperations

type AWSSNSOperations struct{}

type AWSSQSOperations

type AWSSQSOperations struct{}

Jump to

Keyboard shortcuts

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