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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
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 @@

// 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{})

Check warning on line 61 in instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go#L61

Added line #L61 was not covered by tests
}

// 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{}
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) {
// 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)
}
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 {
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 @@
)

// 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{})

Check warning on line 20 in instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/snsattributes.go#L20

Added line #L20 was not covered by tests
}

// 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) {
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 @@
)

// 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{})

Check warning on line 19 in instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/sqsattributes.go#L19

Added line #L19 was not covered by tests
}

// 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