diff --git a/sampler/coralogix-sampler.go b/sampler/coralogix-sampler.go index db4d668..037706a 100644 --- a/sampler/coralogix-sampler.go +++ b/sampler/coralogix-sampler.go @@ -3,7 +3,9 @@ package sampler import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" traceSdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" traceCore "go.opentelemetry.io/otel/trace" ) @@ -13,19 +15,40 @@ const ( TransactionIdentifierTraceState = "cgx_transaction" DistributedTransactionIdentifier = "cgx.transaction.distributed" DistributedTransactionIdentifierTraceState = "cgx_transaction_distributed" + TransactionServiceIdentifier = "cgx.transaction" + TransactionServiceIdentifierTraceState = "cgx_transaction_service" ) type CoralogixSampler struct { adaptedSampler traceSdk.Sampler + service string } -func NewCoralogixSampler(adaptedSampler traceSdk.Sampler) CoralogixSampler { +func NewCoralogixSampler(adaptedSampler traceSdk.Sampler, resa *resource.Resource) CoralogixSampler { + if adaptedSampler == nil { panic("sampler is null") } + if resa == nil { + panic("resource is null") + } + attributes := resa.Attributes() + service := "" + if attributes != nil { + for _, attribute := range attributes { + if attribute.Key == semconv.ServiceNameKey { + service = attribute.Value.AsString() + } + } + } + if service == "" { + panic("service name is empty") + } return CoralogixSampler{ adaptedSampler: adaptedSampler, + service: service, } + } func (s CoralogixSampler) Description() string { return "coralogix-sampler" @@ -34,11 +57,11 @@ func (s CoralogixSampler) Description() string { func (s CoralogixSampler) ShouldSample(parameters traceSdk.SamplingParameters) traceSdk.SamplingResult { adaptedSamplingResult := s.adaptedSampler.ShouldSample(parameters) - return s.generateTransactionSamplingResult(parameters.ParentContext, parameters.Name, adaptedSamplingResult) + return s.generateTransactionSamplingResult(parameters.ParentContext, parameters.Name, adaptedSamplingResult, parameters.Kind) } -func (s CoralogixSampler) generateTransactionSamplingResult(ctx context.Context, name string, adaptedSamplingResult traceSdk.SamplingResult) traceSdk.SamplingResult { - newTracingState := s.generateNewTraceState(ctx, name, adaptedSamplingResult) +func (s CoralogixSampler) generateTransactionSamplingResult(ctx context.Context, name string, adaptedSamplingResult traceSdk.SamplingResult, kind traceCore.SpanKind) traceSdk.SamplingResult { + newTracingState := s.generateNewTraceState(ctx, name, adaptedSamplingResult, kind) newAttributes := s.injectAttributes(adaptedSamplingResult, newTracingState, name) return traceSdk.SamplingResult{ Decision: adaptedSamplingResult.Decision, @@ -65,11 +88,11 @@ func (s *CoralogixSampler) getDescription() string { return "coralogix-sampler" } -func (s *CoralogixSampler) generateNewTraceState(ctx context.Context, name string, samplingResult traceSdk.SamplingResult) traceCore.TraceState { +func (s *CoralogixSampler) generateNewTraceState(ctx context.Context, name string, samplingResult traceSdk.SamplingResult, kind traceCore.SpanKind) traceCore.TraceState { parentSpanContext := s.getParentSpanContext(ctx) parentTraceState := samplingResult.Tracestate - if !parentSpanContext.IsRemote() && parentTraceState.Get(TransactionIdentifierTraceState) != "" { + if !(kind == traceCore.SpanKindConsumer) && !(kind == traceCore.SpanKindServer) && !parentSpanContext.IsRemote() && parentTraceState.Get(TransactionIdentifierTraceState) != "" && parentTraceState.Get(TransactionServiceIdentifierTraceState) == s.service { return parentTraceState } @@ -77,6 +100,10 @@ func (s *CoralogixSampler) generateNewTraceState(ctx context.Context, name strin if err != nil { return parentTraceState } + parentTraceState, err = parentTraceState.Insert(TransactionServiceIdentifierTraceState, s.service) + if err != nil { + return parentTraceState + } if parentTraceState.Get(DistributedTransactionIdentifierTraceState) == "" { parentTraceState, err = parentTraceState.Insert(DistributedTransactionIdentifierTraceState, name) if err != nil { diff --git a/sampler/coralogix-sampler_test.go b/sampler/coralogix-sampler_test.go index eb87be8..2408059 100644 --- a/sampler/coralogix-sampler_test.go +++ b/sampler/coralogix-sampler_test.go @@ -3,7 +3,9 @@ package sampler import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" traceSdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" "testing" ) @@ -13,13 +15,18 @@ import ( ) const ( - spanName = "spanName" + spanName = "spanName" + serviceNme = "serviceName" ) func TestCoralogixSampler_ShouldSample(t *testing.T) { t.Run("When_alwaysSampler_Should_AppendAttributesAndState", func(t *testing.T) { + resource := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + alwaysSampler := traceSdk.AlwaysSample() - coralogixSampler := NewCoralogixSampler(alwaysSampler) + coralogixSampler := NewCoralogixSampler(alwaysSampler, resource) // Act parameters := traceSdk.SamplingParameters{ @@ -38,6 +45,7 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { expectedTraceState := traceCore.TraceState{} expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, spanName) assert.Equal(t, traceSdk.RecordAndSample, result.Decision) @@ -47,7 +55,10 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { t.Run("When_NeverSample_Should_AppendAttributesAndState", func(t *testing.T) { neverSampler := traceSdk.NeverSample() - coralogixSampler := NewCoralogixSampler(neverSampler) + resource := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(neverSampler, resource) // Act parameters := traceSdk.SamplingParameters{ @@ -66,6 +77,7 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { expectedTraceState := traceCore.TraceState{} expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, spanName) assert.Equal(t, traceSdk.Drop, result.Decision) @@ -75,17 +87,24 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { t.Run("When_CustomSamplerIsNull_ShouldFailInit", func(t *testing.T) { assert.Panics(t, func() { - _ = NewCoralogixSampler(nil) + resource := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + _ = NewCoralogixSampler(nil, resource) }) }) t.Run("When_ParentContextExistsAndNotRemote_ShouldCopyParentTraceState", func(t *testing.T) { alwaysSampler := traceSdk.AlwaysSample() - coralogixSampler := NewCoralogixSampler(alwaysSampler) + ras := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(alwaysSampler, ras) traceState := traceCore.TraceState{} traceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) traceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") parentSpan := traceCore.NewSpanContext(traceCore.SpanContextConfig{ @@ -110,19 +129,24 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { attribute.String(DistributedTransactionIdentifier, "fatherSpanName"), } expectedTraceState := traceCore.TraceState{} - expectedTraceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") - expectedTraceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) + expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") assert.ElementsMatch(t, expectedAttributes, result.Attributes) assert.Equal(t, expectedTraceState, result.Tracestate) }) t.Run("When_ParentContextExistsAndRemote_ShouldCopyParentTraceState", func(t *testing.T) { alwaysSampler := traceSdk.AlwaysSample() - coralogixSampler := NewCoralogixSampler(alwaysSampler) + ras := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(alwaysSampler, ras) traceState := traceCore.TraceState{} traceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) traceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") parentSpan := traceCore.NewSpanContext(traceCore.SpanContextConfig{ @@ -150,6 +174,7 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { expectedTraceState := traceCore.TraceState{} expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) assert.ElementsMatch(t, expectedAttributes, result.Attributes) assert.Equal(t, expectedTraceState.Get(TransactionIdentifierTraceState), result.Tracestate.Get(TransactionIdentifierTraceState)) @@ -158,12 +183,60 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { t.Run("When_ParentContextExistsAndRemote_ShouldCopyParentTraceState", func(t *testing.T) { alwaysSampler := traceSdk.AlwaysSample() - coralogixSampler := NewCoralogixSampler(alwaysSampler) + ras := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(alwaysSampler, ras) + + traceState := traceCore.TraceState{} + + traceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + + parentSpan := traceCore.NewSpanContext(traceCore.SpanContextConfig{ + TraceID: traceCore.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: traceCore.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, + TraceFlags: traceCore.FlagsSampled, + TraceState: traceState, + Remote: true, + }) + parentCtx := traceCore.ContextWithSpanContext(context.Background(), parentSpan) + + // Act + parameters := traceSdk.SamplingParameters{ + ParentContext: parentCtx, + Name: spanName, + Attributes: []attribute.KeyValue{}, + } + result := coralogixSampler.ShouldSample(parameters) + + expectedAttributes := []attribute.KeyValue{ + attribute.String(TransactionIdentifier, "spanName"), + attribute.String(DistributedTransactionIdentifier, "fatherSpanName"), + attribute.Bool(TransactionIdentifierRoot, true), + } + expectedTraceState := traceCore.TraceState{} + expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) + expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) + + assert.ElementsMatch(t, expectedAttributes, result.Attributes) + assert.Equal(t, expectedTraceState.Get(TransactionIdentifierTraceState), result.Tracestate.Get(TransactionIdentifierTraceState)) + assert.Equal(t, expectedTraceState.Get(DistributedTransactionIdentifierTraceState), result.Tracestate.Get(DistributedTransactionIdentifierTraceState)) + }) + + t.Run("When_ParentContextExistsAndNotRemoteDiffrentSerrice_ShouldNotCopyParentTraceState", func(t *testing.T) { + alwaysSampler := traceSdk.AlwaysSample() + ras := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(alwaysSampler, ras) traceState := traceCore.TraceState{} traceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") traceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(TransactionServiceIdentifierTraceState, "parentServiceName") parentSpan := traceCore.NewSpanContext(traceCore.SpanContextConfig{ TraceID: traceCore.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, @@ -190,9 +263,56 @@ func TestCoralogixSampler_ShouldSample(t *testing.T) { expectedTraceState := traceCore.TraceState{} expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) assert.ElementsMatch(t, expectedAttributes, result.Attributes) assert.Equal(t, expectedTraceState.Get(TransactionIdentifierTraceState), result.Tracestate.Get(TransactionIdentifierTraceState)) assert.Equal(t, expectedTraceState.Get(DistributedTransactionIdentifierTraceState), result.Tracestate.Get(DistributedTransactionIdentifierTraceState)) }) + + t.Run("When_ParentContextExistsAndNotRemoteSameService_ShouldNotCopyParentTraceState", func(t *testing.T) { + alwaysSampler := traceSdk.AlwaysSample() + ras := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceNme)) + coralogixSampler := NewCoralogixSampler(alwaysSampler, ras) + + traceState := traceCore.TraceState{} + + traceState, _ = traceState.Insert(TransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + traceState, _ = traceState.Insert(TransactionServiceIdentifierTraceState, "parentServiceName") + + parentSpan := traceCore.NewSpanContext(traceCore.SpanContextConfig{ + TraceID: traceCore.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: traceCore.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, + TraceFlags: traceCore.FlagsSampled, + TraceState: traceState, + Remote: false, + }) + parentCtx := traceCore.ContextWithSpanContext(context.Background(), parentSpan) + + // Act + parameters := traceSdk.SamplingParameters{ + ParentContext: parentCtx, + Name: spanName, + Attributes: []attribute.KeyValue{}, + } + result := coralogixSampler.ShouldSample(parameters) + + expectedAttributes := []attribute.KeyValue{ + attribute.String(TransactionIdentifier, "spanName"), + attribute.String(DistributedTransactionIdentifier, "fatherSpanName"), + attribute.Bool(TransactionIdentifierRoot, true), + } + expectedTraceState := traceCore.TraceState{} + expectedTraceState, _ = expectedTraceState.Insert(TransactionIdentifierTraceState, spanName) + expectedTraceState, _ = expectedTraceState.Insert(DistributedTransactionIdentifierTraceState, "fatherSpanName") + expectedTraceState, _ = expectedTraceState.Insert(TransactionServiceIdentifierTraceState, serviceNme) + + assert.ElementsMatch(t, expectedAttributes, result.Attributes) + assert.Equal(t, expectedTraceState.Get(TransactionIdentifierTraceState), result.Tracestate.Get(TransactionIdentifierTraceState)) + assert.Equal(t, expectedTraceState.Get(DistributedTransactionIdentifierTraceState), result.Tracestate.Get(DistributedTransactionIdentifierTraceState)) + }) + }