diff --git a/span.go b/span.go index 5b800f79d..dc77a91e4 100644 --- a/span.go +++ b/span.go @@ -65,7 +65,7 @@ func (tx *Transaction) StartSpan(name, spanType string, parent *Span) *Span { // span type, subtype, and action; a single dot separates span type and // subtype, and the action will not be set. func (tx *Transaction) StartSpanOptions(name, spanType string, opts SpanOptions) *Span { - if tx == nil || opts.parent.IsExitSpan() { + if tx == nil { return newDroppedSpan() } @@ -345,6 +345,16 @@ func (s *Span) End() { if s.Type == "" { s.Type = "custom" } + if s.parent.IsExitSpan() { + s.Context.model.Destination = nil + s.Context.model.Service = nil + + if s.Type != s.parent.Type || s.Subtype != s.parent.Subtype { + s.dropWhen(true) + s.end() + return + } + } if s.exit && !s.Context.setDestinationServiceCalled { // The span was created as an exit span, but the user did not // manually set the destination.service.resource diff --git a/span_test.go b/span_test.go index a08ba13da..aa74adf9b 100644 --- a/span_test.go +++ b/span_test.go @@ -235,13 +235,22 @@ func TestStartExitSpan(t *testing.T) { defer tracer.Close() tx := tracer.StartTransaction("name", "type") + span := tx.StartSpanOptions("name", "type", apm.SpanOptions{ExitSpan: true}) assert.True(t, span.IsExitSpan()) + // when the parent span is an exit span, any children should be noops. - span2 := tx.StartSpan("name", "type", span) + span2 := tx.StartSpan("name", "differenttype", span) + span2.End() assert.True(t, span2.Dropped()) + + // Exit spans MAY have child spans that have the same `type` and `subtype`. + span3 := tx.StartSpan("name", "type", span) + span3.End() + assert.False(t, span3.Dropped()) + span.End() - span2.End() + // Spans should still be marked as an exit span after they've been // ended. assert.True(t, span.IsExitSpan())