Repository: newrelic/newrelic-ruby-agent (Ruby)

Issue:
Spec: Add stack trace attribute to datastore segments

If a datastore segment time passes the configured threshold, an attribute `code.stacktrace` should be added to the datastore segment.


[Spec PR](https://source.datanerd.us/agents/agent-specs/pull/651)


Discussion:
https://new-relic.atlassian.net/browse/NR-189831



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48a489742f..171e302e52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,10 @@
 
   The agent has a new helper method to simplify version comparison. `NewRelic::Helper.version_satisfied?` accepts three arguments: a left-side version number, the comparison operator as a string, and a right-side version number. Our thanks go to [@kekke-n](https://github.com/kekke-n) for this contribution. [PR#3182](https://github.com/newrelic/newrelic-ruby-agent/pull/3182)
 
+- **Feature: Add code.stacktrace attribute on datastore spans when duration exceeds configured threshold**
+
+  The agent will now add the `code.stacktrace` attribute to datastore spans when the duration exceeds the configured threshold. The threshold is configured using the `transaction_tracer.stack_trace_threshold` configuration option. [PR#3220](https://github.com/newrelic/newrelic-ruby-agent/pull/3220)
+
 - **Bugfix: Fix Brewfile source links**
 
   Previously, the multiverse README's links to the Brewfile were broken. Our thanks go to [@emmanuel-ferdman](https://github.com/emmanuel-ferdman) for submitting a PR to fix them! [PR#3191](https://github.com/newrelic/newrelic-ruby-agent/pull/3191)
diff --git a/lib/new_relic/agent/span_event_primitive.rb b/lib/new_relic/agent/span_event_primitive.rb
index 0575d8e8cb..f110deceb7 100644
--- a/lib/new_relic/agent/span_event_primitive.rb
+++ b/lib/new_relic/agent/span_event_primitive.rb
@@ -40,6 +40,7 @@ module SpanEventPrimitive
       SERVER_ADDRESS_KEY = 'server.address'
       SERVER_PORT_KEY = 'server.port'
       SPAN_KIND_KEY = 'span.kind'
+      STACKTRACE_KEY = 'code.stacktrace'
       ENTRY_POINT_KEY = 'nr.entryPoint'
       TRUSTED_PARENT_KEY = 'trustedParentId'
       TRACING_VENDORS_KEY = 'tracingVendors'
@@ -122,6 +123,10 @@ def for_datastore_segment(segment) # rubocop:disable Metrics/AbcSize
           agent_attributes[DB_STATEMENT_KEY] = truncate(segment.nosql_statement, DB_STATEMENT_MAX_BYTES)
         end
 
+        if segment.params[:backtrace]
+          agent_attributes[STACKTRACE_KEY] = segment.params[:backtrace]
+        end
+
         [intrinsics, custom_attributes(segment), agent_attributes.merge(agent_attributes(segment))]
       end
 


Test patch:
diff --git a/test/new_relic/agent/transaction/datastore_segment_test.rb b/test/new_relic/agent/transaction/datastore_segment_test.rb
index a888034664..c9be96a9a5 100644
--- a/test/new_relic/agent/transaction/datastore_segment_test.rb
+++ b/test/new_relic/agent/transaction/datastore_segment_test.rb
@@ -743,7 +743,9 @@ def test_set_instance_info_with_empty_data
         def test_backtrace_not_appended_if_not_over_duration
           segment = nil
           with_config(:'transaction_tracer.stack_trace_threshold' => 2.0) do
-            in_web_transaction('test_txn') do
+            in_web_transaction('test_txn') do |txn|
+              txn.stubs(:sampled?).returns(true)
+
               segment = NewRelic::Agent::Tracer.start_datastore_segment(
                 product: 'SQLite',
                 operation: 'insert',
@@ -761,12 +763,19 @@ def test_backtrace_not_appended_if_not_over_duration
           node = find_node_with_name_matching(sample, /^Datastore/)
 
           assert_nil node.params[:backtrace]
+
+          last_span_events = NewRelic::Agent.agent.span_event_aggregator.harvest![1]
+          _, _, agent_attributes = last_span_events[0]
+
+          assert_nil agent_attributes['code.stacktrace']
         end
 
         def test_backtrace_appended_when_over_duration
           segment = nil
           with_config(:'transaction_tracer.stack_trace_threshold' => 1.0) do
-            in_web_transaction('test_txn') do
+            in_web_transaction('test_txn') do |txn|
+              txn.stubs(:sampled?).returns(true)
+
               segment = NewRelic::Agent::Tracer.start_datastore_segment(
                 product: 'SQLite',
                 operation: 'insert',
@@ -784,6 +793,11 @@ def test_backtrace_appended_when_over_duration
           node = find_node_with_name_matching(sample, /^Datastore/)
 
           refute_nil node.params[:backtrace]
+
+          last_span_events = NewRelic::Agent.agent.span_event_aggregator.harvest![1]
+          _, _, agent_attributes = last_span_events[0]
+
+          refute_nil agent_attributes['code.stacktrace']
         end
 
         def test_node_obfuscated