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

OpenCensus bridge to support TraceState #5651

Merged
merged 28 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
27e75a7
Update OpencensuBridge to propagate TraceState between OTel and OC spans
jianwu Jul 25, 2024
0838db1
address comments; add CHANGELOG; remove inverse logic in TraceState.K…
jianwu Jul 30, 2024
e9bc04b
.
jianwu Jul 31, 2024
1889613
Add SpanState.Walk() instead of Keys()
jianwu Jul 31, 2024
874e7ba
update change log
jianwu Jul 31, 2024
04cf2fd
update doc
jianwu Jul 31, 2024
9c5ac83
.
jianwu Jul 31, 2024
92a0cbe
fmt
jianwu Jul 31, 2024
c0cf713
Merge branch 'main' into tracestate-propagation
jianwu Jul 31, 2024
d58b659
Update changelog; update tests otel2oc.TestSpanContextConversion with…
jianwu Aug 2, 2024
d405c73
Merge branch 'main' into tracestate-propagation
jianwu Aug 2, 2024
6e97577
fmt
jianwu Aug 2, 2024
8b88060
Merge branch 'main' into tracestate-propagation
jianwu Aug 5, 2024
ea66803
Merge branch 'main' into tracestate-propagation
jianwu Aug 5, 2024
9b12824
Merge branch 'main' into tracestate-propagation
jianwu Aug 6, 2024
a36dbeb
Merge branch 'main' into tracestate-propagation
jianwu Aug 7, 2024
e7d4289
Update bridge/opencensus/internal/otel2oc/span_context_test.go
jianwu Aug 9, 2024
af61537
Update bridge/opencensus/internal/otel2oc/span_context_test.go
jianwu Aug 9, 2024
4d255a9
Update bridge/opencensus/internal/otel2oc/span_context_test.go
jianwu Aug 9, 2024
9de698e
Update bridge/opencensus/internal/oc2otel/span_context.go
jianwu Aug 9, 2024
100dd24
fix build and test
jianwu Aug 9, 2024
0f891d7
Merge branch 'main' into tracestate-propagation
jianwu Aug 9, 2024
f5b22f9
fmt
jianwu Aug 9, 2024
dd21c4f
Merge branch 'main' into tracestate-propagation
jianwu Aug 9, 2024
bee8e19
Merge branch 'main' into tracestate-propagation
jianwu Aug 12, 2024
1eecc6c
add more checks for header output for oc2otel span conversion
jianwu Aug 12, 2024
17d8ffd
Merge branch 'main' into tracestate-propagation
jianwu Aug 19, 2024
005c710
Merge branch 'main' into tracestate-propagation
jianwu Aug 21, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. (#5629)
- Add `InstrumentationScope` field to `SpanStub` in `go.opentelemetry.io/otel/sdk/trace/tracetest`, as a replacement for the deprecated `InstrumentationLibrary`. (#5627)
- Zero value of `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` no longer panics. (#5665)
- Add `Walk` function to `TraceState` in `go.opentelemetry.io/otel/trace` to iterate all the key-value pairs. (#5651)
- Bridge the trace state in `go.opentelemetry.io/otel/bridge/opencensus`. (#5651)
pellared marked this conversation as resolved.
Show resolved Hide resolved

### Changed

Expand Down
12 changes: 12 additions & 0 deletions bridge/opencensus/internal/oc2otel/span_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package oc2otel // import "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel"

import (
"slices"

octrace "go.opencensus.io/trace"

"go.opentelemetry.io/otel/trace"
Expand All @@ -14,9 +16,19 @@ func SpanContext(sc octrace.SpanContext) trace.SpanContext {
if sc.IsSampled() {
traceFlags = trace.FlagsSampled
}

entries := slices.Clone(sc.Tracestate.Entries())
slices.Reverse(entries)

tsOtel := trace.TraceState{}
for _, entry := range entries {
tsOtel, _ = tsOtel.Insert(entry.Key, entry.Value)
}

return trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID(sc.TraceID),
SpanID: trace.SpanID(sc.SpanID),
TraceFlags: traceFlags,
TraceState: tsOtel,
})
}
45 changes: 35 additions & 10 deletions bridge/opencensus/internal/oc2otel/span_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,33 @@ package oc2otel
import (
"testing"

"github.com/stretchr/testify/assert"
"go.opencensus.io/plugin/ochttp/propagation/tracecontext"

"go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc"

octrace "go.opencensus.io/trace"
"go.opencensus.io/trace/tracestate"

"go.opentelemetry.io/otel/trace"
)

func TestSpanContextConversion(t *testing.T) {
tsOc, _ := tracestate.New(nil,
XSAM marked this conversation as resolved.
Show resolved Hide resolved
tracestate.Entry{Key: "key1", Value: "value1"},
tracestate.Entry{Key: "key2", Value: "value2"},
)
tsOtel := trace.TraceState{}
tsOtel, _ = tsOtel.Insert("key2", "value2")
tsOtel, _ = tsOtel.Insert("key1", "value1")

httpFormatOc := &tracecontext.HTTPFormat{}

for _, tc := range []struct {
description string
input octrace.SpanContext
expected trace.SpanContext
description string
input octrace.SpanContext
expected trace.SpanContext
expectedTracestate string
}{
{
description: "empty",
Expand Down Expand Up @@ -47,23 +63,32 @@ func TestSpanContextConversion(t *testing.T) {
}),
},
{
description: "trace state is ignored",
description: "trace state should be propagated",
input: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
Tracestate: &tracestate.Tracestate{},
Tracestate: tsOc,
},
expected: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceState: tsOtel,
}),
expectedTracestate: "key1=value1,key2=value2",
},
} {
t.Run(tc.description, func(t *testing.T) {
output := SpanContext(tc.input)
if !output.Equal(tc.expected) {
t.Fatalf("Got %+v spancontext, expected %+v.", output, tc.expected)
}
assert.Equal(t, tc.expected, output)

// Ensure the otel tracestate and oc tracestate has the same header output
_, ts := httpFormatOc.SpanContextToHeaders(tc.input)
assert.Equal(t, tc.expectedTracestate, ts)
assert.Equal(t, tc.expectedTracestate, tc.expected.TraceState().String())

// The reverse conversion should yield the original input
input := otel2oc.SpanContext(output)
assert.Equal(t, tc.input, input)
})
}
}
10 changes: 10 additions & 0 deletions bridge/opencensus/internal/otel2oc/span_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package otel2oc // import "go.opentelemetry.io/otel/bridge/opencensus/internal/o

