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

Support output value in AttributeSetter #6543

Merged
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- Generate server metrics with semantic conventions `v1.26.0` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` when `OTEL_SEMCONV_STABILITY_OPT_IN` is set to `http/dup`. (#6411)
- Generate client metrics with semantic conventions `v1.26.0` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` when `OTEL_SEMCONV_STABILITY_OPT_IN` is set to `http/dup`. (#6607)
- 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)
pellared marked this conversation as resolved.
Show resolved Hide resolved


### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
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 {
pellared marked this conversation as resolved.
Show resolved Hide resolved
serviceID := v2Middleware.GetServiceID(ctx)

if fn, ok := servicemap[serviceID]; ok {
return fn(ctx, in)
return fn(ctx, in, out)
}

return []attribute.KeyValue{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
// GIVEN
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
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)
}
30 changes: 20 additions & 10 deletions instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
dmathieu marked this conversation as resolved.
Show resolved Hide resolved

// 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 {
Expand Down Expand Up @@ -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)),
Expand All @@ -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)...)
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
Expand Down Expand Up @@ -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 != "" {
Expand All @@ -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)
}
24 changes: 21 additions & 3 deletions instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
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"
)

type config struct {
TracerProvider trace.TracerProvider
TextMapPropagator propagation.TextMapPropagator
AttributeSetter []AttributeSetter
AttributeBuilders []AttributeBuilder
}

// Option applies an option value.
Expand Down Expand Up @@ -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...)
})
}
dmathieu marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import (
)

// DynamoDBAttributeSetter sets DynamoDB specific attributes depending on the DynamoDB operation being performed.
SodaDev marked this conversation as resolved.
Show resolved Hide resolved
// 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 {
pellared marked this conversation as resolved.
Show resolved Hide resolved
dynamodbAttributes := []attribute.KeyValue{semconv.DBSystemDynamoDB}

switch v := in.Parameters.(type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"}))
}
Expand Down Expand Up @@ -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"}))
}
Expand Down Expand Up @@ -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"},
Expand Down Expand Up @@ -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"},
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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))
Expand All @@ -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"},
Expand Down Expand Up @@ -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"},
Expand All @@ -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"},
Expand Down Expand Up @@ -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"},
Expand Down Expand Up @@ -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"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
pellared marked this conversation as resolved.
Show resolved Hide resolved
snsAttributes := []attribute.KeyValue{semconv.MessagingSystemKey.String("aws_sns")}

switch v := in.Parameters.(type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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(""))
Expand All @@ -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"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading