From 25d719c3d0c836e755c6ba28f14846979c70f196 Mon Sep 17 00:00:00 2001 From: Brent Wheeldon Date: Tue, 3 Sep 2024 17:08:48 -0400 Subject: [PATCH] Handle Task/GenServer exits correctly in Google formatter We were running into a situation where errors caused by GenServers terminating were not being formatted because they were a slightly different shape to what was expected in the formatter. This updates the formatter to handle them. --- lib/logger_json/formatters/google_cloud.ex | 6 ++- .../formatters/google_cloud_test.exs | 45 +++++++++++++++++++ test/support/crashing_gen_server.ex | 15 +++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 test/support/crashing_gen_server.ex diff --git a/lib/logger_json/formatters/google_cloud.ex b/lib/logger_json/formatters/google_cloud.ex index 025c200..038646f 100644 --- a/lib/logger_json/formatters/google_cloud.ex +++ b/lib/logger_json/formatters/google_cloud.ex @@ -188,11 +188,15 @@ defmodule LoggerJSON.Formatters.GoogleCloud do format_reported_error_event(message, ruby_stacktrace, service_context, meta) end - def format_crash_reason(binary, {error, reason}, service_context, meta) do + def format_crash_reason(binary, {error, reason}, service_context, meta) when is_atom(error) or is_binary(error) do stacktrace = "** (#{error}) #{inspect(reason)}" format_reported_error_event(binary, stacktrace, service_context, meta) end + def format_crash_reason(binary, {error, reason}, service_context, meta) do + format_crash_reason(binary, {inspect(error), reason}, service_context, meta) + end + defp format_reported_error_event(message, stacktrace, service_context, meta) do %{ "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent", diff --git a/test/logger_json/formatters/google_cloud_test.exs b/test/logger_json/formatters/google_cloud_test.exs index 40bf0da..589dced 100644 --- a/test/logger_json/formatters/google_cloud_test.exs +++ b/test/logger_json/formatters/google_cloud_test.exs @@ -414,6 +414,51 @@ defmodule LoggerJSON.Formatters.GoogleCloudTest do } = log_entry end + test "logs Task/GenServer termination" do + test_pid = self() + + logs = + capture_log(fn -> + {:ok, _} = Supervisor.start_link([{CrashingGenServer, :ok}], strategy: :one_for_one) + + {:ok, _} = + Task.start(fn -> + try do + GenServer.call(CrashingGenServer, :boom) + catch + _ -> nil + after + send(test_pid, :done) + end + end) + + # Wait for task to finish + receive do + :done -> nil + end + + # Let logs flush + Process.sleep(100) + end) + + refute logs =~ "FORMATTER CRASH" + + [_, log_entry] = + logs + |> String.trim() + |> String.split("\n") + |> Enum.map(&decode_or_print_error/1) + + assert %{ + "@type" => "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent", + "message" => message, + "stack_trace" => "** ({{%RuntimeError{message: \"boom\"}" <> _, + "serviceContext" => %{"service" => "nonode@nohost"} + } = log_entry + + assert message =~ ~r/Task #PID<\d+.\d+.\d+> started from #{inspect(test_pid)} terminating/ + end + test "logs process exits" do Logger.metadata(crash_reason: {{:EXIT, self()}, :sad_failure}) diff --git a/test/support/crashing_gen_server.ex b/test/support/crashing_gen_server.ex new file mode 100644 index 0000000..6afd223 --- /dev/null +++ b/test/support/crashing_gen_server.ex @@ -0,0 +1,15 @@ +defmodule CrashingGenServer do + use GenServer + + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + def init(state) do + {:ok, state} + end + + def handle_call(:boom, _, _) do + raise "boom" + end +end