diff --git a/CHANGELOG.md b/CHANGELOG.md index 1914e7ba0eb..1ff9c42a0a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369) - Unify endpoint API that related to OTel exporter. (#1401) - Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430) +- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432) ### Removed diff --git a/sdk/trace/sampling.go b/sdk/trace/sampling.go index e66dd65ec0c..c42465c41e8 100644 --- a/sdk/trace/sampling.go +++ b/sdk/trace/sampling.go @@ -56,10 +56,11 @@ const ( RecordAndSample ) -// SamplingResult conveys a SamplingDecision and a set of Attributes. +// SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate. type SamplingResult struct { Decision SamplingDecision Attributes []label.KeyValue + Tracestate trace.TraceState } type traceIDRatioSampler struct { @@ -70,9 +71,15 @@ type traceIDRatioSampler struct { func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult { x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1 if x < ts.traceIDUpperBound { - return SamplingResult{Decision: RecordAndSample} + return SamplingResult{ + Decision: RecordAndSample, + Tracestate: p.ParentContext.TraceState, + } + } + return SamplingResult{ + Decision: Drop, + Tracestate: p.ParentContext.TraceState, } - return SamplingResult{Decision: Drop} } func (ts traceIDRatioSampler) Description() string { @@ -102,7 +109,10 @@ func TraceIDRatioBased(fraction float64) Sampler { type alwaysOnSampler struct{} func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult { - return SamplingResult{Decision: RecordAndSample} + return SamplingResult{ + Decision: RecordAndSample, + Tracestate: p.ParentContext.TraceState, + } } func (as alwaysOnSampler) Description() string { @@ -120,7 +130,10 @@ func AlwaysSample() Sampler { type alwaysOffSampler struct{} func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult { - return SamplingResult{Decision: Drop} + return SamplingResult{ + Decision: Drop, + Tracestate: p.ParentContext.TraceState, + } } func (as alwaysOffSampler) Description() string { diff --git a/sdk/trace/sampling_test.go b/sdk/trace/sampling_test.go index 6ae4afff390..c8cde14c72d 100644 --- a/sdk/trace/sampling_test.go +++ b/sdk/trace/sampling_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -189,3 +190,47 @@ func TestTraceIdRatioSamplesInclusively(t *testing.T) { } } } + +func TestTracestateIsPassed(t *testing.T) { + testCases := []struct { + name string + sampler Sampler + }{ + { + "notSampled", + NeverSample(), + }, + { + "sampled", + AlwaysSample(), + }, + { + "parentSampled", + ParentBased(AlwaysSample()), + }, + { + "parentNotSampled", + ParentBased(NeverSample()), + }, + { + "traceIDRatioSampler", + TraceIDRatioBased(.5), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + traceState, err := trace.TraceStateFromKeyValues(label.String("k", "v")) + if err != nil { + t.Error(err) + } + + parentCtx := trace.SpanContext{ + TraceState: traceState, + } + params := SamplingParameters{ParentContext: parentCtx} + + require.Equal(t, traceState, tc.sampler.ShouldSample(params).Tracestate, "TraceState is not equal") + }) + } +}