Repository: newrelic/elixir_agent
Pull request #526 (2025-05-19 21:12:53)

Tweak error classification, metrics
This PR makes a small tweak to error "type" classification so that exceptions that arise as an exit get classified as their exception type, not as "exit". This should translate into better grouping in the Errors UI

Also adds Error "blame" metrics that will ensure that per-transaction error metrics are reported.


Patch:
diff --git a/lib/new_relic/metric/metric_data.ex b/lib/new_relic/metric/metric_data.ex
index 818603b..507e63e 100644
--- a/lib/new_relic/metric/metric_data.ex
+++ b/lib/new_relic/metric/metric_data.ex
@@ -409,6 +409,22 @@ defmodule NewRelic.Metric.MetricData do
       }
     ]
 
+  def transform({:error, blame}, type: type, error_count: error_count),
+    do: [
+      %Metric{
+        name: join(["Errors", "#{type}Transaction", blame]),
+        call_count: error_count
+      },
+      %Metric{
+        name: "Errors/all#{type}",
+        call_count: error_count
+      },
+      %Metric{
+        name: :"Errors/all",
+        call_count: error_count
+      }
+    ]
+
   def transform(:error, error_count: error_count),
     do: %Metric{
       name: :"Errors/all",
diff --git a/lib/new_relic/transaction/complete.ex b/lib/new_relic/transaction/complete.ex
index f744558..f52b223 100644
--- a/lib/new_relic/transaction/complete.ex
+++ b/lib/new_relic/transaction/complete.ex
@@ -480,7 +480,7 @@ defmodule NewRelic.Transaction.Complete do
 
   defp report_transaction_error_event(_tx_attrs, nil), do: :ignore
 
-  defp report_transaction_error_event(tx_attrs, {:error, error}) do
+  defp report_transaction_error_event(%{name: tx_name, transactionType: type} = tx_attrs, {:error, error}) do
     attributes = Map.drop(tx_attrs, [:error, :"error.kind", :"error.reason", :"error.stack"])
     expected = parse_error_expected(error.reason)
 
@@ -509,7 +509,7 @@ defmodule NewRelic.Transaction.Complete do
 
     unless expected do
       NewRelic.report_metric({:supportability, :error_event}, error_count: 1)
-      NewRelic.report_metric(:error, type: tx_attrs.transactionType, error_count: 1)
+      NewRelic.report_metric({:error, tx_name}, type: type, error_count: 1)
     end
   end
 
diff --git a/lib/new_relic/util/error.ex b/lib/new_relic/util/error.ex
index 2ff4cc3..fa88558 100644
--- a/lib/new_relic/util/error.ex
+++ b/lib/new_relic/util/error.ex
@@ -18,7 +18,7 @@ defmodule NewRelic.Util.Error do
   defp format_type(:error, %ErlangError{original: {_reason, {module, function, args}}}),
     do: Exception.format_mfa(module, function, length(args))
 
-  defp format_type(:error, %{__struct__: struct}), do: inspect(struct)
+  defp format_type(_, %{__exception__: true, __struct__: struct}), do: inspect(struct)
   defp format_type(:exit, _reason), do: "EXIT"
 
   def format_reason(:error, %ErlangError{original: {reason, {module, function, args}}}),


Test patch:
diff --git a/test/error_test.exs b/test/error_test.exs
index b830697..3342e02 100644
--- a/test/error_test.exs
+++ b/test/error_test.exs
@@ -150,7 +150,7 @@ defmodule ErrorTest do
 
     events = TestHelper.gather_harvest(Collector.TransactionErrorEvent.Harvester)
 
-    assert TestHelper.find_event(events, %{"error.class": "EXIT", "error.message": "(RuntimeError) foo"})
+    assert TestHelper.find_event(events, %{"error.class": "RuntimeError", "error.message": "(RuntimeError) foo"})
   end
 
   test "Catch a file error" do
diff --git a/test/transaction_error_event_test.exs b/test/transaction_error_event_test.exs
index 423cf0c..6fb4bde 100644
--- a/test/transaction_error_event_test.exs
+++ b/test/transaction_error_event_test.exs
@@ -164,6 +164,8 @@ defmodule TransactionErrorEventTest do
 
     metrics = TestHelper.gather_harvest(Collector.Metric.Harvester)
     assert TestHelper.find_metric(metrics, "Errors/all")
+    assert TestHelper.find_metric(metrics, "Errors/allWeb")
+    assert TestHelper.find_metric(metrics, "Errors/WebTransaction/Plug/GET/error")
 
     traces = TestHelper.gather_harvest(Collector.TransactionEvent.Harvester)
     assert length(traces) == 1