Skip to content

Commit c011266

Browse files
Add otelaws middleware to support trace propagation in the AWS SDK v2 module (open-telemetry#2856)
* Add middleware for trace propagation * Add tests and make propagator configurable * Fix func doc * Update changelog * Fix linter errors * Fix changelog again Co-authored-by: Anthony Mirabella <[email protected]>
1 parent 151c999 commit c011266

File tree

5 files changed

+153
-4
lines changed

5 files changed

+153
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
- Add trace context propagation support to `instrumentation/github.com/aws/aws-sdk-go-v2/otelaws` (#2856).
14+
1115
## [1.11.0/0.36.3/0.5.1]
1216

1317
### Changed

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"go.opentelemetry.io/otel"
2626
"go.opentelemetry.io/otel/attribute"
2727
"go.opentelemetry.io/otel/codes"
28+
"go.opentelemetry.io/otel/propagation"
2829
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
2930
"go.opentelemetry.io/otel/trace"
3031
)
@@ -40,6 +41,7 @@ type AttributeSetter func(context.Context, middleware.InitializeInput) []attribu
4041

4142
type otelMiddlewares struct {
4243
tracer trace.Tracer
44+
propagator propagation.TextMapPropagator
4345
attributeSetter []AttributeSetter
4446
}
4547

@@ -110,12 +112,29 @@ func (m otelMiddlewares) deserializeMiddleware(stack *middleware.Stack) error {
110112
middleware.Before)
111113
}
112114

115+
func (m otelMiddlewares) finalizeMiddleware(stack *middleware.Stack) error {
116+
return stack.Finalize.Add(middleware.FinalizeMiddlewareFunc("OTelFinalizeMiddleware", func(
117+
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (
118+
out middleware.FinalizeOutput, metadata middleware.Metadata, err error) {
119+
// Propagate the Trace information by injecting it into the HTTP request.
120+
switch req := in.Request.(type) {
121+
case *smithyhttp.Request:
122+
m.propagator.Inject(ctx, propagation.HeaderCarrier(req.Header))
123+
default:
124+
}
125+
126+
return next.HandleFinalize(ctx, in)
127+
}),
128+
middleware.Before)
129+
}
130+
113131
// AppendMiddlewares attaches OTel middlewares to the AWS Go SDK V2 for instrumentation.
114132
// OTel middlewares can be appended to either all aws clients or a specific operation.
115133
// Please see more details in https://aws.github.io/aws-sdk-go-v2/docs/middleware/
116134
func AppendMiddlewares(apiOptions *[]func(*middleware.Stack) error, opts ...Option) {
117135
cfg := config{
118-
TracerProvider: otel.GetTracerProvider(),
136+
TracerProvider: otel.GetTracerProvider(),
137+
TextMapPropagator: otel.GetTextMapPropagator(),
119138
}
120139
for _, opt := range opts {
121140
opt.apply(&cfg)
@@ -127,6 +146,7 @@ func AppendMiddlewares(apiOptions *[]func(*middleware.Stack) error, opts ...Opti
127146

128147
m := otelMiddlewares{tracer: cfg.TracerProvider.Tracer(tracerName,
129148
trace.WithInstrumentationVersion(SemVersion())),
149+
propagator: cfg.TextMapPropagator,
130150
attributeSetter: cfg.AttributeSetter}
131-
*apiOptions = append(*apiOptions, m.initializeMiddlewareBefore, m.initializeMiddlewareAfter, m.deserializeMiddleware)
151+
*apiOptions = append(*apiOptions, m.initializeMiddlewareBefore, m.initializeMiddlewareAfter, m.finalizeMiddleware, m.deserializeMiddleware)
132152
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
"net/http"
20+
"testing"
21+
22+
"github.com/aws/smithy-go/middleware"
23+
smithyhttp "github.com/aws/smithy-go/transport/http"
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
26+
27+
"go.opentelemetry.io/otel/propagation"
28+
)
29+
30+
type mockPropagator struct {
31+
injectKey string
32+
injectValue string
33+
}
34+
35+
func (p mockPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
36+
carrier.Set(p.injectKey, p.injectValue)
37+
}
38+
func (p mockPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
39+
return context.TODO()
40+
}
41+
func (p mockPropagator) Fields() []string {
42+
return []string{}
43+
}
44+
45+
func Test_otelMiddlewares_finalizeMiddleware(t *testing.T) {
46+
stack := middleware.Stack{
47+
Finalize: middleware.NewFinalizeStep(),
48+
}
49+
50+
propagator := mockPropagator{
51+
injectKey: "mock-key",
52+
injectValue: "mock-value",
53+
}
54+
55+
m := otelMiddlewares{
56+
propagator: propagator,
57+
}
58+
59+
err := m.finalizeMiddleware(&stack)
60+
require.NoError(t, err)
61+
62+
input := &smithyhttp.Request{
63+
Request: &http.Request{
64+
Header: http.Header{},
65+
},
66+
}
67+
68+
next := middleware.HandlerFunc(func(ctx context.Context, input interface{}) (output interface{}, metadata middleware.Metadata, err error) {
69+
return nil, middleware.Metadata{}, nil
70+
})
71+
72+
_, _, _ = stack.Finalize.HandleMiddleware(context.Background(), input, next)
73+
74+
// Assert header has been updated with injected values
75+
key := http.CanonicalHeaderKey(propagator.injectKey)
76+
value := propagator.injectValue
77+
78+
assert.Contains(t, input.Header, key)
79+
assert.Contains(t, input.Header[key], value)
80+
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
package otelaws // import "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws"
1616

1717
import (
18+
"go.opentelemetry.io/otel/propagation"
1819
"go.opentelemetry.io/otel/trace"
1920
)
2021

2122
type config struct {
22-
TracerProvider trace.TracerProvider
23-
AttributeSetter []AttributeSetter
23+
TracerProvider trace.TracerProvider
24+
TextMapPropagator propagation.TextMapPropagator
25+
AttributeSetter []AttributeSetter
2426
}
2527

2628
// Option applies an option value.
@@ -46,6 +48,16 @@ func WithTracerProvider(provider trace.TracerProvider) Option {
4648
})
4749
}
4850

51+
// WithTextMapPropagator specifies a Text Map Propagator to use when propagating context.
52+
// If none is specified, the global TextMapPropagator is used.
53+
func WithTextMapPropagator(propagator propagation.TextMapPropagator) Option {
54+
return optionFunc(func(cfg *config) {
55+
if propagator != nil {
56+
cfg.TextMapPropagator = propagator
57+
}
58+
})
59+
}
60+
4961
// WithAttributeSetter specifies an attribute setter function for setting service specific attributes.
5062
// If none is specified, the service will be determined by the DefaultAttributeSetter function and the corresponding attributes will be included.
5163
func WithAttributeSetter(attributesetters ...AttributeSetter) Option {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
22+
"go.opentelemetry.io/otel"
23+
)
24+
25+
func TestWithTextMapPropagator(t *testing.T) {
26+
cfg := config{}
27+
propagator := otel.GetTextMapPropagator()
28+
29+
option := WithTextMapPropagator(propagator)
30+
option.apply(&cfg)
31+
32+
assert.Equal(t, cfg.TextMapPropagator, propagator)
33+
}

0 commit comments

Comments
 (0)