From 3ba06069309a04e8a31f42126b2e7720d68b85dc Mon Sep 17 00:00:00 2001 From: Devon Estes Date: Mon, 14 Dec 2020 14:38:00 +0100 Subject: [PATCH 1/4] update travis to build matrix --- .travis.yml | 12 +++++++----- mix.lock | 22 +++++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 002569d..2639785 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,12 @@ sudo: false language: elixir -otp_release: '21.1' -elixir: '1.8' +elixir: + - 1.11.1 + - 1.8.0 +otp_release: + - 23.1.0 + - 20.0.0 stages: - check formatted @@ -12,9 +16,7 @@ stages: jobs: include: - stage: test - - - otp_release: '18.3' - elixir: '1.4' + script: mix test - stage: check formatted script: mix format --check-formatted diff --git a/mix.lock b/mix.lock index 1cf3253..369662f 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,13 @@ %{ - "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, - "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, - "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, - "hackney": {:hex, :hackney, "1.3.2", "43bd07ab88753f5e136e38fddd2a09124bee25733b03361eeb459d0173fc17ab", [:rebar, :make], [{:idna, "~> 1.0.2", [hex: :idna, optional: false]}, {:ssl_verify_hostname, "~> 1.0.5", [hex: :ssl_verify_hostname, optional: false]}]}, - "idna": {:hex, :idna, "1.0.3", "d456a8761cad91c97e9788c27002eb3b773adaf5c893275fc35ba4e3434bbd9b", [:rebar3], []}, - "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, - "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, - "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, - "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.6", "45866d958d9ae51cfe8fef0050ab8054d25cba23ace43b88046092aa2c714645", [:make], []}, + "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "f4763bbe08233eceed6f24bc4fcc8d71c17cfeafa6439157c57349aa1bb4f17c"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm", "db622da03aa039e6366ab953e31186cc8190d32905e33788a1acb22744e6abd2"}, + "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm", "1b34655872366414f69dd987cb121c049f76984b6ac69f52fff6d8fd64d29cfd"}, + "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm", "f050061c87ad39478c942995b5a20c40f2c0bc06525404613b8b0474cb8bd796"}, + "hackney": {:hex, :hackney, "1.3.2", "43bd07ab88753f5e136e38fddd2a09124bee25733b03361eeb459d0173fc17ab", [:make, :rebar], [{:idna, "~> 1.0.2", [hex: :idna, repo: "hexpm", optional: false]}, {:ssl_verify_hostname, "~> 1.0.5", [hex: :ssl_verify_hostname, repo: "hexpm", optional: false]}], "hexpm", "9b811cff637b29f9c7e2c61abf01986c85cd4f64a9422315fd803993b4e82615"}, + "idna": {:hex, :idna, "1.0.3", "d456a8761cad91c97e9788c27002eb3b773adaf5c893275fc35ba4e3434bbd9b", [:rebar3], [], "hexpm", "357d489a51112db4f216034406834f9172b3c0ff5a12f83fb28b25ca271541d1"}, + "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b96c400e04b7b765c0854c05a4966323e90c0d11fee0483b1567cda079abb205"}, + "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], [], "hexpm", "8aad5eef6d9d20899918868b10e79fc2dafe72a79102882c2947999c10b30cd9"}, + "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "c1c408c57a1e4c88c365b9aff1198c350e22b765dbb97a460e9e6bd9364c6194"}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm", "6e56493a862433fccc3aca3025c946d6720d8eedf6e3e6fb911952a7071c357f"}, + "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.6", "45866d958d9ae51cfe8fef0050ab8054d25cba23ace43b88046092aa2c714645", [:make], [], "hexpm", "72b2fc8a8e23d77eed4441137fefa491bbf4a6dc52e9c0045f3f8e92e66243b5"}, } From 4f66a785829ef304bf3f388b91fed99041bea06e Mon Sep 17 00:00:00 2001 From: Devon Estes Date: Tue, 15 Dec 2020 09:22:50 +0100 Subject: [PATCH 2/4] Handle changes with new logger This catches some previously uncaught crash reports that were missed because of the change in pattern in the new logger. --- .travis.yml | 30 +++++++++---------- lib/rollbax/reporter/standard.ex | 49 ++++++++++++++++++++++++++++++-- mix.lock | 22 +++++++------- test/rollbax/logger_test.exs | 15 +++++----- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2639785..565f972 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,22 @@ -sudo: false - language: elixir - elixir: - 1.11.1 - - 1.8.0 otp_release: - - 23.1.0 - - 20.0.0 + - 23.1 -stages: - - check formatted - - test - -jobs: +matrix: include: - - stage: test - script: mix test + - elixir: 1.11.1 + otp_release: 23.1 + - elixir: 1.8.0 + otp_release: 19.3 - - stage: check formatted - script: mix format --check-formatted +script: + - if [[ "$TRAVIS_ELIXIR_VERSION" == "1.11"* ]]; then mix format --check-formatted; fi + - mix test +sudo: false +dist: trusty +cache: + directories: + - _build + - deps diff --git a/lib/rollbax/reporter/standard.ex b/lib/rollbax/reporter/standard.ex index 8857395..5219130 100644 --- a/lib/rollbax/reporter/standard.ex +++ b/lib/rollbax/reporter/standard.ex @@ -10,7 +10,11 @@ defmodule Rollbax.Reporter.Standard do handle_error_format(format, data) end - def handle_event(_type, _event) do + def handle_event(:error_report, {_, :crash_report, data}) do + handle_error_format(:crash_report, data) + end + + def handle_event(_, _) do :next end @@ -70,6 +74,47 @@ defmodule Rollbax.Reporter.Standard do } end + # OTP error logger crash report + defp handle_error_format(:crash_report, [data, _]) do + {m, f, a} = Keyword.fetch!(data, :initial_call) + + name = + case Keyword.get(data, :registered_name) do + [] -> data |> Keyword.fetch!(:pid) |> inspect() + name -> inspect(name) + end + + {class, message, stacktrace, crash_report} = + case Keyword.fetch!(data, :error_info) do + {_, %class{message: message}, stacktrace} -> + {inspect(class), message, stacktrace, ""} + + {_, info, stacktrace} when is_tuple(info) -> + case elem(info, 0) do + %class{message: message} -> {inspect(class), message, stacktrace, ""} + %class{} -> {inspect(class), inspect(class), stacktrace, ""} + atom when is_atom(atom) -> {inspect(atom), inspect(atom), stacktrace, ""} + {%class{message: message}, inner_stacktrace} -> {inspect(class), message, inner_stacktrace, ""} + {%class{}, inner_stacktrace} -> {inspect(class), inspect(class), inner_stacktrace, ""} + {atom, inner_stacktrace} when is_atom(atom) -> {inspect(atom), inspect(atom), inner_stacktrace, ""} + reason -> {"ProcessCrash", "A process crashed", stacktrace, inspect(reason, limit: :infinity)} + end + end + + %Rollbax.Exception{ + class: "Crash report (#{class})", + message: message, + stacktrace: stacktrace, + custom: %{ + name: name, + started_from: data |> Keyword.fetch!(:ancestors) |> hd() |> inspect(), + function: inspect(Function.capture(m, f, length(a))), + arguments: inspect(a), + crash_report: crash_report + } + } + end + defp handle_error_format('** State machine ' ++ _ = message, data) do if charlist_contains?(message, 'Callback mode') do :next @@ -94,7 +139,7 @@ defmodule Rollbax.Reporter.Standard do # Any other error (for example, the ones logged through # :error_logger.error_msg/1). This reporter doesn't report those to Rollbar. - defp handle_error_format(_format, _data) do + defp handle_error_format(_, _) do :next end diff --git a/mix.lock b/mix.lock index 369662f..daba4d0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,13 @@ %{ - "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "f4763bbe08233eceed6f24bc4fcc8d71c17cfeafa6439157c57349aa1bb4f17c"}, - "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm", "db622da03aa039e6366ab953e31186cc8190d32905e33788a1acb22744e6abd2"}, - "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm", "1b34655872366414f69dd987cb121c049f76984b6ac69f52fff6d8fd64d29cfd"}, - "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm", "f050061c87ad39478c942995b5a20c40f2c0bc06525404613b8b0474cb8bd796"}, - "hackney": {:hex, :hackney, "1.3.2", "43bd07ab88753f5e136e38fddd2a09124bee25733b03361eeb459d0173fc17ab", [:make, :rebar], [{:idna, "~> 1.0.2", [hex: :idna, repo: "hexpm", optional: false]}, {:ssl_verify_hostname, "~> 1.0.5", [hex: :ssl_verify_hostname, repo: "hexpm", optional: false]}], "hexpm", "9b811cff637b29f9c7e2c61abf01986c85cd4f64a9422315fd803993b4e82615"}, - "idna": {:hex, :idna, "1.0.3", "d456a8761cad91c97e9788c27002eb3b773adaf5c893275fc35ba4e3434bbd9b", [:rebar3], [], "hexpm", "357d489a51112db4f216034406834f9172b3c0ff5a12f83fb28b25ca271541d1"}, - "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b96c400e04b7b765c0854c05a4966323e90c0d11fee0483b1567cda079abb205"}, - "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], [], "hexpm", "8aad5eef6d9d20899918868b10e79fc2dafe72a79102882c2947999c10b30cd9"}, - "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "c1c408c57a1e4c88c365b9aff1198c350e22b765dbb97a460e9e6bd9364c6194"}, - "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm", "6e56493a862433fccc3aca3025c946d6720d8eedf6e3e6fb911952a7071c357f"}, - "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.6", "45866d958d9ae51cfe8fef0050ab8054d25cba23ace43b88046092aa2c714645", [:make], [], "hexpm", "72b2fc8a8e23d77eed4441137fefa491bbf4a6dc52e9c0045f3f8e92e66243b5"}, + "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.3.2", "43bd07ab88753f5e136e38fddd2a09124bee25733b03361eeb459d0173fc17ab", [:make, :rebar], [{:idna, "~> 1.0.2", [hex: :idna, repo: "hexpm", optional: false]}, {:ssl_verify_hostname, "~> 1.0.5", [hex: :ssl_verify_hostname, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "1.0.3", "d456a8761cad91c97e9788c27002eb3b773adaf5c893275fc35ba4e3434bbd9b", [:rebar3], [], "hexpm"}, + "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], [], "hexpm"}, + "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, + "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.6", "45866d958d9ae51cfe8fef0050ab8054d25cba23ace43b88046092aa2c714645", [:make], [], "hexpm"}, } diff --git a/test/rollbax/logger_test.exs b/test/rollbax/logger_test.exs index c291329..4de5a43 100644 --- a/test/rollbax/logger_test.exs +++ b/test/rollbax/logger_test.exs @@ -33,7 +33,7 @@ defmodule Rollbax.LoggerTest do def init(args), do: {:ok, args} def handle_cast(:raise_elixir, state) do - Map.fetch!(%{}, :nonexistent_key) + _ = Map.fetch!(%{}, :nonexistent_key) {:noreply, state} end end @@ -47,11 +47,11 @@ defmodule Rollbax.LoggerTest do data = assert_performed_request()["data"] # Check the exception. - assert data["body"]["trace"]["exception"] == %{ + assert %{ "class" => "GenServer terminating (KeyError)", - "message" => "key :nonexistent_key not found in: %{}" - } - + "message" => message + } = data["body"]["trace"]["exception"] + assert message =~ "key :nonexistent_key not found" assert [frame] = find_frames_for_current_file(data["body"]["trace"]["frames"]) assert frame["method"] == "MyGenServer.handle_cast/2" @@ -247,7 +247,7 @@ defmodule Rollbax.LoggerTest do ~r[anonymous fn/0 in Rollbax.LoggerTest.(\")?test task with anonymous function raising an error(\")?/1] assert data["custom"]["name"] == inspect(task) - assert data["custom"]["function"] =~ ~r/\A#Function<.* in Rollbax\.LoggerTest/ + assert data["custom"]["function"] =~ ~r/Rollbax\.LoggerTest/ assert data["custom"]["arguments"] == "[]" end) end @@ -270,10 +270,9 @@ defmodule Rollbax.LoggerTest do assert [frame] = find_frames_for_current_file(data["body"]["trace"]["frames"]) assert frame["method"] == "MyModule.raise_error/1" - assert data["custom"] == %{ + assert Map.take(data["custom"], ["name", "function", "started_from"]) == %{ "name" => inspect(task), "function" => "&MyModule.raise_error/1", - "arguments" => ~s(["my message"]), "started_from" => inspect(self()) } end) From 7633d9562fe5b95177071bfb6d5986d78c1db5ed Mon Sep 17 00:00:00 2001 From: Devon Estes Date: Fri, 18 Dec 2020 20:08:56 +0100 Subject: [PATCH 3/4] adding another exit pattern --- lib/rollbax/reporter/standard.ex | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rollbax/reporter/standard.ex b/lib/rollbax/reporter/standard.ex index 5219130..d7395a9 100644 --- a/lib/rollbax/reporter/standard.ex +++ b/lib/rollbax/reporter/standard.ex @@ -91,12 +91,13 @@ defmodule Rollbax.Reporter.Standard do {_, info, stacktrace} when is_tuple(info) -> case elem(info, 0) do - %class{message: message} -> {inspect(class), message, stacktrace, ""} - %class{} -> {inspect(class), inspect(class), stacktrace, ""} - atom when is_atom(atom) -> {inspect(atom), inspect(atom), stacktrace, ""} - {%class{message: message}, inner_stacktrace} -> {inspect(class), message, inner_stacktrace, ""} - {%class{}, inner_stacktrace} -> {inspect(class), inspect(class), inner_stacktrace, ""} - {atom, inner_stacktrace} when is_atom(atom) -> {inspect(atom), inspect(atom), inner_stacktrace, ""} + %class{message: message} -> {inspect(class), message, stacktrace, inspect(info)} + %class{} -> {inspect(class), inspect(class), stacktrace, inspect(info)} + atom when is_atom(atom) -> {inspect(atom), inspect(atom), stacktrace, inspect(info)} + {%class{message: message}, inner_stacktrace} -> {inspect(class), message, inner_stacktrace, inspect(info)} + {%class{}, inner_stacktrace} -> {inspect(class), inspect(class), inner_stacktrace, inspect(info)} + {atom, inner_stacktrace} when is_atom(atom) -> {inspect(atom), inspect(atom), inner_stacktrace, inspect(info)} + {{%class{message: message}, inner_stacktrace}, _} -> {inspect(class), message, inner_stacktrace, inspect(info)} reason -> {"ProcessCrash", "A process crashed", stacktrace, inspect(reason, limit: :infinity)} end end From 01aabfb3d81193fce890618ba7595e9bf12da159 Mon Sep 17 00:00:00 2001 From: Devon Estes Date: Fri, 23 Apr 2021 20:34:00 +0200 Subject: [PATCH 4/4] add missing case clause --- lib/rollbax/reporter/standard.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rollbax/reporter/standard.ex b/lib/rollbax/reporter/standard.ex index d7395a9..325d0b9 100644 --- a/lib/rollbax/reporter/standard.ex +++ b/lib/rollbax/reporter/standard.ex @@ -89,6 +89,9 @@ defmodule Rollbax.Reporter.Standard do {_, %class{message: message}, stacktrace} -> {inspect(class), message, stacktrace, ""} + {:exit, reason, stacktrace} when is_atom(reason) -> + {inspect(reason), inspect(reason), stacktrace, ""} + {_, info, stacktrace} when is_tuple(info) -> case elem(info, 0) do %class{message: message} -> {inspect(class), message, stacktrace, inspect(info)}