Skip to content

Commit

Permalink
Add AttributeBuilder, which will extend the AttributeSetter contract …
Browse files Browse the repository at this point in the history
…with output value. Thanks to that, users can instrument attributes based on the AWS SDK responses.
  • Loading branch information
SodaDev committed Jan 6, 2025
1 parent 00786cc commit e8d0895
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 50 deletions.
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)
- `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` change: Added new `AttributeBuilder` interface to support adding attributes based on input and output instead of input only. It is also now possible to provide a custom function to set attributes using `WithAttributeBuilder`

### Changed

- `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` change: Use of `AttributeBuilder` interface instead of `AttributeSetter`. Previous usage of `AttributeSetter` were backward compatibly changed to use `AttributeBuilder` interface.
- `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` change: Attributes are added to the span after the execution instead of before the execution of AWS SDK


## [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
17 changes: 12 additions & 5 deletions instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/attributes.go
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.
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)

Check warning on line 70 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#L70

Added line #L70 was not covered by tests
}

return []attribute.KeyValue{}
Expand Down
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

// 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)...)
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)
}
22 changes: 19 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,17 @@
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 +51,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)
})

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

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go#L56-L60

Added lines #L56 - L60 were not covered by tests
}

return WithAttributeBuilder(attributeBuilders...)

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

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/config.go#L63

Added line #L63 was not covered by tests
}

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

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L20 was not covered by tests
}

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

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

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

0 comments on commit e8d0895

Please sign in to comment.