Skip to content

Commit e9b9aca

Browse files
evantorrieMrAlias
andauthored
Add tests for propagation of Sampler Tracestate changes (#1655)
* Add tests for propagation of Sampler Tracestate changes Sampler specification indicates that SamplingResult.Tracestate should be associated with the SpanContext of the newly created span. See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampler * Fix SamplingResult TraceState propagation * Add Changelog entry Co-authored-by: Tyler Yahn <[email protected]>
1 parent 875a258 commit e9b9aca

File tree

3 files changed

+159
-1
lines changed

3 files changed

+159
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1616
- Removed the exported `SimpleSpanProcessor` and `BatchSpanProcessor` structs.
1717
These are now returned as a SpanProcessor interface from their respective constructors. (#1638)
1818

19+
### Fixed
20+
21+
- `SamplingResult.TraceState` is correctly propagated to a newly created
22+
span's `SpanContext`. (#1655)
23+
1924
## [0.18.0] - 2020-03-03
2025

2126
### Added
@@ -41,7 +46,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
4146
### Changed
4247

4348
- Replaced interface `oteltest.SpanRecorder` with its existing implementation
44-
`StandardSpanRecorder` (#1542).
49+
`StandardSpanRecorder`. (#1542)
4550
- Default span limit values to 128. (#1535)
4651
- Rename `MaxEventsPerSpan`, `MaxAttributesPerSpan` and `MaxLinksPerSpan` to `EventCountLimit`, `AttributeCountLimit` and `LinkCountLimit`, and move these fields into `SpanLimits`. (#1535)
4752
- Renamed the `otel/label` package to `otel/attribute`. (#1541)

sdk/trace/span.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, parent trac
543543
kind: o.SpanKind,
544544
}
545545
sampled := makeSamplingDecision(data)
546+
span.spanContext.TraceState = sampled.Tracestate
546547

547548
if !span.spanContext.IsSampled() && !o.Record {
548549
return span

sdk/trace/trace_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,22 @@ var (
5858
sid trace.SpanID
5959

6060
handler *storingHandler = &storingHandler{}
61+
62+
k1, k2, k3 attribute.Key
63+
kv1, kv2, kv3 attribute.KeyValue
6164
)
6265

6366
func init() {
6467
tid, _ = trace.TraceIDFromHex("01020304050607080102040810203040")
6568
sid, _ = trace.SpanIDFromHex("0102040810203040")
6669

70+
k1 = attribute.Key("k1")
71+
kv1 = k1.String("v1")
72+
k2 = attribute.Key("k2")
73+
kv2 = k2.String("v2")
74+
k3 = attribute.Key("k3")
75+
kv3 = k3.String("v3")
76+
6777
otel.SetErrorHandler(handler)
6878
}
6979

@@ -1526,3 +1536,145 @@ func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) {
15261536
t.Errorf("Link: -got +want %s", diff)
15271537
}
15281538
}
1539+
1540+
type stateSampler struct {
1541+
prefix string
1542+
f func(trace.TraceState) trace.TraceState
1543+
}
1544+
1545+
func (s *stateSampler) ShouldSample(p SamplingParameters) SamplingResult {
1546+
decision := Drop
1547+
if strings.HasPrefix(p.Name, s.prefix) {
1548+
decision = RecordAndSample
1549+
}
1550+
return SamplingResult{Decision: decision, Tracestate: s.f(p.ParentContext.TraceState)}
1551+
}
1552+
1553+
func (s stateSampler) Description() string {
1554+
return "stateSampler"
1555+
}
1556+
1557+
// Check that a new span propagates the SamplerResult.TraceState
1558+
func TestSamplerTraceState(t *testing.T) {
1559+
mustTS := func(t trace.TraceState, err error) trace.TraceState { return t }
1560+
makeInserter := func(k attribute.KeyValue, prefix string) Sampler {
1561+
return &stateSampler{
1562+
prefix: prefix,
1563+
f: func(t trace.TraceState) trace.TraceState { return mustTS(t.Insert(k)) },
1564+
}
1565+
}
1566+
makeDeleter := func(k attribute.Key, prefix string) Sampler {
1567+
return &stateSampler{
1568+
prefix: prefix,
1569+
f: func(t trace.TraceState) trace.TraceState { return mustTS(t.Delete(k)) },
1570+
}
1571+
}
1572+
clearer := func(prefix string) Sampler {
1573+
return &stateSampler{
1574+
prefix: prefix,
1575+
f: func(t trace.TraceState) trace.TraceState { return trace.TraceState{} },
1576+
}
1577+
}
1578+
1579+
tests := []struct {
1580+
name string
1581+
sampler Sampler
1582+
spanName string
1583+
input trace.TraceState
1584+
want trace.TraceState
1585+
exportSpan bool
1586+
}{
1587+
{
1588+
name: "alwaysOn",
1589+
sampler: AlwaysSample(),
1590+
input: mustTS(trace.TraceStateFromKeyValues(kv1)),
1591+
want: mustTS(trace.TraceStateFromKeyValues(kv1)),
1592+
exportSpan: true,
1593+
},
1594+
{
1595+
name: "alwaysOff",
1596+
sampler: NeverSample(),
1597+
input: mustTS(trace.TraceStateFromKeyValues(kv1)),
1598+
want: mustTS(trace.TraceStateFromKeyValues(kv1)),
1599+
exportSpan: false,
1600+
},
1601+
{
1602+
name: "insertKeySampled",
1603+
sampler: makeInserter(kv2, "span"),
1604+
spanName: "span0",
1605+
input: mustTS(trace.TraceStateFromKeyValues(kv1)),
1606+
want: mustTS(trace.TraceStateFromKeyValues(kv2, kv1)),
1607+
exportSpan: true,
1608+
},
1609+
{
1610+
name: "insertKeyDropped",
1611+
sampler: makeInserter(kv2, "span"),
1612+
spanName: "nospan0",
1613+
input: mustTS(trace.TraceStateFromKeyValues(kv1)),
1614+
want: mustTS(trace.TraceStateFromKeyValues(kv2, kv1)),
1615+
exportSpan: false,
1616+
},
1617+
{
1618+
name: "deleteKeySampled",
1619+
sampler: makeDeleter(k1, "span"),
1620+
spanName: "span0",
1621+
input: mustTS(trace.TraceStateFromKeyValues(kv1, kv2)),
1622+
want: mustTS(trace.TraceStateFromKeyValues(kv2)),
1623+
exportSpan: true,
1624+
},
1625+
{
1626+
name: "deleteKeyDropped",
1627+
sampler: makeDeleter(k1, "span"),
1628+
spanName: "nospan0",
1629+
input: mustTS(trace.TraceStateFromKeyValues(kv1, kv2, kv3)),
1630+
want: mustTS(trace.TraceStateFromKeyValues(kv2, kv3)),
1631+
exportSpan: false,
1632+
},
1633+
{
1634+
name: "clearer",
1635+
sampler: clearer("span"),
1636+
spanName: "span0",
1637+
input: mustTS(trace.TraceStateFromKeyValues(kv1, kv3)),
1638+
want: mustTS(trace.TraceStateFromKeyValues()),
1639+
exportSpan: true,
1640+
},
1641+
}
1642+
1643+
for _, ts := range tests {
1644+
ts := ts
1645+
t.Run(ts.name, func(t *testing.T) {
1646+
te := NewTestExporter()
1647+
tp := NewTracerProvider(WithConfig(Config{DefaultSampler: ts.sampler}), WithSyncer(te), WithResource(resource.Empty()))
1648+
tr := tp.Tracer("TraceState")
1649+
1650+
sc1 := trace.SpanContext{
1651+
TraceID: tid,
1652+
SpanID: sid,
1653+
TraceFlags: trace.FlagsSampled,
1654+
TraceState: ts.input,
1655+
}
1656+
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc1)
1657+
_, span := tr.Start(ctx, ts.spanName)
1658+
1659+
// span's TraceState should be set regardless of Sampled/NonSampled state.
1660+
require.Equal(t, ts.want, span.SpanContext().TraceState)
1661+
1662+
span.End()
1663+
1664+
got := te.Spans()
1665+
if len(got) > 0 != ts.exportSpan {
1666+
t.Errorf("unexpected number of exported spans %d", len(got))
1667+
}
1668+
if len(got) == 0 {
1669+
return
1670+
}
1671+
1672+
receivedState := got[0].SpanContext.TraceState
1673+
1674+
if diff := cmpDiff(receivedState, ts.want); diff != "" {
1675+
t.Errorf("TraceState not propagated: -got +want %s", diff)
1676+
}
1677+
})
1678+
}
1679+
1680+
}

0 commit comments

Comments
 (0)