Skip to content

Commit 4be24de

Browse files
jennynilsenMrAliasAneurysm9
authored
otelaws: adding dynamodb attributes (#1582)
* adding dynamodb attributes * applying suggestions * allowing users to use the default attribute setter in addition to their own * making setting attributesetters more flexible * Apply suggestions from code review Co-authored-by: Tyler Yahn <[email protected]> * Revert "Apply suggestions from code review" This reverts commit 05c3847. * Apply suggestions from code review Co-authored-by: Tyler Yahn <[email protected]> * adding comments for exported functions and types * Apply suggestions from code review Co-authored-by: Tyler Yahn <[email protected]> * fixing test and applying suggestions * fixing checks Co-authored-by: Tyler Yahn <[email protected]> Co-authored-by: Anthony Mirabella <[email protected]>
1 parent f7fe163 commit 4be24de

File tree

12 files changed

+805
-7
lines changed

12 files changed

+805
-7
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,18 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
88

99
## [Unreleased]
1010

11+
12+
### Added
13+
14+
- DynamoDB spans created with the `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` package will now have the appropriate database attributes added for the operation being performed.
15+
These attributes are detected automatically, but it is also now possible to provide a custom function to set attributes using `WithAttributeSetter`. (#1582)
16+
1117
### Fixed
1218

1319
- Fix the `echo` middleware by using `SpanKind.SERVER` when deciding the `SpanStatus`.
1420
This makes `4xx` response codes to not be an error anymore. (#1848)
1521

22+
1623
## [1.4.0/0.29.0] - 2022-02-14
1724

1825
### Added

instrumentation/github.com/aws/aws-lambda-go/otellambda/example/go.sum

+7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 h1:3ADoioDMOtF4uiK59vC
1717
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E=
1818
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk=
1919
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI=
20+
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.13.0 h1:Xlmdkxi8WcIwX5Cy9BS+scWcmvARw8pg0bi7kaeERUY=
21+
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.13.0/go.mod h1:eNvoR4P1XQN7xElmYA8cWeFENLY3pfsj/5nFRItzXnA=
2022
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.7.0 h1:F1diQIOkNn8jcez4173r+PLPdkWK7chy74r3fKpDrLI=
2123
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.7.0/go.mod h1:8ctElVINyp+SjhoZZceUAZw78glZH6R8ox5MVNu5j2s=
24+
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.5.0 h1:tzVhIPr/psp8Gb2Blst9mq6HklkhAGPqv2eaiSq6yoU=
25+
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.5.0/go.mod h1:u0rI/Mm45zCJe86J5kvPfG7pYzkVZzNjEkoTVbfOYE8=
2226
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw=
2327
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU=
2428
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.11.0 h1:XAe+PDnaBELHr25qaJKfB415V4CKFWE8H+prUreql8k=
@@ -46,7 +50,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
4650
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
4751
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
4852
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
53+
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
4954
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
55+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
5056
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
5157
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5258
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -78,6 +84,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
7884
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7985
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8086
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
87+
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
8188
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
8289
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
8390
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=

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

+25-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414

1515
package otelaws
1616

17-
import "go.opentelemetry.io/otel/attribute"
17+
import (
18+
"context"
19+
20+
v2Middleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
21+
"github.com/aws/smithy-go/middleware"
22+
23+
"go.opentelemetry.io/otel/attribute"
24+
)
1825

1926
// AWS attributes.
2027
const (
@@ -24,6 +31,10 @@ const (
2431
RequestIDKey attribute.Key = "aws.request_id"
2532
)
2633

34+
var servicemap map[string]AttributeSetter = map[string]AttributeSetter{
35+
"dynamodb": DynamoDBAttributeSetter,
36+
}
37+
2738
func OperationAttr(operation string) attribute.KeyValue {
2839
return OperationKey.String(operation)
2940
}
@@ -39,3 +50,16 @@ func ServiceAttr(service string) attribute.KeyValue {
3950
func RequestIDAttr(requestID string) attribute.KeyValue {
4051
return RequestIDKey.String(requestID)
4152
}
53+
54+
// DefaultAttributeSetter checks to see if there are service specific attributes available to set for the AWS service.
55+
// If there are service specific attributes available then they will be included.
56+
func DefaultAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue {
57+
58+
serviceID := v2Middleware.GetServiceID(ctx)
59+
60+
if fn, ok := servicemap[serviceID]; ok {
61+
return fn(ctx, in)
62+
}
63+
64+
return []attribute.KeyValue{}
65+
}

instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/aws.go

+24-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
smithyhttp "github.com/aws/smithy-go/transport/http"
2424

2525
"go.opentelemetry.io/otel"
26+
"go.opentelemetry.io/otel/attribute"
2627
"go.opentelemetry.io/otel/codes"
2728
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
2829
"go.opentelemetry.io/otel/trace"
@@ -34,8 +35,12 @@ const (
3435

3536
type spanTimestampKey struct{}
3637

38+
// AttributeSetter returns an array of KeyValue pairs, it can be used to set custom attributes.
39+
type AttributeSetter func(context.Context, middleware.InitializeInput) []attribute.KeyValue
40+
3741
type otelMiddlewares struct {
38-
tracer trace.Tracer
42+
tracer trace.Tracer
43+
attributeSetter []AttributeSetter
3944
}
4045

4146
func (m otelMiddlewares) initializeMiddlewareBefore(stack *middleware.Stack) error {
@@ -55,12 +60,20 @@ func (m otelMiddlewares) initializeMiddlewareAfter(stack *middleware.Stack) erro
5560
out middleware.InitializeOutput, metadata middleware.Metadata, err error) {
5661

5762
serviceID := v2Middleware.GetServiceID(ctx)
63+
64+
attributes := []attribute.KeyValue{
65+
ServiceAttr(serviceID),
66+
RegionAttr(v2Middleware.GetRegion(ctx)),
67+
OperationAttr(v2Middleware.GetOperationName(ctx)),
68+
}
69+
for _, setter := range m.attributeSetter {
70+
attributes = append(attributes, setter(ctx, in)...)
71+
}
72+
5873
ctx, span := m.tracer.Start(ctx, serviceID,
5974
trace.WithTimestamp(ctx.Value(spanTimestampKey{}).(time.Time)),
6075
trace.WithSpanKind(trace.SpanKindClient),
61-
trace.WithAttributes(ServiceAttr(serviceID),
62-
RegionAttr(v2Middleware.GetRegion(ctx)),
63-
OperationAttr(v2Middleware.GetOperationName(ctx))),
76+
trace.WithAttributes(attributes...),
6477
)
6578
defer span.End()
6679

@@ -110,7 +123,13 @@ func AppendMiddlewares(apiOptions *[]func(*middleware.Stack) error, opts ...Opti
110123
opt.apply(&cfg)
111124
}
112125

126+
if cfg.AttributeSetter == nil {
127+
cfg.AttributeSetter = []AttributeSetter{DefaultAttributeSetter}
128+
}
129+
113130
m := otelMiddlewares{tracer: cfg.TracerProvider.Tracer(tracerName,
114-
trace.WithInstrumentationVersion(SemVersion()))}
131+
trace.WithInstrumentationVersion(SemVersion())),
132+
attributeSetter: cfg.AttributeSetter}
115133
*apiOptions = append(*apiOptions, m.initializeMiddlewareBefore, m.initializeMiddlewareAfter, m.deserializeMiddleware)
134+
116135
}

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import (
1919
)
2020

2121
type config struct {
22-
TracerProvider trace.TracerProvider
22+
TracerProvider trace.TracerProvider
23+
AttributeSetter []AttributeSetter
2324
}
2425

2526
// Option applies an option value.
@@ -44,3 +45,11 @@ func WithTracerProvider(provider trace.TracerProvider) Option {
4445
}
4546
})
4647
}
48+
49+
// WithAttributeSetter specifies an attribute setter function for setting service specific attributes.
50+
// If none is specified, the service will be determined by the DefaultAttributeSetter function and the corresponding attributes will be included.
51+
func WithAttributeSetter(attributesetters ...AttributeSetter) Option {
52+
return optionFunc(func(cfg *config) {
53+
cfg.AttributeSetter = append(cfg.AttributeSetter, attributesetters...)
54+
})
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package otelaws
16+
17+
import (
18+
"context"
19+
"encoding/json"
20+
21+
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
22+
"github.com/aws/smithy-go/middleware"
23+
24+
"go.opentelemetry.io/otel/attribute"
25+
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
26+
)
27+
28+
// DynamoDBAttributeSetter sets DynamoDB specific attributes depending on the the DynamoDB operation being performed.
29+
func DynamoDBAttributeSetter(ctx context.Context, in middleware.InitializeInput) []attribute.KeyValue {
30+
31+
dynamodbAttributes := []attribute.KeyValue{semconv.DBSystemKey.String("dynamodb")}
32+
33+
switch v := in.Parameters.(type) {
34+
case *dynamodb.GetItemInput:
35+
36+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
37+
38+
if v.ConsistentRead != nil {
39+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBConsistentReadKey.Bool(*v.ConsistentRead))
40+
}
41+
42+
if v.ProjectionExpression != nil {
43+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProjectionKey.String(*v.ProjectionExpression))
44+
}
45+
46+
case *dynamodb.BatchGetItemInput:
47+
var tableNames []string
48+
for k := range v.RequestItems {
49+
tableNames = append(tableNames, k)
50+
}
51+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.StringSlice(tableNames))
52+
53+
case *dynamodb.BatchWriteItemInput:
54+
var tableNames []string
55+
for k := range v.RequestItems {
56+
tableNames = append(tableNames, k)
57+
}
58+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.StringSlice(tableNames))
59+
60+
case *dynamodb.CreateTableInput:
61+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
62+
63+
if v.GlobalSecondaryIndexes != nil {
64+
globalindexes, _ := json.Marshal(v.GlobalSecondaryIndexes)
65+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBGlobalSecondaryIndexesKey.String(string(globalindexes)))
66+
}
67+
68+
if v.LocalSecondaryIndexes != nil {
69+
localindexes, _ := json.Marshal(v.LocalSecondaryIndexes)
70+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBLocalSecondaryIndexesKey.String(string(localindexes)))
71+
}
72+
73+
if v.ProvisionedThroughput != nil {
74+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProvisionedReadCapacityKey.Int64(*v.ProvisionedThroughput.ReadCapacityUnits))
75+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProvisionedWriteCapacityKey.Int64(*v.ProvisionedThroughput.WriteCapacityUnits))
76+
}
77+
78+
case *dynamodb.DeleteItemInput:
79+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
80+
81+
case *dynamodb.DeleteTableInput:
82+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
83+
84+
case *dynamodb.DescribeTableInput:
85+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
86+
87+
case *dynamodb.ListTablesInput:
88+
89+
if v.ExclusiveStartTableName != nil {
90+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBExclusiveStartTableKey.String(*v.ExclusiveStartTableName))
91+
}
92+
93+
if v.Limit != nil {
94+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBLimitKey.Int(int(*v.Limit)))
95+
}
96+
97+
case *dynamodb.PutItemInput:
98+
99+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
100+
101+
case *dynamodb.QueryInput:
102+
103+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
104+
105+
if v.ConsistentRead != nil {
106+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBConsistentReadKey.Bool(*v.ConsistentRead))
107+
}
108+
109+
if v.IndexName != nil {
110+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBIndexNameKey.String(*v.IndexName))
111+
}
112+
113+
if v.Limit != nil {
114+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBLimitKey.Int(int(*v.Limit)))
115+
}
116+
117+
if v.ScanIndexForward != nil {
118+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBScanForwardKey.Bool(*v.ScanIndexForward))
119+
}
120+
121+
if v.ProjectionExpression != nil {
122+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProjectionKey.String(*v.ProjectionExpression))
123+
}
124+
125+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBSelectKey.String(string(v.Select)))
126+
127+
case *dynamodb.ScanInput:
128+
129+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
130+
131+
if v.ConsistentRead != nil {
132+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBConsistentReadKey.Bool(*v.ConsistentRead))
133+
}
134+
135+
if v.IndexName != nil {
136+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBIndexNameKey.String(*v.IndexName))
137+
}
138+
139+
if v.Limit != nil {
140+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBLimitKey.Int(int(*v.Limit)))
141+
}
142+
143+
if v.ProjectionExpression != nil {
144+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProjectionKey.String(*v.ProjectionExpression))
145+
}
146+
147+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBSelectKey.String(string(v.Select)))
148+
149+
if v.Segment != nil {
150+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBSegmentKey.Int(int(*v.Segment)))
151+
}
152+
153+
if v.TotalSegments != nil {
154+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTotalSegmentsKey.Int(int(*v.TotalSegments)))
155+
}
156+
157+
case *dynamodb.UpdateItemInput:
158+
159+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
160+
161+
case *dynamodb.UpdateTableInput:
162+
163+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBTableNamesKey.String(*v.TableName))
164+
165+
if v.AttributeDefinitions != nil {
166+
attributedefinitions, _ := json.Marshal(v.AttributeDefinitions)
167+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBAttributeDefinitionsKey.String(string(attributedefinitions)))
168+
}
169+
170+
if v.GlobalSecondaryIndexUpdates != nil {
171+
globalsecondaryindexupdates, _ := json.Marshal(v.GlobalSecondaryIndexUpdates)
172+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBGlobalSecondaryIndexUpdatesKey.String(string(globalsecondaryindexupdates)))
173+
}
174+
175+
if v.ProvisionedThroughput != nil {
176+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProvisionedReadCapacityKey.Int64(*v.ProvisionedThroughput.ReadCapacityUnits))
177+
dynamodbAttributes = append(dynamodbAttributes, semconv.AWSDynamoDBProvisionedWriteCapacityKey.Int64(*v.ProvisionedThroughput.WriteCapacityUnits))
178+
}
179+
180+
}
181+
182+
return dynamodbAttributes
183+
184+
}

0 commit comments

Comments
 (0)