From 82f5c1b225ed310b473195cc89952138088df2a6 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:38:16 -0300 Subject: [PATCH 01/10] feat: reporter --- lib/tower_honeybadger/reporter.ex | 5 +++++ mix.exs | 3 +-- mix.lock | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 lib/tower_honeybadger/reporter.ex create mode 100644 mix.lock diff --git a/lib/tower_honeybadger/reporter.ex b/lib/tower_honeybadger/reporter.ex new file mode 100644 index 0000000..b71e258 --- /dev/null +++ b/lib/tower_honeybadger/reporter.ex @@ -0,0 +1,5 @@ +defmodule TowerHoneybadger.Reporter do + @behaviour Tower.Reporter + + +end diff --git a/mix.exs b/mix.exs index 6d8a5c8..f489242 100644 --- a/mix.exs +++ b/mix.exs @@ -21,8 +21,7 @@ defmodule TowerHoneybadger.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - # {:dep_from_hexpm, "~> 0.3.0"}, - # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + {:tower, github: "mimiquate/tower"} ] end end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..1b84f54 --- /dev/null +++ b/mix.lock @@ -0,0 +1,3 @@ +%{ + "tower": {:git, "https://github.com/mimiquate/tower.git", "140a358d321ede1f3f2225341430143c2ec47d47", []}, +} From 6aaa77796b27bf2477841d534dc09e11ce61d1be Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:51:39 -0300 Subject: [PATCH 02/10] honeybadger client --- lib/tower_honeybadger/honeybadger/client.ex | 35 +++++++++++++++++++++ lib/tower_honeybadger/reporter.ex | 2 -- mix.exs | 1 + mix.lock | 1 + 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 lib/tower_honeybadger/honeybadger/client.ex diff --git a/lib/tower_honeybadger/honeybadger/client.ex b/lib/tower_honeybadger/honeybadger/client.ex new file mode 100644 index 0000000..1ae03d6 --- /dev/null +++ b/lib/tower_honeybadger/honeybadger/client.ex @@ -0,0 +1,35 @@ +defmodule TowerHoneybadger.Honeybadger.Client do + @base_url "https://api.honeybadger.io/v1" + @api_key_header ~c"X-API-Key" + + def post(path, payload) when is_map(payload) do + case :httpc.request( + :post, + { + ~c"#{@base_url}#{path}", + [{@api_key_header, api_key()}], + ~c"application/json", + Jason.encode!(payload) + }, + [ + ssl: [ + verify: :verify_peer, + cacerts: :public_key.cacerts_get() + ] + ], + [] + ) do + {:ok, result} -> + result + |> IO.inspect() + + {:error, reason} -> + reason + |> IO.inspect() + end + end + + defp api_key do + Application.fetch_env!(:tower_honeybadger, :api_key) + end +end diff --git a/lib/tower_honeybadger/reporter.ex b/lib/tower_honeybadger/reporter.ex index b71e258..89a1da4 100644 --- a/lib/tower_honeybadger/reporter.ex +++ b/lib/tower_honeybadger/reporter.ex @@ -1,5 +1,3 @@ defmodule TowerHoneybadger.Reporter do @behaviour Tower.Reporter - - end diff --git a/mix.exs b/mix.exs index f489242..5212b14 100644 --- a/mix.exs +++ b/mix.exs @@ -21,6 +21,7 @@ defmodule TowerHoneybadger.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ + {:jason, "~> 1.4"}, {:tower, github: "mimiquate/tower"} ] end diff --git a/mix.lock b/mix.lock index 1b84f54..9d27f76 100644 --- a/mix.lock +++ b/mix.lock @@ -1,3 +1,4 @@ %{ + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "tower": {:git, "https://github.com/mimiquate/tower.git", "140a358d321ede1f3f2225341430143c2ec47d47", []}, } From a46d641490f8f97563a53799c6ae645e03873147 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:05:12 -0300 Subject: [PATCH 03/10] notice from exception --- lib/tower_honeybadger/honeybadger/notice.ex | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lib/tower_honeybadger/honeybadger/notice.ex diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex new file mode 100644 index 0000000..ceaef11 --- /dev/null +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -0,0 +1,34 @@ +defmodule TowerHoneybadger.Honeybadger.Notice do + def from_exception(exception, stacktrace) when is_exception(exception) and is_list(stacktrace) do + %{ + "error" => %{ + "class" => inspect(exception.__struct__), + "message" => Exception.message(exception), + "backtrace" => backtrace(stacktrace) + } + } + end + + defp backtrace(stacktrace) do + stacktrace + |> Enum.map(fn {m, f, a, location} -> + backtrace_entry = %{ + "method" => Exception.format_mfa(m, f, a) + } + + backtrace_entry = + if location[:file] do + Map.put(backtrace_entry, "file", to_string(location[:file])) + else + backtrace_entry + end + + if location[:line] do + Map.put(backtrace_entry, "number", location[:line]) + else + backtrace_entry + end + end) + |> Enum.reverse() + end +end From 7792b6b7c9701f2c37687a09c309757e5da6d65f Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:06:26 -0300 Subject: [PATCH 04/10] mix format --- lib/tower_honeybadger/honeybadger/notice.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index ceaef11..23b7761 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -1,5 +1,6 @@ defmodule TowerHoneybadger.Honeybadger.Notice do - def from_exception(exception, stacktrace) when is_exception(exception) and is_list(stacktrace) do + def from_exception(exception, stacktrace) + when is_exception(exception) and is_list(stacktrace) do %{ "error" => %{ "class" => inspect(exception.__struct__), From 245edad0b3f8ffc668b0b4077827eaa02af24327 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:15:58 -0300 Subject: [PATCH 05/10] report and plug conn --- lib/tower_honeybadger/honeybadger/notice.ex | 27 ++++++++++++++++++++- lib/tower_honeybadger/reporter.ex | 27 +++++++++++++++++++++ mix.exs | 5 ++-- mix.lock | 4 +++ 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index 23b7761..0fa723f 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -1,6 +1,9 @@ defmodule TowerHoneybadger.Honeybadger.Notice do - def from_exception(exception, stacktrace) + def from_exception(exception, stacktrace, options \\ []) when is_exception(exception) and is_list(stacktrace) do + + plug_conn = Keyword.get(options, :plug_conn) + %{ "error" => %{ "class" => inspect(exception.__struct__), @@ -8,6 +11,7 @@ defmodule TowerHoneybadger.Honeybadger.Notice do "backtrace" => backtrace(stacktrace) } } + |> maybe_put_request_data(plug_conn) end defp backtrace(stacktrace) do @@ -32,4 +36,25 @@ defmodule TowerHoneybadger.Honeybadger.Notice do end) |> Enum.reverse() end + + defp maybe_put_request_data(notice, %Plug.Conn{} = conn) do + notice + |> Map.put("request", request_data(conn)) + end + + defp request_data(%Plug.Conn{} = conn) do + conn = + conn + |> Plug.Conn.fetch_cookies() + |> Plug.Conn.fetch_query_params() + + %{ + "url" => "#{conn.scheme}://#{conn.host}:#{conn.port}#{conn.request_path}", + "params" => + case conn.params do + %Plug.Conn.Unfetched{aspect: :params} -> "unfetched" + other -> other + end + } + end end diff --git a/lib/tower_honeybadger/reporter.ex b/lib/tower_honeybadger/reporter.ex index 89a1da4..7807b2c 100644 --- a/lib/tower_honeybadger/reporter.ex +++ b/lib/tower_honeybadger/reporter.ex @@ -1,3 +1,30 @@ defmodule TowerHoneybadger.Reporter do @behaviour Tower.Reporter + + alias TowerHoneybadger.Honeybadger + + @impl true + def report_exception(exception, stacktrace, metadata \\ %{}) + when is_exception(exception) and is_list(stacktrace) do + if enabled?() do + Honeybadger.Client.post( + "/notices", + Honeybadger.Notice.from_exception(exception, stacktrace, plug_conn: plug_conn(metadata)) + ) + else + IO.puts("TowerHoneybadger NOT enabled, ignoring exception report...") + end + end + + defp plug_conn(%{conn: conn}) do + conn + end + + defp plug_conn(_) do + nil + end + + defp enabled? do + Application.get_env(:tower_honeybadger, :enabled, false) + end end diff --git a/mix.exs b/mix.exs index 5212b14..63ca233 100644 --- a/mix.exs +++ b/mix.exs @@ -14,7 +14,7 @@ defmodule TowerHoneybadger.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger] + extra_applications: [:logger, :public_key] ] end @@ -22,7 +22,8 @@ defmodule TowerHoneybadger.MixProject do defp deps do [ {:jason, "~> 1.4"}, - {:tower, github: "mimiquate/tower"} + {:tower, github: "mimiquate/tower"}, + {:plug, "~> 1.16"} ] end end diff --git a/mix.lock b/mix.lock index 9d27f76..6944878 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,8 @@ %{ "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "plug": {:hex, :plug, "1.16.0", "1d07d50cb9bb05097fdf187b31cf087c7297aafc3fed8299aac79c128a707e47", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cbf53aa1f5c4d758a7559c0bd6d59e286c2be0c6a1fac8cc3eee2f638243b93e"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "tower": {:git, "https://github.com/mimiquate/tower.git", "140a358d321ede1f3f2225341430143c2ec47d47", []}, } From 860f0c70b0ef5e1bb631c39489e345aec91f05df Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:16:13 -0300 Subject: [PATCH 06/10] mix format --- lib/tower_honeybadger/honeybadger/notice.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index 0fa723f..eb80001 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -1,7 +1,6 @@ defmodule TowerHoneybadger.Honeybadger.Notice do def from_exception(exception, stacktrace, options \\ []) when is_exception(exception) and is_list(stacktrace) do - plug_conn = Keyword.get(options, :plug_conn) %{ From 552719132caa73e520250f1cdd5ff8be89f0491e Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:40:35 -0300 Subject: [PATCH 07/10] support wildcard TLS certificates --- lib/tower_honeybadger/honeybadger/client.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tower_honeybadger/honeybadger/client.ex b/lib/tower_honeybadger/honeybadger/client.ex index 1ae03d6..b5c000e 100644 --- a/lib/tower_honeybadger/honeybadger/client.ex +++ b/lib/tower_honeybadger/honeybadger/client.ex @@ -14,7 +14,11 @@ defmodule TowerHoneybadger.Honeybadger.Client do [ ssl: [ verify: :verify_peer, - cacerts: :public_key.cacerts_get() + cacerts: :public_key.cacerts_get(), + # Support wildcard certificates + customize_hostname_check: [ + match_fun: :public_key.pkix_verify_hostname_match_fun(:https) + ] ] ], [] From f9658931275e3edae97a01fa7082df17b4053efe Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:51:15 -0300 Subject: [PATCH 08/10] environment name --- lib/tower_honeybadger/honeybadger/notice.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index eb80001..f702624 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -8,6 +8,9 @@ defmodule TowerHoneybadger.Honeybadger.Notice do "class" => inspect(exception.__struct__), "message" => Exception.message(exception), "backtrace" => backtrace(stacktrace) + }, + "server" => %{ + "environment_name" => Application.fetch_env!(:tower_honeybadger, :environment) } } |> maybe_put_request_data(plug_conn) From 825f135e8a8c22f998e0571c9507b694d11d6557 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:52:37 -0300 Subject: [PATCH 09/10] don't reverse stacktrace --- lib/tower_honeybadger/honeybadger/notice.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index f702624..9076fc9 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -36,7 +36,6 @@ defmodule TowerHoneybadger.Honeybadger.Notice do backtrace_entry end end) - |> Enum.reverse() end defp maybe_put_request_data(notice, %Plug.Conn{} = conn) do From fba29ec258460a46cb63e2daa8ea98f7c891ad65 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:57:19 -0300 Subject: [PATCH 10/10] environemt fun --- lib/tower_honeybadger/honeybadger/notice.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tower_honeybadger/honeybadger/notice.ex b/lib/tower_honeybadger/honeybadger/notice.ex index 9076fc9..76e0961 100644 --- a/lib/tower_honeybadger/honeybadger/notice.ex +++ b/lib/tower_honeybadger/honeybadger/notice.ex @@ -10,7 +10,7 @@ defmodule TowerHoneybadger.Honeybadger.Notice do "backtrace" => backtrace(stacktrace) }, "server" => %{ - "environment_name" => Application.fetch_env!(:tower_honeybadger, :environment) + "environment_name" => environment() } } |> maybe_put_request_data(plug_conn) @@ -58,4 +58,8 @@ defmodule TowerHoneybadger.Honeybadger.Notice do end } end + + defp environment do + Application.fetch_env!(:tower_honeybadge, :environment) + end end