From cd58a8680def48b145ad3e1aa0aaa9468a763e85 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:04:19 -0300 Subject: [PATCH] includes exception request data if available --- lib/tower_sentry/reporter.ex | 13 ++++++- mix.exs | 7 +++- test/support/error_test_plug.ex | 16 +++++++++ test/tower_sentry_test.exs | 63 +++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 test/support/error_test_plug.ex diff --git a/lib/tower_sentry/reporter.ex b/lib/tower_sentry/reporter.ex index 9bc0300..2500bf2 100644 --- a/lib/tower_sentry/reporter.ex +++ b/lib/tower_sentry/reporter.ex @@ -7,13 +7,15 @@ defmodule TowerSentry.Reporter do reason: exception, stacktrace: stacktrace, id: id, - metadata: metadata + metadata: metadata, + plug_conn: plug_conn }) do if enabled?() do # TODO: Include plug conn data if available Sentry.capture_exception( exception, stacktrace: stacktrace, + request: request_data(plug_conn), extra: %{id: id, metadata: metadata} ) else @@ -41,6 +43,15 @@ defmodule TowerSentry.Reporter do end end + defp request_data(%Plug.Conn{} = conn) do + %{ + method: conn.method, + url: "#{conn.scheme}://#{conn.host}:#{conn.port}#{conn.request_path}" + } + end + + defp request_data(_), do: %{} + defp enabled? do Sentry.Config.dsn() # Application.get_env(:sentry, :dsn) diff --git a/mix.exs b/mix.exs index d651dbf..6c45dfb 100644 --- a/mix.exs +++ b/mix.exs @@ -6,11 +6,15 @@ defmodule TowerSentry.MixProject do app: :tower_sentry, version: "0.1.0", elixir: "~> 1.16", + elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, deps: deps() ] end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + # Run "mix help compile.app" to learn about applications. def application do [ @@ -27,7 +31,8 @@ defmodule TowerSentry.MixProject do {:hackney, "~> 1.20", optional: true}, # Test - {:bypass, "~> 2.1", only: :test} + {:bypass, "~> 2.1", only: :test}, + {:plug_cowboy, "~> 2.7", only: :test} ] end end diff --git a/test/support/error_test_plug.ex b/test/support/error_test_plug.ex new file mode 100644 index 0000000..e9b9ccd --- /dev/null +++ b/test/support/error_test_plug.ex @@ -0,0 +1,16 @@ +defmodule TowerSentry.ErrorTestPlug do + use Plug.Router + + plug(:match) + plug(:dispatch) + + get "/arithmetic-error" do + 1 / 0 + + send_resp(conn, 200, "OK") + end + + match _ do + send_resp(conn, 404, "Not Found") + end +end diff --git a/test/tower_sentry_test.exs b/test/tower_sentry_test.exs index 57961ba..efd8204 100644 --- a/test/tower_sentry_test.exs +++ b/test/tower_sentry_test.exs @@ -76,6 +76,69 @@ defmodule TowerSentryTest do assert_receive({^ref, :sent}, 500) end + test "reports arithmetic error when a Plug.Conn IS present with Plug.Cowboy", %{bypass: bypass} do + # ref message synchronization trick copied from + # https://github.com/PSPDFKit-labs/bypass/issues/112 + parent = self() + ref = make_ref() + # An ephemeral port hopefully not being in the host running this code + plug_port = 51111 + url = "http://127.0.0.1:#{plug_port}/arithmetic-error" + + Bypass.expect_once(bypass, "POST", "/api/1/envelope", fn conn -> + {:ok, body, conn} = Plug.Conn.read_body(conn) + + assert [_id, _header, event] = String.split(body, "\n", trim: true) + + assert( + { + :ok, + %{ + "level" => "error", + "environment" => "test", + "exception" => [exception], + "request" => %{ + "method" => "GET", + "url" => ^url + } + } + } = Jason.decode(event)# |> IO.inspect() + ) + + assert( + %{ + "type" => "ArithmeticError", + "value" => "bad argument in arithmetic expression", + "stacktrace" => %{"frames" => frames} + } = exception + ) + + assert( + %{ + "function" => "anonymous fn/2 in TowerSentry.ErrorTestPlug.do_match/4", + "filename" => "test/support/error_test_plug.ex", + "lineno" => 8 + } = List.last(frames) + ) + + send(parent, {ref, :sent}) + + conn + |> Plug.Conn.put_resp_content_type("application/json") + |> Plug.Conn.resp(200, Jason.encode!(%{"ok" => true})) + end) + + start_supervised!( + {Plug.Cowboy, plug: TowerSentry.ErrorTestPlug, scheme: :http, port: plug_port} + ) + + capture_log(fn -> + {:ok, _response} = :httpc.request(:get, {url, [{~c"user-agent", "httpc client"}]}, [], []) + end) + + assert_receive({^ref, :sent}, 500) + end + test "reports message", %{bypass: bypass} do # ref message synchronization trick copied from # https://github.com/PSPDFKit-labs/bypass/issues/112