Repository: newrelic/elixir_agent (elixir)

Issue:
transaction.name missing from Span events
transaction.name is missing from Span events
https://docs.newrelic.com/attribute-dictionary/?event=Span

This attribute is required to correlate transaction metrics and names with Span events. While one span event for each transaction has _part_ of the transaction name - for example:
WebTransaction/AbsinthePlug/query/actor.account.results vs AbsinthePlug/query/actor.account.results

But since it is not an exact match one would need to know what part of the transaction name is on the Span to query it. A use case is to find distributed traces that match a given transaction metric name. But any other use cases requiring this correlation will likewise not function properly without the attribute.

Pull request description:
Add transaction.name to spansaction
Adds `transaction.name` attribute to the "spans action" span event so it can be correlated to transaction metrics

Closes #490 

Patch:
diff --git a/lib/new_relic/telemetry/plug.ex b/lib/new_relic/telemetry/plug.ex
index 2eaf315..351c690 100644
--- a/lib/new_relic/telemetry/plug.ex
+++ b/lib/new_relic/telemetry/plug.ex
@@ -288,7 +288,7 @@ defmodule NewRelic.Telemetry.Plug do
   defp status_code(_), do: nil
 
   defp plug_name(conn, match_path) do
-    "/Plug/#{conn.method}/#{match_path}"
+    "/Plug/#{conn.method}#{match_path}"
     |> String.replace("/*glob", "")
     |> String.replace("/*_path", "")
   end
diff --git a/lib/new_relic/transaction/complete.ex b/lib/new_relic/transaction/complete.ex
index a52840f..8c00e0d 100644
--- a/lib/new_relic/transaction/complete.ex
+++ b/lib/new_relic/transaction/complete.ex
@@ -246,6 +246,7 @@ defmodule NewRelic.Transaction.Complete do
           |> Map.drop(@spansaction_exclude_attrs)
           |> Map.merge(NewRelic.Config.automatic_attributes())
           |> Map.merge(%{
+            "transaction.name": Util.metric_join(["#{tx_attrs[:transactionType]}Transaction", tx_attrs.name]),
             tracingVendors: tx_attrs[:tracingVendors],
             trustedParentId: tx_attrs[:trustedParentId]
           })


Test patch:
diff --git a/examples/apps/ecto_example/test/ecto_example_test.exs b/examples/apps/ecto_example/test/ecto_example_test.exs
index df2ca67..fb5a4f7 100644
--- a/examples/apps/ecto_example/test/ecto_example_test.exs
+++ b/examples/apps/ecto_example/test/ecto_example_test.exs
@@ -33,19 +33,19 @@ defmodule EctoExampleTest do
 
     assert TestSupport.find_metric(
              metrics,
-             {"Datastore/statement/Postgres/counts/insert", "WebTransaction/Plug/GET//hello"},
+             {"Datastore/statement/Postgres/counts/insert", "WebTransaction/Plug/GET/hello"},
              3
            )
 
     assert TestSupport.find_metric(
              metrics,
-             {"Datastore/statement/MySQL/counts/insert", "WebTransaction/Plug/GET//hello"},
+             {"Datastore/statement/MySQL/counts/insert", "WebTransaction/Plug/GET/hello"},
              3
            )
 
     assert TestSupport.find_metric(
              metrics,
-             {"Datastore/statement/SQLite3/counts/insert", "WebTransaction/Plug/GET//hello"},
+             {"Datastore/statement/SQLite3/counts/insert", "WebTransaction/Plug/GET/hello"},
              3
            )
 
diff --git a/examples/apps/redix_example/test/redix_example_test.exs b/examples/apps/redix_example/test/redix_example_test.exs
index 5a17363..5470e23 100644
--- a/examples/apps/redix_example/test/redix_example_test.exs
+++ b/examples/apps/redix_example/test/redix_example_test.exs
@@ -47,7 +47,7 @@ defmodule RedixExampleTest do
 
     assert TestSupport.find_metric(
              metrics,
-             {"Datastore/operation/Redis/SET", "WebTransaction/Plug/GET//hello"},
+             {"Datastore/operation/Redis/SET", "WebTransaction/Plug/GET/hello"},
              1
            )
 
diff --git a/test/metric_transaction_test.exs b/test/metric_transaction_test.exs
index 00be5e4..56e150f 100644
--- a/test/metric_transaction_test.exs
+++ b/test/metric_transaction_test.exs
@@ -99,7 +99,7 @@ defmodule MetricTransactionTest do
 
     metrics = TestHelper.gather_harvest(Collector.Metric.Harvester)
 
-    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET//foo/:blah")
+    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET/foo/:blah")
     refute TestHelper.find_metric(metrics, "WebFrontend/QueueTime")
     assert TestHelper.find_metric(metrics, "Apdex")
     assert TestHelper.find_metric(metrics, "HttpDispatcher")
@@ -110,7 +110,7 @@ defmodule MetricTransactionTest do
 
     metrics = TestHelper.gather_harvest(Collector.Metric.Harvester)
 
-    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET//foo/:blah")
+    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET/foo/:blah")
 
     # Unscoped
     assert TestHelper.find_metric(metrics, "External/domain.net/HttpClient/GET")
@@ -120,12 +120,12 @@ defmodule MetricTransactionTest do
     # Scoped
     assert TestHelper.find_metric(
              metrics,
-             {"External/domain.net/HttpClient/GET", "WebTransaction/Plug/GET//foo/:blah"}
+             {"External/domain.net/HttpClient/GET", "WebTransaction/Plug/GET/foo/:blah"}
            )
 
     assert TestHelper.find_metric(
              metrics,
-             {"External/MetricTransactionTest.External.call", "WebTransaction/Plug/GET//foo/:blah"}
+             {"External/MetricTransactionTest.External.call", "WebTransaction/Plug/GET/foo/:blah"}
            )
   end
 
