From 57cdb6edf9726ae24e26b5550e20498156924554 Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 17 Nov 2023 12:30:51 -0600 Subject: [PATCH] Always Link Transaction IDs to Traces --- v3/internal/expect.go | 1 + v3/newrelic/errors_from_internal.go | 2 + v3/newrelic/errors_test.go | 85 ++++++++++++++++++++++++----- v3/newrelic/harvest_test.go | 3 +- v3/newrelic/internal_txn.go | 10 ++-- v3/newrelic/slow_queries_test.go | 1 + v3/newrelic/tracing.go | 11 ++++ v3/newrelic/txn_trace.go | 3 + 8 files changed, 97 insertions(+), 19 deletions(-) diff --git a/v3/internal/expect.go b/v3/internal/expect.go index 2638225fd..edb92bdc2 100644 --- a/v3/internal/expect.go +++ b/v3/internal/expect.go @@ -22,6 +22,7 @@ type WantError struct { TxnName string Msg string Klass string + GUID string UserAttributes map[string]interface{} AgentAttributes map[string]interface{} } diff --git a/v3/newrelic/errors_from_internal.go b/v3/newrelic/errors_from_internal.go index cf8161303..fdb439366 100644 --- a/v3/newrelic/errors_from_internal.go +++ b/v3/newrelic/errors_from_internal.go @@ -124,6 +124,8 @@ func (h *tracedError) WriteJSON(buf *bytes.Buffer) { h.Stack.WriteJSON(buf) } buf.WriteByte('}') + buf.WriteByte(',') + jsonx.AppendString(buf, h.txnEvent.TxnID) buf.WriteByte(']') } diff --git a/v3/newrelic/errors_test.go b/v3/newrelic/errors_test.go index 783306d18..6766276e9 100644 --- a/v3/newrelic/errors_test.go +++ b/v3/newrelic/errors_test.go @@ -27,6 +27,48 @@ func testExpectedJSON(t testing.TB, expect string, actual string) { } } +func TestErrorNoCAT(t *testing.T) { + he := &tracedError{ + errorData: errorData{ + When: time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC), + Stack: emptyStackTrace, + Msg: "my_msg", + Klass: "my_class", + }, + txnEvent: txnEvent{ + FinalName: "my_txn_name", + Attrs: nil, + TxnID: "txn-guid-id", + BetterCAT: betterCAT{ + Enabled: false, + }, + TotalTime: 2 * time.Second, + }, + } + js, err := json.Marshal(he) + if nil != err { + t.Error(err) + } + + expect := ` + [ + 1.41713646e+12, + "my_txn_name", + "my_msg", + "my_class", + { + "agentAttributes":{}, + "userAttributes":{}, + "intrinsics":{ + "totalTime":2 + }, + "stack_trace":[] + }, + "txn-guid-id" + ]` + testExpectedJSON(t, expect, string(js)) +} + func TestErrorTraceMarshal(t *testing.T) { he := &tracedError{ errorData: errorData{ @@ -38,6 +80,7 @@ func TestErrorTraceMarshal(t *testing.T) { txnEvent: txnEvent{ FinalName: "my_txn_name", Attrs: nil, + TxnID: "txn-guid-id", BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", @@ -69,7 +112,8 @@ func TestErrorTraceMarshal(t *testing.T) { "sampled":false }, "stack_trace":[] - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -89,6 +133,7 @@ func TestErrorTraceMarshalOldCAT(t *testing.T) { Enabled: false, }, TotalTime: 2 * time.Second, + TxnID: "txn-guid-id", }, } js, err := json.Marshal(he) @@ -109,7 +154,8 @@ func TestErrorTraceMarshalOldCAT(t *testing.T) { "totalTime":2 }, "stack_trace":[] - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -135,11 +181,13 @@ func TestErrorTraceAttributes(t *testing.T) { txnEvent: txnEvent{ FinalName: "my_txn_name", Attrs: attr, + TxnID: "txn-id", + BetterCAT: betterCAT{ Enabled: true, - TxnID: "txn-id", Priority: 0.5, TraceID: "trace-id", + TxnID: "txn-id", }, TotalTime: 2 * time.Second, }, @@ -164,7 +212,8 @@ func TestErrorTraceAttributes(t *testing.T) { "priority":0.500000, "sampled":false } - } + }, + "txn-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -188,6 +237,7 @@ func TestErrorTraceAttributesOldCAT(t *testing.T) { Klass: "my_class", }, txnEvent: txnEvent{ + TxnID: "txn-guid-id", FinalName: "my_txn_name", Attrs: attr, BetterCAT: betterCAT{ @@ -212,7 +262,8 @@ func TestErrorTraceAttributesOldCAT(t *testing.T) { "intrinsics":{ "totalTime":2 } - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -231,6 +282,8 @@ func TestErrorsLifecycle(t *testing.T) { mergeTxnErrors(&he, ers, txnEvent{ FinalName: "txnName", Attrs: nil, + TxnID: "txn-id", + BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", @@ -257,12 +310,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -274,12 +328,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -291,12 +346,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -308,17 +364,18 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ] ] ]`) if string(js) != expect { - t.Error(string(js), expect) + t.Error(string(js), "expect: ", expect) } } diff --git a/v3/newrelic/harvest_test.go b/v3/newrelic/harvest_test.go index 58748fab0..73fdd9b71 100644 --- a/v3/newrelic/harvest_test.go +++ b/v3/newrelic/harvest_test.go @@ -415,7 +415,7 @@ func TestHarvestErrorEventsReady(t *testing.T) { }) h.ErrorEvents.Add(&errorEvent{ errorData: errorData{Klass: "klass", Msg: "msg", When: time.Now()}, - txnEvent: txnEvent{FinalName: "finalName", Duration: 1 * time.Second}, + txnEvent: txnEvent{FinalName: "finalName", Duration: 1 * time.Second, TxnID: "txn-guid-id"}, }, 0) ready := h.Ready(now.Add(10 * time.Second)) payloads := ready.Payloads(true) @@ -544,6 +544,7 @@ func TestHarvestMetricsTracesReady(t *testing.T) { TxnName: "finalName", Msg: "msg", Klass: "klass", + GUID: "error-guid-id", }}) expectErrors(t, h.ErrorTraces, []internal.WantError{}) diff --git a/v3/newrelic/internal_txn.go b/v3/newrelic/internal_txn.go index 30fe644d4..b06a8c61c 100644 --- a/v3/newrelic/internal_txn.go +++ b/v3/newrelic/internal_txn.go @@ -118,11 +118,13 @@ func newTxn(app *app, run *appRun, name string, opts ...TraceOption) *thread { if !txnOpts.SuppressCLM && run.Config.CodeLevelMetrics.Enabled && (txnOpts.DemandCLM || run.Config.CodeLevelMetrics.Scope == 0 || (run.Config.CodeLevelMetrics.Scope&TransactionCLM) != 0) { reportCodeLevelMetrics(txnOpts, run, txn.Attrs.Agent.Add) } + txn.TraceIDGenerator = run.Reply.TraceIDGenerator + traceID := txn.TraceIDGenerator.GenerateTraceID() + txn.SetTransactionID(traceID) if run.Config.DistributedTracer.Enabled { txn.BetterCAT.Enabled = true - txn.TraceIDGenerator = run.Reply.TraceIDGenerator - txn.BetterCAT.SetTraceAndTxnIDs(txn.TraceIDGenerator.GenerateTraceID()) + txn.BetterCAT.SetTraceAndTxnIDs(traceID) txn.BetterCAT.Priority = newPriorityFromRandom(txn.TraceIDGenerator.Float32) txn.ShouldCollectSpanEvents = txn.shouldCollectSpanEvents txn.ShouldCreateSpanGUID = txn.shouldCreateSpanGUID @@ -523,7 +525,7 @@ func (thd *thread) End(recovered interface{}) error { // segments occur. for _, evt := range txn.SpanEvents { evt.TraceID = txn.BetterCAT.TraceID - evt.TransactionID = txn.BetterCAT.TxnID + evt.TransactionID = txn.TxnID evt.Sampled = txn.BetterCAT.Sampled evt.Priority = txn.BetterCAT.Priority } @@ -1144,7 +1146,7 @@ func (thd *thread) CreateDistributedTracePayload(hdrs http.Header) { p.Priority = txn.BetterCAT.Priority p.Timestamp.Set(txn.Reply.DistributedTraceTimestampGenerator()) p.TrustedAccountKey = txn.Reply.TrustedAccountKey - p.TransactionID = txn.BetterCAT.TxnID // Set the transaction ID to the transaction guid. + p.TransactionID = txn.TxnID // Set the transaction ID to the transaction guid. if nil != txn.BetterCAT.Inbound { p.NonTrustedTraceState = txn.BetterCAT.Inbound.NonTrustedTraceState p.OriginalTraceState = txn.BetterCAT.Inbound.OriginalTraceState diff --git a/v3/newrelic/slow_queries_test.go b/v3/newrelic/slow_queries_test.go index 3d831f427..9aa112ad6 100644 --- a/v3/newrelic/slow_queries_test.go +++ b/v3/newrelic/slow_queries_test.go @@ -211,6 +211,7 @@ func TestSlowQueriesBetterCAT(t *testing.T) { FinalName: "WebTransaction/Go/hello", Duration: 3 * time.Second, Attrs: attr, + TxnID: "my-txn-id", BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", diff --git a/v3/newrelic/tracing.go b/v3/newrelic/tracing.go index a189cef5e..c8fac7ef6 100644 --- a/v3/newrelic/tracing.go +++ b/v3/newrelic/tracing.go @@ -36,6 +36,7 @@ type txnEvent struct { datastoreCallCount uint64 datastoreDuration time.Duration errGroupCallback ErrorGroupCallback + TxnID string } // betterCAT stores the transaction's priority and all fields related @@ -62,6 +63,16 @@ func (bc *betterCAT) SetTraceAndTxnIDs(traceID string) { } } +func (e *txnEvent) SetTransactionID(transactionID string) { + txnLength := 16 + if len(transactionID) <= txnLength { + e.TxnID = transactionID + } else { + e.TxnID = transactionID[:txnLength] + } + +} + // txnData contains the recorded data of a transaction. type txnData struct { IsWeb bool diff --git a/v3/newrelic/txn_trace.go b/v3/newrelic/txn_trace.go index 2e925c5e4..f1d0c99ce 100644 --- a/v3/newrelic/txn_trace.go +++ b/v3/newrelic/txn_trace.go @@ -294,6 +294,9 @@ func (trace *harvestTrace) writeJSON(buf *bytes.Buffer) { jsonx.AppendString(buf, trace.CrossProcess.GUID) } else if trace.BetterCAT.Enabled { jsonx.AppendString(buf, trace.BetterCAT.TraceID) + } else if !trace.BetterCAT.Enabled && trace.CrossProcess.GUID != "" { + jsonx.AppendString(buf, trace.txnEvent.TxnID) + } else { buf.WriteString(`""`) }