diff --git a/CHANGELOG.md b/CHANGELOG.md index bd817ab8e9f..023536806ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Generate server metrics with semantic conventions v1.26 in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` when `OTEL_SEMCONV_STABILITY_OPT_IN` is set to `http/dup`. (#6411) +- Added new `AttributeBuilder` interface to support adding attributes based on SDK input and output in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws`. (#6543) + +### Changed + +- Deprecated the `AttributeSetter` interface, in favor of `AttributeSetter` in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` (#6543) +- Added custom attribute to the span after execution of the SDK rather than before in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws`. (#6543) + ## [1.33.0/0.58.0/0.27.0/0.13.0/0.8.0/0.6.0/0.5.0] - 2024-12-12 diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go index daf8c5f7cfe..8c2ef782146 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go @@ -23,10 +23,10 @@ const ( AWSSystemVal string = "aws-api" ) -var servicemap = map[string]AttributeSetter{ - dynamodb.ServiceID: DynamoDBAttributeSetter, - sqs.ServiceID: SQSAttributeSetter, - sns.ServiceID: SNSAttributeSetter, +var servicemap = map[string]AttributeBuilder{ + dynamodb.ServiceID: DynamoDBAttributeBuilder, + sqs.ServiceID: SQSAttributeBuilder, + sns.ServiceID: SNSAttributeBuilder, } // SystemAttr return the AWS RPC system attribute. @@ -56,11 +56,18 @@ func RequestIDAttr(requestID string) attribute.KeyValue { // DefaultAttributeSetter checks to see if there are service specific attributes available to set for the AWS service. // If there are service specific attributes available then they will be included. +// Deprecated: Kept for backward compatibility, use DefaultAttributeBuilder instead. This will be removed in a future release. func DefaultAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue { + return DefaultAttributeBuilder(ctx, in, middleware.InitializeOutput{}) +} + +// DefaultAttributeBuilder checks to see if there are service specific attributes available to set for the AWS service. +// If there are service specific attributes available then they will be included. +func DefaultAttributeBuilder(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue { serviceID := v2Middleware.GetServiceID(ctx) if fn, ok := servicemap[serviceID]; ok { - return fn(ctx, in) + return fn(ctx, in, out) } return []attribute.KeyValue{} diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes_test.go index e7c1c6d234e..6db57937fec 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes_test.go @@ -4,8 +4,12 @@ package otelaws import ( + "context" "testing" + awsMiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/sqs" + "github.com/aws/smithy-go/middleware" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" @@ -40,3 +44,29 @@ func TestSystemAttribute(t *testing.T) { attr := SystemAttr() assert.Equal(t, semconv.RPCSystemKey.String("aws-api"), attr) } + +func TestDefaultAttributeBuilder_ShouldReturnNoAttributesOnNotSupportedService(t *testing.T) { + // GIVEN + testCtx := awsMiddleware.SetServiceID(context.TODO(), "not-implemented-service") + + // WHEN + attr := DefaultAttributeBuilder(testCtx, middleware.InitializeInput{}, middleware.InitializeOutput{}) + assert.Empty(t, attr) +} + +func TestDefaultAttributeBuilder_ShouldReturnAttributesOnSupportedService(t *testing.T) { + // GIVEN + testCtx := awsMiddleware.SetServiceID(context.TODO(), sqs.ServiceID) + testQueueURL := "test-queue-url" + + // WHEN + attr := DefaultAttributeBuilder(testCtx, middleware.InitializeInput{ + Parameters: &sqs.SendMessageInput{ + QueueUrl: &testQueueURL, + }, + }, middleware.InitializeOutput{}) + assert.ElementsMatch(t, []attribute.KeyValue{ + semconv.MessagingSystem("AmazonSQS"), + semconv.NetPeerName(testQueueURL), + }, attr) +} diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go index 42ea8ecab10..596c267694a 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go @@ -27,12 +27,16 @@ const ( type spanTimestampKey struct{} // AttributeSetter returns an array of KeyValue pairs, it can be used to set custom attributes. +// Deprecated: Kept for backward compatibility, use AttributeBuilder instead. This will be removed in a future release. type AttributeSetter func(context.Context, middleware.InitializeInput) []attribute.KeyValue +// AttributeBuilder returns an array of KeyValue pairs, it can be used to set custom attributes. +type AttributeBuilder func(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue + type otelMiddlewares struct { - tracer trace.Tracer - propagator propagation.TextMapPropagator - attributeSetter []AttributeSetter + tracer trace.Tracer + propagator propagation.TextMapPropagator + attributeBuilders []AttributeBuilder } func (m otelMiddlewares) initializeMiddlewareBefore(stack *middleware.Stack) error { @@ -61,9 +65,6 @@ func (m otelMiddlewares) initializeMiddlewareAfter(stack *middleware.Stack) erro RegionAttr(region), OperationAttr(operation), } - for _, setter := range m.attributeSetter { - attributes = append(attributes, setter(ctx, in)...) - } ctx, span := m.tracer.Start(ctx, spanName(serviceID, operation), trace.WithTimestamp(ctx.Value(spanTimestampKey{}).(time.Time)), @@ -73,6 +74,7 @@ func (m otelMiddlewares) initializeMiddlewareAfter(stack *middleware.Stack) erro defer span.End() out, metadata, err = next.HandleInitialize(ctx, in) + span.SetAttributes(m.buildAttributes(ctx, in, out)...) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -125,6 +127,14 @@ func (m otelMiddlewares) deserializeMiddleware(stack *middleware.Stack) error { middleware.Before) } +func (m otelMiddlewares) buildAttributes(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) (attributes []attribute.KeyValue) { + for _, builder := range m.attributeBuilders { + attributes = append(attributes, builder(ctx, in, out)...) + } + + return attributes +} + func spanName(serviceID, operation string) string { spanName := serviceID if operation != "" { @@ -145,15 +155,15 @@ func AppendMiddlewares(apiOptions *[]func(*middleware.Stack) error, opts ...Opti opt.apply(&cfg) } - if cfg.AttributeSetter == nil { - cfg.AttributeSetter = []AttributeSetter{DefaultAttributeSetter} + if cfg.AttributeBuilders == nil { + cfg.AttributeBuilders = []AttributeBuilder{DefaultAttributeBuilder} } m := otelMiddlewares{ tracer: cfg.TracerProvider.Tracer(ScopeName, trace.WithInstrumentationVersion(Version())), - propagator: cfg.TextMapPropagator, - attributeSetter: cfg.AttributeSetter, + propagator: cfg.TextMapPropagator, + attributeBuilders: cfg.AttributeBuilders, } *apiOptions = append(*apiOptions, m.initializeMiddlewareBefore, m.initializeMiddlewareAfter, m.finalizeMiddlewareAfter, m.deserializeMiddleware) } diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go index 57be19d20f4..385ba6b8561 100755 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go @@ -4,6 +4,11 @@ package otelaws // import "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws" import ( + "context" + + "github.com/aws/smithy-go/middleware" + + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) @@ -11,7 +16,7 @@ import ( type config struct { TracerProvider trace.TracerProvider TextMapPropagator propagation.TextMapPropagator - AttributeSetter []AttributeSetter + AttributeBuilders []AttributeBuilder } // Option applies an option value. @@ -48,9 +53,22 @@ func WithTextMapPropagator(propagator propagation.TextMapPropagator) Option { } // WithAttributeSetter specifies an attribute setter function for setting service specific attributes. -// If none is specified, the service will be determined by the DefaultAttributeSetter function and the corresponding attributes will be included. +// If none is specified, the service will be determined by the DefaultAttributeBuilder function and the corresponding attributes will be included. func WithAttributeSetter(attributesetters ...AttributeSetter) Option { + var attributeBuilders []AttributeBuilder + for _, setter := range attributesetters { + attributeBuilders = append(attributeBuilders, func(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue { + return setter(ctx, in) + }) + } + + return WithAttributeBuilder(attributeBuilders...) +} + +// WithAttributeBuilder specifies an attribute setter function for setting service specific attributes. +// If none is specified, the service will be determined by the DefaultAttributeBuilder function and the corresponding attributes will be included. +func WithAttributeBuilder(attributeBuilders ...AttributeBuilder) Option { return optionFunc(func(cfg *config) { - cfg.AttributeSetter = append(cfg.AttributeSetter, attributesetters...) + cfg.AttributeBuilders = append(cfg.AttributeBuilders, attributeBuilders...) }) } diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes.go index 81d66fe61f8..fcf74ea9e18 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes.go @@ -15,7 +15,13 @@ import ( ) // DynamoDBAttributeSetter sets DynamoDB specific attributes depending on the DynamoDB operation being performed. +// Deprecated: Kept for backward compatibility, use DynamoDBAttributeBuilder instead. This will be removed in a future release. func DynamoDBAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue { + return DynamoDBAttributeBuilder(ctx, in, middleware.InitializeOutput{}) +} + +// DynamoDBAttributeBuilder sets DynamoDB specific attributes depending on the DynamoDB operation being performed. +func DynamoDBAttributeBuilder(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue { dynamodbAttributes := []attribute.KeyValue{semconv.DBSystemDynamoDB} switch v := in.Parameters.(type) { diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes_test.go index 79f4e80faf6..b5f4e1bcbf6 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/dynamodbattributes_test.go @@ -31,7 +31,7 @@ func TestDynamodbTagsBatchGetItemInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice("aws.dynamodb.table_names", []string{"table1"})) } @@ -60,7 +60,7 @@ func TestDynamodbTagsBatchWriteItemInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice("aws.dynamodb.table_names", []string{"table1"})) } @@ -114,7 +114,7 @@ func TestDynamodbTagsCreateTableInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -144,7 +144,7 @@ func TestDynamodbTagsDeleteItemInput(t *testing.T) { TableName: aws.String("table1"), }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -157,7 +157,7 @@ func TestDynamodbTagsDeleteTableInput(t *testing.T) { TableName: aws.String("table1"), }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -170,7 +170,7 @@ func TestDynamodbTagsDescribeTableInput(t *testing.T) { TableName: aws.String("table1"), }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -184,7 +184,7 @@ func TestDynamodbTagsListTablesInput(t *testing.T) { Limit: aws.Int32(10), }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.String("aws.dynamodb.exclusive_start_table", "table1")) assert.Contains(t, attributes, attribute.Int("aws.dynamodb.limit", 10)) @@ -202,7 +202,7 @@ func TestDynamodbTagsPutItemInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -230,7 +230,7 @@ func TestDynamodbTagsQueryInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"table1"}, @@ -257,7 +257,7 @@ func TestDynamodbTagsScanInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"my-table"}, @@ -285,7 +285,7 @@ func TestDynamodbTagsUpdateItemInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"my-table"}, @@ -326,7 +326,7 @@ func TestDynamodbTagsUpdateTableInput(t *testing.T) { }, } - attributes := DynamoDBAttributeSetter(context.TODO(), input) + attributes := DynamoDBAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, attribute.StringSlice( "aws.dynamodb.table_names", []string{"my-table"}, diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go index 03c0405804f..228f611d8b3 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go @@ -15,7 +15,13 @@ import ( ) // SNSAttributeSetter sets SNS specific attributes depending on the SNS operation is being performed. +// Deprecated: Kept for backward compatibility, use SNSAttributeBuilder instead. This will be removed in a future release. func SNSAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue { + return SNSAttributeBuilder(ctx, in, middleware.InitializeOutput{}) +} + +// SNSAttributeBuilder sets SNS specific attributes depending on the SNS operation is being performed. +func SNSAttributeBuilder(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue { snsAttributes := []attribute.KeyValue{semconv.MessagingSystemKey.String("aws_sns")} switch v := in.Parameters.(type) { diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes_test.go index fcf319b8361..cc6a59d75f6 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes_test.go @@ -23,7 +23,7 @@ func TestPublishInput(t *testing.T) { }, } - attributes := SNSAttributeSetter(context.Background(), input) + attributes := SNSAttributeBuilder(context.Background(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.MessagingSystemKey.String("aws_sns")) assert.Contains(t, attributes, semconv.MessagingDestinationName("my-topic")) @@ -36,7 +36,7 @@ func TestPublishInputWithNoDestination(t *testing.T) { Parameters: &sns.PublishInput{}, } - attributes := SNSAttributeSetter(context.Background(), input) + attributes := SNSAttributeBuilder(context.Background(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.MessagingSystemKey.String("aws_sns")) assert.Contains(t, attributes, semconv.MessagingDestinationName("")) @@ -52,7 +52,7 @@ func TestPublishBatchInput(t *testing.T) { }, } - attributes := SNSAttributeSetter(context.Background(), input) + attributes := SNSAttributeBuilder(context.Background(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.MessagingSystemKey.String("aws_sns")) assert.Contains(t, attributes, semconv.MessagingDestinationName("my-topic-batch")) diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go index cdc064da947..b860a8f09b6 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go @@ -14,7 +14,13 @@ import ( ) // SQSAttributeSetter sets SQS specific attributes depending on the SQS operation being performed. +// Deprecated: Kept for backward compatibility, use SQSAttributeBuilder instead. This will be removed in a future release. func SQSAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue { + return SQSAttributeBuilder(ctx, in, middleware.InitializeOutput{}) +} + +// SQSAttributeBuilder sets SQS specific attributes depending on the SQS operation being performed. +func SQSAttributeBuilder(ctx context.Context, in middleware.InitializeInput, out middleware.InitializeOutput) []attribute.KeyValue { sqsAttributes := []attribute.KeyValue{semconv.MessagingSystem("AmazonSQS")} key := semconv.NetPeerNameKey diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes_test.go index 3b427b4a3d7..23fa889d165 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes_test.go @@ -22,7 +22,7 @@ func TestSQSDeleteMessageBatchInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -34,7 +34,7 @@ func TestSQSDeleteMessageInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -46,7 +46,7 @@ func TestSQSDeleteQueueInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -58,7 +58,7 @@ func TestSQSGetQueueAttributesInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -70,7 +70,7 @@ func TestSQSListDeadLetterSourceQueuesInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -82,7 +82,7 @@ func TestSQSListQueueTagsInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -94,7 +94,7 @@ func TestSQSPurgeQueueInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -106,7 +106,7 @@ func TestSQSReceiveMessageInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -118,7 +118,7 @@ func TestSQSRemovePermissionInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -130,7 +130,7 @@ func TestSQSSendMessageBatchInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -142,7 +142,7 @@ func TestSQSSendMessageInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -154,7 +154,7 @@ func TestSQSSetQueueAttributesInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -166,7 +166,7 @@ func TestSQSTagQueueInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } @@ -178,7 +178,7 @@ func TestSQSUntagQueueInput(t *testing.T) { }, } - attributes := SQSAttributeSetter(context.TODO(), input) + attributes := SQSAttributeBuilder(context.TODO(), input, middleware.InitializeOutput{}) assert.Contains(t, attributes, semconv.NetPeerName("test-queue-url")) } diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/dynamodbattributes_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/dynamodbattributes_test.go index 8c6c137233e..ee1d6389c61 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/dynamodbattributes_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/dynamodbattributes_test.go @@ -18,12 +18,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws" ) type dynamoDBAuthResolver struct{} @@ -75,7 +76,7 @@ func TestDynamodbTags(t *testing.T) { }, }, func(options *dynamodb.Options) { otelaws.AppendMiddlewares( - &options.APIOptions, otelaws.WithAttributeSetter(otelaws.DynamoDBAttributeSetter), otelaws.WithTracerProvider(provider)) + &options.APIOptions, otelaws.WithAttributeBuilder(otelaws.DynamoDBAttributeBuilder), otelaws.WithTracerProvider(provider)) }) if cases.expectedError == codes.Unset { @@ -186,3 +187,86 @@ func TestDynamodbTagsCustomSetter(t *testing.T) { assert.Contains(t, attrs, attribute.String("customattribute1key", "customattribute1value")) }) } + +func TestDynamodbTagsCustomBuilder(t *testing.T) { + cases := struct { + responseStatus int + expectedRegion string + expectedStatusCode int + expectedError codes.Code + }{ + responseStatus: http.StatusOK, + expectedRegion: "us-west-2", + expectedStatusCode: http.StatusOK, + } + + server := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(cases.responseStatus) + })) + defer server.Close() + + t.Run("dynamodb tags", func(t *testing.T) { + sr := tracetest.NewSpanRecorder() + provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + + svc := dynamodb.New(dynamodb.Options{ + Region: cases.expectedRegion, + BaseEndpoint: &server.URL, + AuthSchemeResolver: &dynamoDBAuthResolver{}, + Retryer: aws.NopRetryer{}, + }) + + mycustomsetter := otelaws.AttributeBuilder(func(context.Context, middleware.InitializeInput, middleware.InitializeOutput) []attribute.KeyValue { + customAttributes := []attribute.KeyValue{ + { + Key: "customattribute2key", + Value: attribute.StringValue("customattribute2value"), + }, + { + Key: "customattribute1key", + Value: attribute.StringValue("customattribute1value"), + }, + } + + return customAttributes + }) + + _, err := svc.GetItem(context.Background(), &dynamodb.GetItemInput{ + TableName: aws.String("table1"), + ConsistentRead: aws.Bool(false), + ProjectionExpression: aws.String("test"), + Key: map[string]dtypes.AttributeValue{ + "id": &dtypes.AttributeValueMemberS{Value: "test"}, + }, + }, func(options *dynamodb.Options) { + otelaws.AppendMiddlewares( + &options.APIOptions, otelaws.WithAttributeBuilder(otelaws.DynamoDBAttributeBuilder, mycustomsetter), otelaws.WithTracerProvider(provider)) + }) + + if cases.expectedError == codes.Unset { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + + assert.Equal(t, "DynamoDB.GetItem", span.Name()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + attrs := span.Attributes() + assert.Contains(t, attrs, attribute.Int("http.status_code", cases.expectedStatusCode)) + assert.Contains(t, attrs, attribute.String("rpc.service", "DynamoDB")) + assert.Contains(t, attrs, attribute.String("aws.region", cases.expectedRegion)) + assert.Contains(t, attrs, attribute.String("rpc.method", "GetItem")) + assert.Contains(t, attrs, attribute.StringSlice( + "aws.dynamodb.table_names", []string{"table1"}, + )) + assert.Contains(t, attrs, attribute.String("aws.dynamodb.projection", "test")) + assert.Contains(t, attrs, attribute.Bool("aws.dynamodb.consistent_read", false)) + assert.Contains(t, attrs, attribute.String("customattribute2key", "customattribute2value")) + assert.Contains(t, attrs, attribute.String("customattribute1key", "customattribute1value")) + }) +}