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

feat: implement Span Links API #1269

Merged
merged 9 commits into from
Jul 22, 2022
41 changes: 41 additions & 0 deletions model/marshal_fastjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ type Transaction struct {

// FAAS holds Function-as-a-Service properties for the transaction.
FAAS *FAAS `json:"faas,omitempty"`

// Links holds a list of spans linked to the transaction.
Links []SpanLink `json:"links,omitempty"`
}

// OTel holds bridged OpenTelemetry information.
Expand Down Expand Up @@ -379,6 +382,9 @@ type Span struct {
// aggregated set of spans as defined by `composite.compression_strategy`.
Composite *CompositeSpan `json:"composite,omitempty"`

// Links holds a list of spans linked to the span.
Links []SpanLink `json:"links,omitempty"`

// OTel holds information bridged from OpenTelemetry.
OTel *OTel `json:"otel,omitempty"`
}
Expand All @@ -402,6 +408,12 @@ type SpanContext struct {
Tags IfaceMap `json:"tags,omitempty"`
}

// SpanLink holds the information of a linked span.
type SpanLink struct {
TraceID TraceID `json:"trace_id"`
SpanID SpanID `json:"span_id"`
}

// DestinationSpanContext holds contextual information about the destination
// for a span that relates to an operation involving an external service.
type DestinationSpanContext struct {
Expand Down
6 changes: 6 additions & 0 deletions modelwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ func (w *modelWriter) buildModelTransaction(out *model.Transaction, tx *Transact
out.SpanCount.Started = td.spansCreated
out.SpanCount.Dropped = td.spansDropped
out.OTel = td.Context.otel
for _, sl := range td.links {
out.Links = append(out.Links, model.SpanLink{TraceID: model.TraceID(sl.Trace), SpanID: model.SpanID(sl.Span)})
}
if dss := buildDroppedSpansStats(td.droppedSpansStats); len(dss) > 0 {
out.DroppedSpansStats = dss
}
Expand Down Expand Up @@ -152,6 +155,9 @@ func (w *modelWriter) buildModelSpan(out *model.Span, span *Span, sd *SpanData)
out.Outcome = normalizeOutcome(sd.Outcome)
out.Context = sd.Context.build()
out.OTel = sd.Context.otel
for _, sl := range sd.links {
out.Links = append(out.Links, model.SpanLink{TraceID: model.TraceID(sl.Trace), SpanID: model.SpanID(sl.Span)})
}
if sd.composite.count > 1 {
out.Composite = sd.composite.build()
}
Expand Down
6 changes: 6 additions & 0 deletions span.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ type SpanOptions struct {
// transaction timestamp. Calculating the timstamp in this way will ensure
// monotonicity of events within a transaction.
Start time.Time

// Links, if non-nil, holds a list of spans linked to the span.
Links []SpanLink
}

func (t *Tracer) startSpan(name, spanType string, transactionID SpanID, opts SpanOptions) *Span {
Expand All @@ -233,6 +236,7 @@ func (t *Tracer) startSpan(name, spanType string, transactionID SpanID, opts Spa
span.transactionID = transactionID
span.timestamp = opts.Start
span.Type = spanType
span.links = opts.Links
if dot := strings.IndexRune(spanType, '.'); dot != -1 {
span.Type = spanType[:dot]
span.Subtype = spanType[dot+1:]
Expand Down Expand Up @@ -578,6 +582,8 @@ type SpanData struct {
// Context describes the context in which span occurs.
Context SpanContext

links []SpanLink

mu sync.Mutex
stacktrace []stacktrace.Frame
errorCaptured bool
Expand Down
31 changes: 31 additions & 0 deletions span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,37 @@ func TestSpanParentID(t *testing.T) {
assert.Equal(t, model.SpanID(parentID), payloads.Spans[0].ParentID)
}

func TestSpanLink(t *testing.T) {
kruskall marked this conversation as resolved.
Show resolved Hide resolved
tracer := apmtest.NewRecordingTracer()
defer tracer.Close()

links := []apm.SpanLink{
{Trace: apm.TraceID{1}, Span: apm.SpanID{1}},
{Trace: apm.TraceID{2}, Span: apm.SpanID{2}},
}

tx := tracer.StartTransaction("name", "type")
span := tx.StartSpanOptions("name", "type", apm.SpanOptions{
Links: links,
})

span.End()
tx.End()

tracer.Flush(nil)

payloads := tracer.Payloads()
require.Len(t, payloads.Spans, 1)
require.Len(t, payloads.Spans[0].Links, len(links))

// Assert span links are identical.
expectedLinks := []model.SpanLink{
{TraceID: model.TraceID{1}, SpanID: model.SpanID{1}},
{TraceID: model.TraceID{2}, SpanID: model.SpanID{2}},
}
assert.Equal(t, expectedLinks, payloads.Spans[0].Links)
}

func TestSpanTiming(t *testing.T) {
var spanStart, spanEnd time.Time
txStart := time.Now()
Expand Down
6 changes: 6 additions & 0 deletions tracecontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ func (id SpanID) MarshalText() ([]byte, error) {
return text, nil
}

// SpanLink describes a linked span.
type SpanLink struct {
Trace TraceID
Span SpanID
}

// TraceOptions describes the options for a trace.
type TraceOptions uint8

Expand Down
5 changes: 5 additions & 0 deletions transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ func (t *Tracer) StartTransactionOptions(name, transactionType string, opts Tran
if tx.timestamp.IsZero() {
tx.timestamp = time.Now()
}
tx.links = opts.Links
return tx
}

Expand All @@ -163,6 +164,9 @@ type TransactionOptions struct {
// Start is the start time of the transaction. If this has the
// zero value, time.Now() will be used instead.
Start time.Time

// Links, if non-nil, holds a list of spans linked to the transaction.
Links []SpanLink
}

// Transaction describes an event occurring in the monitored service.
Expand Down Expand Up @@ -374,6 +378,7 @@ type TransactionData struct {
propagateLegacyHeader bool
timestamp time.Time

links []SpanLink
mu sync.Mutex
errorCaptured bool
spansCreated int
Expand Down
25 changes: 25 additions & 0 deletions transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,31 @@ func TestTransactionSampleRateOmission(t *testing.T) {
}
}

func TestTransactionSpanLink(t *testing.T) {
kruskall marked this conversation as resolved.
Show resolved Hide resolved
tracer := apmtest.NewRecordingTracer()
defer tracer.Close()

links := []apm.SpanLink{
{Trace: apm.TraceID{1}, Span: apm.SpanID{1}},
{Trace: apm.TraceID{2}, Span: apm.SpanID{2}},
}

tx := tracer.StartTransactionOptions("name", "type", apm.TransactionOptions{Links: links})
tx.End()

tracer.Flush(nil)

payloads := tracer.Payloads()
assert.Len(t, payloads.Transactions, 1)

// Assert span links are identical.
expectedLinks := []model.SpanLink{
{TraceID: model.TraceID{1}, SpanID: model.SpanID{1}},
{TraceID: model.TraceID{2}, SpanID: model.SpanID{2}},
}
assert.Equal(t, expectedLinks, payloads.Transactions[0].Links)
}

func TestTransactionDiscard(t *testing.T) {
tracer, transport := transporttest.NewRecorderTracer()
defer tracer.Close()
Expand Down