@@ -134,7 +134,7 @@ defmodule MetricTransactionTest do
 
     metrics = TestHelper.gather_harvest(Collector.Metric.Harvester)
 
-    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET//foo/:blah")
+    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET/foo/:blah")
 
     # Unscoped
     assert TestHelper.find_metric(
@@ -145,7 +145,7 @@ defmodule MetricTransactionTest do
     # Scoped
     assert TestHelper.find_metric(
              metrics,
-             {"Function/MetricTransactionTest.External.make_queries/0", "WebTransaction/Plug/GET//foo/:blah"}
+             {"Function/MetricTransactionTest.External.make_queries/0", "WebTransaction/Plug/GET/foo/:blah"}
            )
   end
 
@@ -195,7 +195,7 @@ defmodule MetricTransactionTest do
 
     assert TestHelper.find_metric(
              metrics,
-             "WebTransaction/Plug/GET//fancy/:transaction/:_names/*supported"
+             "WebTransaction/Plug/GET/fancy/:transaction/:_names/*supported"
            )
   end
 
@@ -206,7 +206,7 @@ defmodule MetricTransactionTest do
 
     metrics = TestHelper.gather_harvest(Collector.Metric.Harvester)
 
-    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET//status/check", 2)
-    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET//status/info")
+    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET/status/check", 2)
+    assert TestHelper.find_metric(metrics, "WebTransaction/Plug/GET/status/info")
   end
 end
diff --git a/test/span_event_test.exs b/test/span_event_test.exs
index a89ba2f..7f3e2a5 100644
--- a/test/span_event_test.exs
+++ b/test/span_event_test.exs
@@ -264,6 +264,8 @@ defmodule SpanEventTest do
 
     assert tx_event[:parentId] == "7d3efb1b173fecfa"
 
+    assert spansaction_event[:"transaction.name"] == "WebTransaction/Plug/GET/hello"
+
     assert tx_event[:traceId] == "d6b4ba0c3a712ca"
     assert spansaction_event[:traceId] == "d6b4ba0c3a712ca"
     assert tx_root_process_event[:traceId] == "d6b4ba0c3a712ca"
diff --git a/test/transaction_error_event_test.exs b/test/transaction_error_event_test.exs
index dbf3c59..d272a22 100644
--- a/test/transaction_error_event_test.exs
+++ b/test/transaction_error_event_test.exs
@@ -233,7 +233,7 @@ defmodule TransactionErrorEventTest do
     assert [
              [
                _ts,
-               "WebTransaction/Plug/GET//caught/error",
+               "WebTransaction/Plug/GET/caught/error",
                _,
                _,
                %{userAttributes: %{nested: "process"}},
diff --git a/test/transaction_test.exs b/test/transaction_test.exs
index 4fe2651..eb4c928 100644
--- a/test/transaction_test.exs
+++ b/test/transaction_test.exs
@@ -212,7 +212,7 @@ defmodule TransactionTest do
     events = TestHelper.gather_harvest(Collector.TransactionEvent.Harvester)
 
     assert Enum.find(events, fn [_, event] ->
-             event[:path] == "/foo/1" && event[:name] == "/Plug/GET//foo/:blah" &&
+             event[:path] == "/foo/1" && event[:name] == "/Plug/GET/foo/:blah" &&
                event[:foo] == "BAR" && event[:duration_us] > 0 && event[:duration_us] < 50_000 &&
                event[:start_time] < 2_000_000_000_000 && event[:start_time] > 1_400_000_000_000 &&
                event[:start_time_mono] == nil && event[:test_attribute] == "test_value" &&
@@ -227,7 +227,7 @@ defmodule TransactionTest do
 
     events = TestHelper.gather_harvest(Collector.TransactionEvent.Harvester)
 
-    [_, event] = Enum.find(events, fn [_, event] -> event[:name] == "/Plug/GET//funky_attrs" end)
+    [_, event] = Enum.find(events, fn [_, event] -> event[:name] == "/Plug/GET/funky_attrs" end)
 
     # Basic values
     assert event[:one] == 1
@@ -285,7 +285,7 @@ defmodule TransactionTest do
              event[:status] == 500 &&
                event[:query] =~ "query{}" &&
                event[:error] &&
-               event[:name] == "/Plug/GET//fail" &&
+               event[:name] == "/Plug/GET/fail" &&
                event[:error_reason] =~ "TransactionError" &&
                event[:error_kind] == :exit &&
                event[:error_stack] =~ "test/transaction_test.exs"
@@ -304,7 +304,7 @@ defmodule TransactionTest do
              event[:status] == 500 &&
                event[:query] =~ "query{}" &&
                event[:error] &&
-               event[:name] == "/Plug/GET//erlang_exit" &&
+               event[:name] == "/Plug/GET/erlang_exit" &&
                event[:error_reason] =~ "something_bad" &&
                event[:error_kind] == :exit
            end)
@@ -321,7 +321,7 @@ defmodule TransactionTest do
     assert Enum.find(events, fn [_, event] ->
              event[:status] == 500 &&
                event[:error] &&
-               event[:name] == "/Plug/GET//await_timeout" &&
+               event[:name] == "/Plug/GET/await_timeout" &&
                event[:error_reason] =~ "timeout" &&
                event[:error_kind] == :exit
            end)