Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4b08c01
Prototypes propagation with multiple values. Adds MultiTextMapCarrier…
jamesmoessis Nov 14, 2024
4cffc3c
implement composability suggestion with regards to interface design
jamesmoessis Apr 28, 2025
5970fd4
rename GetAll to Values as suggested
jamesmoessis Apr 28, 2025
a63c63a
ensure backwards compatibility message is in all places it should be
jamesmoessis Apr 28, 2025
9e4e348
optimise when bVals is empty as suggested -- also add test case for e…
jamesmoessis Apr 28, 2025
c72ade6
add changelog entry
jamesmoessis Apr 28, 2025
9de8b16
appease linter
jamesmoessis Apr 28, 2025
cf66b68
Update propagation/baggage.go
jamesmoessis Apr 29, 2025
52f3e06
rename MultiGetter to ValuesGetter, implementing review suggestion
jamesmoessis Apr 29, 2025
8bbb867
add more information into the go doc of Extract, as per review
jamesmoessis Apr 29, 2025
1bceb79
update HeaderCarrier go doc to explicitly state that it satisfies Mul…
jamesmoessis Apr 29, 2025
c7bf432
update TextMapPropagator.Extract go doc to mention checking if carrie…
jamesmoessis Apr 29, 2025
324cfcf
Merge branch 'main' into multi-baggage-propagation-prototype
pellared May 6, 2025
5c239a5
remove MultiTextMapCarrier and just reference ValuesGetter directly, …
jamesmoessis May 8, 2025
2381840
Merge branch 'main' into multi-baggage-propagation-prototype
jamesmoessis May 8, 2025
47204d0
Update propagation/baggage.go
jamesmoessis May 8, 2025
a239650
Update CHANGELOG.md
jamesmoessis May 8, 2025
41191a9
add compile time checks for implementing interfaces
jamesmoessis May 8, 2025
9698552
update test to additionally test baggage with map carrier, as map car…
jamesmoessis May 8, 2025
f7254e2
Merge branch 'main' into multi-baggage-propagation-prototype
pellared May 9, 2025
d116229
Update propagation/baggage_test.go
jamesmoessis May 14, 2025
0d124fe
Update propagation/baggage_test.go
jamesmoessis May 14, 2025
f9c5a64
Merge branch 'main' into multi-baggage-propagation-prototype
pellared May 14, 2025
91a4ffc
fix applied suggestion
jamesmoessis May 15, 2025
1307ad4
Merge branch 'main' into multi-baggage-propagation-prototype
pellared May 15, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
The package contains semantic conventions from the `v1.31.0` version of the OpenTelemetry Semantic Conventions.
See the [migration documentation](./semconv/v1.31.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.30.0`(#6479)
- Add `Recording`, `Scope`, and `Record` types in `go.opentelemetry.io/otel/log/logtest`. (#6507)
- Allow extraction of multiple values in propagation. Implements for baggage propagator. (#5973)

### Removed

Expand Down
35 changes: 32 additions & 3 deletions propagation/baggage.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) {

// Extract returns a copy of parent with the baggage from the carrier added.
func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context {
multiCarrier, isMultiCarrier := carrier.(MultiTextMapCarrier)
if isMultiCarrier {
return extractMultiBaggage(parent, multiCarrier)
}
return extractSingleBaggage(parent, carrier)
}

// Fields returns the keys who's values are set with Inject.
func (b Baggage) Fields() []string {
return []string{baggageHeader}
}

func extractSingleBaggage(parent context.Context, carrier TextMapCarrier) context.Context {
bStr := carrier.Get(baggageHeader)
if bStr == "" {
return parent
Expand All @@ -41,7 +54,23 @@ func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context
return baggage.ContextWithBaggage(parent, bag)
}

// Fields returns the keys who's values are set with Inject.
func (b Baggage) Fields() []string {
return []string{baggageHeader}
func extractMultiBaggage(parent context.Context, carrier MultiTextMapCarrier) context.Context {
bVals := carrier.Values(baggageHeader)
if len(bVals) == 0 {
return parent
}
members := make([]baggage.Member, 0)
for _, bStr := range bVals {
currBag, err := baggage.Parse(bStr)
if err != nil {
continue
}
members = append(members, currBag.Members()...)
}

b, err := baggage.New(members...)
if err != nil || b.Len() == 0 {
return parent
}
return baggage.ContextWithBaggage(parent, b)
}
54 changes: 54 additions & 0 deletions propagation/baggage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,60 @@ func TestExtractValidBaggageFromHTTPReq(t *testing.T) {
}
}

func TestExtractValidMultipleBaggageHeaders(t *testing.T) {
prop := propagation.TextMapPropagator(propagation.Baggage{})
tests := []struct {
name string
headers []string
want members
}{
{
name: "non conflicting headers",
headers: []string{"key1=val1", "key2=val2"},
want: members{
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
},
},
{
name: "conflicting keys, uses last val",
headers: []string{"key1=val1", "key1=val2"},
want: members{
{Key: "key1", Value: "val2"},
},
},
{
name: "single empty",
headers: []string{"", "key1=val1"},
want: members{
{Key: "key1", Value: "val1"},
},
},
{
name: "all empty",
headers: []string{"", ""},
want: members{},
},
{
name: "none",
headers: []string{},
want: members{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
req.Header["Baggage"] = tt.headers

ctx := context.Background()
ctx = prop.Extract(ctx, propagation.HeaderCarrier(req.Header))
expected := tt.want.Baggage(t)
assert.Equal(t, expected, baggage.FromContext(ctx))
})
}
}

func TestExtractInvalidDistributedContextFromHTTPReq(t *testing.T) {
prop := propagation.TextMapPropagator(propagation.Baggage{})
tests := []struct {
Expand Down
28 changes: 27 additions & 1 deletion propagation/propagation.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,27 @@ type TextMapCarrier interface {
// must never be done outside of a new major release.
}

// MultiTextMapCarrier is a TextMapCarrier that can return multiple values for a single key.
type MultiTextMapCarrier interface {
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.

TextMapCarrier
MultiGetter
}

// MultiGetter can return multiple values for a single key,
// with contrast to TextMapCarrier.Get which returns a single value.
type MultiGetter interface {
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.

// Values returns all values associated with the passed key.
Values(key string) []string
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.
}

// MapCarrier is a TextMapCarrier that uses a map held in memory as a storage
// medium for propagated key-value pairs.
type MapCarrier map[string]string
Expand Down Expand Up @@ -58,11 +79,16 @@ func (c MapCarrier) Keys() []string {
// HeaderCarrier adapts http.Header to satisfy the TextMapCarrier interface.
type HeaderCarrier http.Header

// Get returns the value associated with the passed key.
// Get returns the first value associated with the passed key.
func (hc HeaderCarrier) Get(key string) string {
return http.Header(hc).Get(key)
}

// Values returns all values associated with the passed key.
func (hc HeaderCarrier) Values(key string) []string {
return http.Header(hc).Values(key)
}

// Set stores the key-value pair.
func (hc HeaderCarrier) Set(key string, value string) {
http.Header(hc).Set(key, value)
Expand Down
Loading