import (
octrace "go.opencensus.io/trace"
"go.opencensus.io/trace/tracestate"

"go.opentelemetry.io/otel/trace"
)
Expand All @@ -15,9 +16,18 @@ func SpanContext(sc trace.SpanContext) octrace.SpanContext {
// OpenCensus doesn't expose functions to directly set sampled
to = 0x1
}

entries := make([]tracestate.Entry, 0, sc.TraceState().Len())
sc.TraceState().Walk(func(key, value string) bool {
entries = append(entries, tracestate.Entry{Key: key, Value: value})
return true
})
tsOc, _ := tracestate.New(nil, entries...)

return octrace.SpanContext{
TraceID: octrace.TraceID(sc.TraceID()),
SpanID: octrace.SpanID(sc.SpanID()),
TraceOptions: to,
Tracestate: tsOc,
}
}
54 changes: 48 additions & 6 deletions bridge/opencensus/internal/otel2oc/span_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,36 @@ package otel2oc
import (
"testing"

"go.opencensus.io/plugin/ochttp/propagation/tracecontext"

"go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel"

"github.com/stretchr/testify/assert"

"go.opencensus.io/trace/tracestate"

octrace "go.opencensus.io/trace"

"go.opentelemetry.io/otel/trace"
)

func TestSpanContextConversion(t *testing.T) {
tsOc, _ := tracestate.New(nil,
// Oc has a reverse order of TraceState entries compared to OTel
tracestate.Entry{Key: "key1", Value: "value1"},
tracestate.Entry{Key: "key2", Value: "value2"},
)
tsOtel := trace.TraceState{}
tsOtel, _ = tsOtel.Insert("key2", "value2")
tsOtel, _ = tsOtel.Insert("key1", "value1")

httpFormatOc := &tracecontext.HTTPFormat{}

for _, tc := range []struct {
description string
input trace.SpanContext
expected octrace.SpanContext
description string
input trace.SpanContext
expected octrace.SpanContext
expectedTracestate string
}{
{
description: "empty",
Expand Down Expand Up @@ -45,12 +65,34 @@ func TestSpanContextConversion(t *testing.T) {
TraceOptions: octrace.TraceOptions(0),
},
},
{
description: "trace state should be propagated",
input: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceState: tsOtel,
}),
expected: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0),
Tracestate: tsOc,
},
jianwu marked this conversation as resolved.
Show resolved Hide resolved
expectedTracestate: "key1=value1,key2=value2",
},
} {
t.Run(tc.description, func(t *testing.T) {
output := SpanContext(tc.input)
if output != tc.expected {
t.Fatalf("Got %+v spancontext, expected %+v.", output, tc.expected)
}
assert.Equal(t, tc.expected, output)
jianwu marked this conversation as resolved.
Show resolved Hide resolved

// Ensure the otel tracestate and oc tracestate has the same header output
_, ts := httpFormatOc.SpanContextToHeaders(tc.expected)
assert.Equal(t, tc.expectedTracestate, ts)
assert.Equal(t, tc.expectedTracestate, tc.input.TraceState().String())

// The reverse conversion should yield the original input
input := oc2otel.SpanContext(output)
assert.Equal(t, tc.input, input)
})
}
}
10 changes: 10 additions & 0 deletions trace/tracestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,16 @@ func (ts TraceState) Get(key string) string {
return ""
}

// Walk walks all key value pairs in the TraceState by calling f
// Iteration stops if f returns false.
func (ts TraceState) Walk(f func(key, value string) bool) {
for _, m := range ts.list {
if !f(m.Key, m.Value) {
break
jianwu marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

// Insert adds a new list-member defined by the key/value pair to the
// TraceState. If a list-member already exists for the given key, that
// list-member's value is updated. The new or updated list-member is always
Expand Down
46 changes: 46 additions & 0 deletions trace/tracestate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,52 @@ func TestTraceStateDelete(t *testing.T) {
}
}

func TestTraceStateWalk(t *testing.T) {
testCases := []struct {
name string
tracestate TraceState
num int
expected [][]string
}{
{
name: "With keys",
tracestate: TraceState{list: []member{
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
}},
num: 3,
expected: [][]string{{"key1", "val1"}, {"key2", "val2"}},
},
{
name: "With keys walk partially",
tracestate: TraceState{list: []member{
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
}},
num: 1,
expected: [][]string{{"key1", "val1"}},
},

{
name: "Without keys",
tracestate: TraceState{list: []member{}},
num: 2,
expected: [][]string{},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := [][]string{}
tc.tracestate.Walk(func(key, value string) bool {
got = append(got, []string{key, value})
return len(got) < tc.num
})
assert.Equal(t, tc.expected, got)
})
}
}

var insertTS = TraceState{list: []member{
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
Expand Down
Loading