diff --git a/README.md b/README.md
index 4dbd358f..2b2c83a2 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,9 @@ This is the official Sentry SDK for [Sentry].
### Install
-To use Sentry in your project, add it as a dependency in your `mix.exs` file. Sentry does not install a JSON library nor HTTP client by itself. Sentry will default to trying to use [Jason] for JSON serialization and [Hackney] for HTTP requests, but can be configured to use other ones. To use the default ones, do:
+To use Sentry in your project, add it as a dependency in your `mix.exs` file.
+
+Sentry does not install a JSON library nor HTTP client by itself. Sentry will default to trying to use [Hackney] for HTTP requests, but can be configured to use other ones. To use the default ones, do:
```elixir
defp deps do
@@ -26,12 +28,33 @@ defp deps do
# ...
{:sentry, "~> 10.0"},
- {:jason, "~> 1.4"},
{:hackney, "~> 1.19"}
]
end
```
+For Elixir 1.18+, `JSON` kernel module will be used by default to serialize JSON data.
+
+For Elixir lower than 1.18, Sentry will default to trying to use [Jason] for JSON serialization. To use it, do:
+
+```elixir
+defp deps do
+ [
+ # ...
+
+ {:sentry, "~> 10.0"},
+ {:jason, "~> 1.4"}
+ ]
+end
+```
+
+To use `Jason` or other JSON library for Elixir 1.18+, it is required to define it as a compile-time configuration:
+
+```elixir
+# config.exs
+config :sentry, json_library: Jason
+```
+
### Configuration
Sentry has a range of configuration options, but most applications will have a configuration that looks like the following:
diff --git a/config/config.exs b/config/config.exs
index 1184ea49..d63cc9d2 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -15,4 +15,4 @@ if config_env() == :test do
config :logger, backends: []
end
-config :phoenix, :json_library, Jason
+config :phoenix, :json_library, if(Code.ensure_loaded?(JSON), do: JSON, else: Jason)
diff --git a/lib/sentry/client.ex b/lib/sentry/client.ex
index 87c124fc..cad1d4b7 100644
--- a/lib/sentry/client.ex
+++ b/lib/sentry/client.ex
@@ -207,8 +207,6 @@ defmodule Sentry.Client do
@spec render_event(Event.t()) :: map()
def render_event(%Event{} = event) do
- json_library = Config.json_library()
-
event
|> Event.remove_non_payload_keys()
|> update_if_present(:breadcrumbs, fn bcs -> Enum.map(bcs, &Map.from_struct/1) end)
@@ -218,9 +216,9 @@ defmodule Sentry.Client do
Map.from_struct(message)
end)
|> update_if_present(:request, &(&1 |> Map.from_struct() |> remove_nils()))
- |> update_if_present(:extra, &sanitize_non_jsonable_values(&1, json_library))
- |> update_if_present(:user, &sanitize_non_jsonable_values(&1, json_library))
- |> update_if_present(:tags, &sanitize_non_jsonable_values(&1, json_library))
+ |> update_if_present(:extra, &sanitize_non_jsonable_values/1)
+ |> update_if_present(:user, &sanitize_non_jsonable_values/1)
+ |> update_if_present(:tags, &sanitize_non_jsonable_values/1)
|> update_if_present(:exception, fn list -> Enum.map(list, &render_exception/1) end)
|> update_if_present(:threads, fn list -> Enum.map(list, &render_thread/1) end)
end
@@ -255,11 +253,11 @@ defmodule Sentry.Client do
:maps.filter(fn _key, value -> not is_nil(value) end, map)
end
- defp sanitize_non_jsonable_values(map, json_library) do
+ defp sanitize_non_jsonable_values(map) do
# We update the existing map instead of building a new one from scratch
# due to performance reasons. See the docs for :maps.map/2.
Enum.reduce(map, map, fn {key, value}, acc ->
- case sanitize_non_jsonable_value(value, json_library) do
+ case sanitize_non_jsonable_value(value) do
:unchanged -> acc
{:changed, value} -> Map.put(acc, key, value)
end
@@ -267,15 +265,15 @@ defmodule Sentry.Client do
end
# For performance, skip all the keys that we know for sure are JSON encodable.
- defp sanitize_non_jsonable_value(value, _json_library)
+ defp sanitize_non_jsonable_value(value)
when is_binary(value) or is_number(value) or is_boolean(value) or is_nil(value) do
:unchanged
end
- defp sanitize_non_jsonable_value(value, json_library) when is_list(value) do
+ defp sanitize_non_jsonable_value(value) when is_list(value) do
{mapped, changed?} =
Enum.map_reduce(value, _changed? = false, fn value, changed? ->
- case sanitize_non_jsonable_value(value, json_library) do
+ case sanitize_non_jsonable_value(value) do
:unchanged -> {value, changed?}
{:changed, value} -> {value, true}
end
@@ -288,14 +286,14 @@ defmodule Sentry.Client do
end
end
- defp sanitize_non_jsonable_value(value, json_library)
+ defp sanitize_non_jsonable_value(value)
when is_map(value) and not is_struct(value) do
- {:changed, sanitize_non_jsonable_values(value, json_library)}
+ {:changed, sanitize_non_jsonable_values(value)}
end
- defp sanitize_non_jsonable_value(value, json_library) do
+ defp sanitize_non_jsonable_value(value) do
try do
- json_library.encode(value)
+ Sentry.JSON.encode(value)
catch
_type, _reason -> {:changed, inspect(value)}
else
diff --git a/lib/sentry/config.ex b/lib/sentry/config.ex
index a2407512..91d01a97 100644
--- a/lib/sentry/config.ex
+++ b/lib/sentry/config.ex
@@ -148,11 +148,16 @@ defmodule Sentry.Config do
],
json_library: [
type: {:custom, __MODULE__, :__validate_json_library__, []},
- default: Jason,
+ deprecated: "JSON kernel module is available for Elixir 1.18+.",
type_doc: "`t:module/0`",
doc: """
A module that implements the "standard" Elixir JSON behaviour, that is, exports the
- `encode/1` and `decode/1` functions. If you use the default, make sure to add
+ `encode/1` and `decode/1` functions.
+
+ This configuration should be set at compile-time.
+
+ Defaults to `Jason` if `JSON` kernel module is not available. If you use the
+ default configuration with Elixir version lower than 1.18, make sure to add
[`:jason`](https://hex.pm/packages/jason) as a dependency of your application.
"""
],
@@ -569,9 +574,6 @@ defmodule Sentry.Config do
@spec report_deps?() :: boolean()
def report_deps?, do: fetch!(:report_deps)
- @spec json_library() :: module()
- def json_library, do: fetch!(:json_library)
-
@spec log_level() :: :debug | :info | :warning | :warn | :error
def log_level, do: fetch!(:log_level)
@@ -693,6 +695,8 @@ defmodule Sentry.Config do
{:error, "nil is not a valid value for the :json_library option"}
end
+ def __validate_json_library__(JSON = mod), do: {:ok, mod}
+
def __validate_json_library__(mod) when is_atom(mod) do
try do
with {:ok, %{}} <- mod.decode("{}"),
diff --git a/lib/sentry/envelope.ex b/lib/sentry/envelope.ex
index 649f6e85..6b9e0802 100644
--- a/lib/sentry/envelope.ex
+++ b/lib/sentry/envelope.ex
@@ -2,7 +2,7 @@ defmodule Sentry.Envelope do
@moduledoc false
# https://develop.sentry.dev/sdk/envelopes/
- alias Sentry.{Attachment, CheckIn, ClientReport, Config, Event, UUID}
+ alias Sentry.{Attachment, CheckIn, ClientReport, Event, UUID}
@type t() :: %__MODULE__{
event_id: UUID.t(),
@@ -65,23 +65,21 @@ defmodule Sentry.Envelope do
"""
@spec to_binary(t()) :: {:ok, binary()} | {:error, any()}
def to_binary(%__MODULE__{} = envelope) do
- json_library = Config.json_library()
-
headers_iodata =
case envelope.event_id do
nil -> "{{}}\n"
event_id -> ~s({"event_id":"#{event_id}"}\n)
end
- items_iodata = Enum.map(envelope.items, &item_to_binary(json_library, &1))
+ items_iodata = Enum.map(envelope.items, &item_to_binary/1)
{:ok, IO.iodata_to_binary([headers_iodata, items_iodata])}
catch
{:error, _reason} = error -> error
end
- defp item_to_binary(json_library, %Event{} = event) do
- case event |> Sentry.Client.render_event() |> json_library.encode() do
+ defp item_to_binary(%Event{} = event) do
+ case event |> Sentry.Client.render_event() |> Sentry.JSON.encode() do
{:ok, encoded_event} ->
header = ~s({"type":"event","length":#{byte_size(encoded_event)}})
[header, ?\n, encoded_event, ?\n]
@@ -91,7 +89,7 @@ defmodule Sentry.Envelope do
end
end
- defp item_to_binary(json_library, %Attachment{} = attachment) do
+ defp item_to_binary(%Attachment{} = attachment) do
header = %{"type" => "attachment", "length" => byte_size(attachment.data)}
header =
@@ -100,13 +98,13 @@ defmodule Sentry.Envelope do
into: header,
do: {Atom.to_string(key), value}
- {:ok, header_iodata} = json_library.encode(header)
+ {:ok, header_iodata} = Sentry.JSON.encode(header)
[header_iodata, ?\n, attachment.data, ?\n]
end
- defp item_to_binary(json_library, %CheckIn{} = check_in) do
- case check_in |> CheckIn.to_map() |> json_library.encode() do
+ defp item_to_binary(%CheckIn{} = check_in) do
+ case check_in |> CheckIn.to_map() |> Sentry.JSON.encode() do
{:ok, encoded_check_in} ->
header = ~s({"type":"check_in","length":#{byte_size(encoded_check_in)}})
[header, ?\n, encoded_check_in, ?\n]
@@ -116,8 +114,8 @@ defmodule Sentry.Envelope do
end
end
- defp item_to_binary(json_library, %ClientReport{} = client_report) do
- case client_report |> Map.from_struct() |> json_library.encode() do
+ defp item_to_binary(%ClientReport{} = client_report) do
+ case client_report |> Map.from_struct() |> Sentry.JSON.encode() do
{:ok, encoded_client_report} ->
header = ~s({"type":"client_report","length":#{byte_size(encoded_client_report)}})
[header, ?\n, encoded_client_report, ?\n]
diff --git a/lib/sentry/json.ex b/lib/sentry/json.ex
new file mode 100644
index 00000000..67fbeec1
--- /dev/null
+++ b/lib/sentry/json.ex
@@ -0,0 +1,28 @@
+defmodule Sentry.JSON do
+ @moduledoc false
+
+ @default_library if(Code.ensure_loaded?(JSON), do: JSON, else: Jason)
+ @library Application.compile_env(:sentry, :json_library, @default_library)
+
+ @spec decode(String.t()) :: {:ok, term()} | {:error, term()}
+ if @library == JSON do
+ def decode(binary) do
+ {:ok, JSON.decode!(binary)}
+ rescue
+ error -> {:error, error}
+ end
+ else
+ defdelegate decode(binary), to: @library
+ end
+
+ @spec encode(term()) :: {:ok, String.t()} | {:error, term()}
+ if @library == JSON do
+ def encode(data) do
+ {:ok, JSON.encode!(data)}
+ rescue
+ error -> {:error, error}
+ end
+ else
+ defdelegate encode(data), to: @library
+ end
+end
diff --git a/lib/sentry/transport.ex b/lib/sentry/transport.ex
index a353b2b8..91c6a170 100644
--- a/lib/sentry/transport.ex
+++ b/lib/sentry/transport.ex
@@ -95,7 +95,7 @@ defmodule Sentry.Transport do
defp request(client, endpoint, headers, body) do
with {:ok, 200, _headers, body} <-
client_post_and_validate_return_value(client, endpoint, headers, body),
- {:ok, json} <- Config.json_library().decode(body) do
+ {:ok, json} <- Sentry.JSON.decode(body) do
{:ok, Map.get(json, "id")}
else
{:ok, 429, headers, _body} ->
diff --git a/pages/setup-with-plug-and-phoenix.md b/pages/setup-with-plug-and-phoenix.md
index 00271923..64c2cd1a 100644
--- a/pages/setup-with-plug-and-phoenix.md
+++ b/pages/setup-with-plug-and-phoenix.md
@@ -6,8 +6,8 @@ You can capture errors in Plug (and Phoenix) applications with `Sentry.PlugConte
If you are using Phoenix:
- 1. Add `Sentry.PlugCapture` above the `use Phoenix.Endpoint` line in your endpoint file
- 1. Add `Sentry.PlugContext` below `Plug.Parsers`
+1. Add `Sentry.PlugCapture` above the `use Phoenix.Endpoint` line in your endpoint file
+1. Add `Sentry.PlugContext` below `Plug.Parsers`
```diff
defmodule MyAppWeb.Endpoint
@@ -51,7 +51,7 @@ defmodule MyAppWeb.ErrorView do
def render("500.html", _assigns) do
case Sentry.get_last_event_id_and_source() do
{event_id, :plug} when is_binary(event_id) ->
- opts = Jason.encode!(%{eventId: event_id})
+ opts = JSON.encode!(%{eventId: event_id})
~E"""
@@ -72,8 +72,8 @@ end
If you are in a non-Phoenix Plug application:
- 1. Add `Sentry.PlugCapture` at the top of your Plug application
- 1. Add `Sentry.PlugContext` below `Plug.Parsers` (if it is in your stack)
+1. Add `Sentry.PlugCapture` at the top of your Plug application
+1. Add `Sentry.PlugContext` below `Plug.Parsers` (if it is in your stack)
```diff
defmodule MyApp.Router do
diff --git a/test/envelope_test.exs b/test/envelope_test.exs
index dd887d3d..0acde830 100644
--- a/test/envelope_test.exs
+++ b/test/envelope_test.exs
@@ -15,10 +15,10 @@ defmodule Sentry.EnvelopeTest do
assert {:ok, encoded} = Envelope.to_binary(envelope)
assert [id_line, header_line, event_line] = String.split(encoded, "\n", trim: true)
- assert Jason.decode!(id_line) == %{"event_id" => event.event_id}
- assert %{"type" => "event", "length" => _} = Jason.decode!(header_line)
+ assert decode!(id_line) == %{"event_id" => event.event_id}
+ assert %{"type" => "event", "length" => _} = decode!(header_line)
- assert {:ok, decoded_event} = Jason.decode(event_line)
+ assert decoded_event = decode!(event_line)
assert decoded_event["event_id"] == event.event_id
assert decoded_event["breadcrumbs"] == []
assert decoded_event["environment"] == "test"
@@ -65,29 +65,29 @@ defmodule Sentry.EnvelopeTest do
"..."
] = String.split(encoded, "\n", trim: true)
- assert %{"event_id" => _} = Jason.decode!(id_line)
+ assert %{"event_id" => _} = decode!(id_line)
- assert Jason.decode!(attachment1_header) == %{
+ assert decode!(attachment1_header) == %{
"type" => "attachment",
"length" => 3,
"filename" => "example.dat"
}
- assert Jason.decode!(attachment2_header) == %{
+ assert decode!(attachment2_header) == %{
"type" => "attachment",
"length" => 6,
"filename" => "example.txt",
"content_type" => "text/plain"
}
- assert Jason.decode!(attachment3_header) == %{
+ assert decode!(attachment3_header) == %{
"type" => "attachment",
"length" => 2,
"filename" => "example.json",
"content_type" => "application/json"
}
- assert Jason.decode!(attachment4_header) == %{
+ assert decode!(attachment4_header) == %{
"type" => "attachment",
"length" => 3,
"filename" => "dump",
@@ -105,10 +105,10 @@ defmodule Sentry.EnvelopeTest do
assert {:ok, encoded} = Envelope.to_binary(envelope)
assert [id_line, header_line, event_line] = String.split(encoded, "\n", trim: true)
- assert %{"event_id" => _} = Jason.decode!(id_line)
- assert %{"type" => "check_in", "length" => _} = Jason.decode!(header_line)
+ assert %{"event_id" => _} = decode!(id_line)
+ assert %{"type" => "check_in", "length" => _} = decode!(header_line)
- assert {:ok, decoded_check_in} = Jason.decode(event_line)
+ assert decoded_check_in = decode!(event_line)
assert decoded_check_in["check_in_id"] == check_in_id
assert decoded_check_in["monitor_slug"] == "test"
assert decoded_check_in["status"] == "ok"
@@ -128,10 +128,10 @@ defmodule Sentry.EnvelopeTest do
assert {:ok, encoded} = Envelope.to_binary(envelope)
assert [id_line, header_line, event_line] = String.split(encoded, "\n", trim: true)
- assert %{"event_id" => _} = Jason.decode!(id_line)
- assert %{"type" => "client_report", "length" => _} = Jason.decode!(header_line)
+ assert %{"event_id" => _} = decode!(id_line)
+ assert %{"type" => "client_report", "length" => _} = decode!(header_line)
- assert {:ok, decoded_client_report} = Jason.decode(event_line)
+ assert decoded_client_report = decode!(event_line)
assert decoded_client_report["timestamp"] == client_report.timestamp
assert decoded_client_report["discarded_events"] == [
diff --git a/test/mix/sentry.package_source_code_test.exs b/test/mix/sentry.package_source_code_test.exs
index f60474b9..ab2db4fd 100644
--- a/test/mix/sentry.package_source_code_test.exs
+++ b/test/mix/sentry.package_source_code_test.exs
@@ -61,20 +61,6 @@ defmodule Mix.Tasks.Sentry.PackageSourceCodeTest do
]} = Process.info(self(), :messages)
end
- # This is not really a regression test, but something like was reported in
- # https://github.com/getsentry/sentry-elixir/issues/760. Fixed by adding
- # "loadpaths" and "compile" to the dependencies of this Mix task.
- test "supports custom configured :json_library" do
- defmodule Sentry.ExampleJSON do
- defdelegate encode(term), to: Jason
- defdelegate decode(term), to: Jason
- end
-
- put_test_config(json_library: Sentry.ExampleJSON)
-
- assert :ok = Mix.Task.rerun("sentry.package_source_code")
- end
-
test "supports --no-compile and --no-deps-check" do
assert :ok = Mix.Task.rerun("sentry.package_source_code", ["--no-compile", "--no-deps-check"])
end
diff --git a/test/plug_capture_test.exs b/test/plug_capture_test.exs
index 226fc2cf..8480483f 100644
--- a/test/plug_capture_test.exs
+++ b/test/plug_capture_test.exs
@@ -29,7 +29,7 @@ defmodule Sentry.PlugCaptureTest do
use Phoenix.Endpoint, otp_app: :sentry
use Plug.Debugger, otp_app: :sentry
- plug Plug.Parsers, parsers: [:json], pass: ["*/*"], json_decoder: Jason
+ plug Plug.Parsers, parsers: [:json], pass: ["*/*"], json_decoder: json_library()
plug Sentry.PlugContext
plug PhoenixRouter
end
@@ -45,7 +45,7 @@ defmodule Sentry.PlugCaptureTest do
use Phoenix.Endpoint, otp_app: :sentry
use Plug.Debugger, otp_app: :sentry
- plug Plug.Parsers, parsers: [:json], pass: ["*/*"], json_decoder: Jason
+ plug Plug.Parsers, parsers: [:json], pass: ["*/*"], json_decoder: json_library()
plug Sentry.PlugContext
plug PhoenixRouter
end
diff --git a/test/sentry/client_test.exs b/test/sentry/client_test.exs
index 8e11ab73..bfa98339 100644
--- a/test/sentry/client_test.exs
+++ b/test/sentry/client_test.exs
@@ -65,25 +65,6 @@ defmodule Sentry.ClientTest do
assert rendered.tags.tokens == inspect(MapSet.new([1]))
end
- test "works if the JSON library crashes" do
- defmodule RaisingJSONClient do
- def encode(:crash), do: raise("Oops")
- def encode(term), do: Jason.encode(term)
-
- def decode(term), do: Jason.decode(term)
- end
-
- put_test_config(json_library: RaisingJSONClient)
-
- event = Event.create_event(message: "Something went wrong", extra: %{crasher: :crash})
-
- assert %{} = rendered = Client.render_event(event)
- assert rendered.extra.crasher == ":crash"
- after
- :code.delete(RaisingJSONClient)
- :code.purge(RaisingJSONClient)
- end
-
test "renders stacktrace for threads" do
event =
Event.create_event(
@@ -334,22 +315,6 @@ defmodule Sentry.ClientTest do
assert error.reason == {:request_failure, :econnrefused}
end
- test "logs an error when unable to encode JSON" do
- defmodule BadJSONClient do
- def encode(term) when term == %{}, do: {:ok, "{}"}
- def encode(_term), do: {:error, :im_just_bad}
-
- def decode(term), do: Jason.decode(term)
- end
-
- put_test_config(json_library: BadJSONClient)
- event = Event.create_event(message: "Something went wrong")
-
- assert capture_log(fn ->
- Client.send_event(event, result: :sync)
- end) =~ "the Sentry SDK could not encode the event to JSON: :im_just_bad"
- end
-
test "uses the async sender pool when :result is :none", %{bypass: bypass} do
test_pid = self()
ref = make_ref()
diff --git a/test/sentry/config_test.exs b/test/sentry/config_test.exs
index 3637c712..ff3ca782 100644
--- a/test/sentry/config_test.exs
+++ b/test/sentry/config_test.exs
@@ -158,22 +158,30 @@ defmodule Sentry.ConfigTest do
end
test ":json_library" do
- assert Config.validate!(json_library: Jason)[:json_library] == Jason
+ assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
+ assert Config.validate!(json_library: Jason)[:json_library] == Jason
+ end) =~
+ """
+ :json_library option is deprecated. \
+ JSON kernel module is available for Elixir 1.18+.\
+ """
# Default
- assert Config.validate!([])[:json_library] == Jason
+ assert Config.validate!([])[:json_library] == nil
- assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
- Config.validate!(json_library: Atom)
- end
+ ExUnit.CaptureIO.capture_io(:stderr, fn ->
+ assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
+ Config.validate!(json_library: Atom)
+ end
- assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
- Config.validate!(json_library: nil)
- end
+ assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
+ Config.validate!(json_library: nil)
+ end
- assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
- Config.validate!(json_library: "not a module")
- end
+ assert_raise ArgumentError, ~r/invalid value for :json_library option/, fn ->
+ Config.validate!(json_library: "not a module")
+ end
+ end)
end
test ":before_send" do
diff --git a/test/sentry/json_test.exs b/test/sentry/json_test.exs
new file mode 100644
index 00000000..e029e915
--- /dev/null
+++ b/test/sentry/json_test.exs
@@ -0,0 +1,23 @@
+defmodule Sentry.JSONTest do
+ use ExUnit.Case, async: true
+
+ describe "decode/1" do
+ test "decodes empty object to empty map" do
+ assert Sentry.JSON.decode("{}") == {:ok, %{}}
+ end
+
+ test "returns {:error, reason} if binary is not a JSON" do
+ assert {:error, _reason} = Sentry.JSON.decode("not JSON")
+ end
+ end
+
+ describe "encode/1" do
+ test "encodes empty map to empty object" do
+ assert Sentry.JSON.encode(%{}) == {:ok, "{}"}
+ end
+
+ test "returns {:error, reason} if data cannot be parsed to JSON" do
+ assert {:error, _reason} = Sentry.JSON.encode({:ok, "will fail"})
+ end
+ end
+end
diff --git a/test/sentry/transport_test.exs b/test/sentry/transport_test.exs
index 0b7d7e85..b717cb48 100644
--- a/test/sentry/transport_test.exs
+++ b/test/sentry/transport_test.exs
@@ -153,32 +153,6 @@ defmodule Sentry.TransportTest do
:code.purge(ThrowingHTTPClient)
end
- test "returns an error if the JSON library crashes when decoding the response",
- %{bypass: bypass} do
- envelope = Envelope.from_event(Event.create_event(message: "Hello"))
-
- defmodule CrashingJSONLibrary do
- defdelegate encode(term), to: Jason
-
- def decode("{}"), do: {:ok, %{}}
- def decode(_body), do: raise("I'm a really bad JSON library")
- end
-
- Bypass.expect_once(bypass, "POST", "/api/1/envelope/", fn conn ->
- Plug.Conn.resp(conn, 200, ~s)
- end)
-
- put_test_config(json_library: CrashingJSONLibrary)
-
- assert {:error, %RuntimeError{message: "I'm a really bad JSON library"}, _stacktrace} =
- error(fn ->
- Transport.encode_and_post_envelope(envelope, HackneyClient, _retries = [])
- end)
- after
- :code.delete(CrashingJSONLibrary)
- :code.purge(CrashingJSONLibrary)
- end
-
test "returns an error if the response from Sentry is not valid JSON, and retries",
%{bypass: bypass} do
envelope = Envelope.from_event(Event.create_event(message: "Hello"))
@@ -190,7 +164,7 @@ defmodule Sentry.TransportTest do
Plug.Conn.resp(conn, 200, ~s)
end)
- assert {:request_failure, %Jason.DecodeError{}} =
+ assert {:request_failure, _error} =
error(fn ->
Transport.encode_and_post_envelope(envelope, HackneyClient, _retries = [0])
end)
diff --git a/test/support/example_plug_application.ex b/test/support/example_plug_application.ex
index ee6e88c5..0fc5d637 100644
--- a/test/support/example_plug_application.ex
+++ b/test/support/example_plug_application.ex
@@ -5,6 +5,8 @@ defmodule Sentry.ExamplePlugApplication do
import ExUnit.Assertions
+ alias Sentry.TestHelpers
+
plug Plug.Parsers, parsers: [:multipart, :urlencoded]
plug Sentry.PlugContext
plug :match
@@ -50,7 +52,7 @@ defmodule Sentry.ExamplePlugApplication do
{event_id, :plug} ->
opts =
%{title: "Testing", eventId: event_id}
- |> Jason.encode!()
+ |> TestHelpers.encode!()
"""
diff --git a/test/support/test_error_view.ex b/test/support/test_error_view.ex
index 29dcd7e4..fb8fb6e1 100644
--- a/test/support/test_error_view.ex
+++ b/test/support/test_error_view.ex
@@ -3,12 +3,14 @@ defmodule Sentry.ErrorView do
import Phoenix.HTML, only: [raw: 1]
+ alias Sentry.TestHelpers
+
def render(_, _) do
case Sentry.get_last_event_id_and_source() do
{event_id, :plug} ->
opts =
%{title: "Testing", eventId: event_id}
- |> Jason.encode!()
+ |> TestHelpers.encode!()
assigns = %{opts: opts}
diff --git a/test/support/test_helpers.ex b/test/support/test_helpers.ex
index eca6a617..dcc31b57 100644
--- a/test/support/test_helpers.ex
+++ b/test/support/test_helpers.ex
@@ -1,7 +1,20 @@
defmodule Sentry.TestHelpers do
import ExUnit.Assertions
- alias Sentry.Config
+ @spec decode!(String.t()) :: term()
+ def decode!(binary) do
+ {:ok, data} = Sentry.JSON.decode(binary)
+ data
+ end
+
+ @spec decode!(term()) :: String.t()
+ def encode!(data) do
+ {:ok, binary} = Sentry.JSON.encode(data)
+ binary
+ end
+
+ @spec json_library() :: module()
+ def json_library(), do: if(Code.ensure_loaded?(JSON), do: JSON, else: Jason)
@spec put_test_config(keyword()) :: :ok
def put_test_config(config) when is_list(config) do
@@ -46,7 +59,7 @@ defmodule Sentry.TestHelpers do
@spec decode_envelope!(binary()) :: [{header :: map(), item :: map()}]
def decode_envelope!(binary) do
[id_line | rest] = String.split(binary, "\n")
- {:ok, %{"event_id" => _}} = Config.json_library().decode(id_line)
+ %{"event_id" => _} = decode!(id_line)
decode_envelope_items(rest)
end
@@ -55,8 +68,8 @@ defmodule Sentry.TestHelpers do
|> Enum.chunk_every(2)
|> Enum.flat_map(fn
[header, item] ->
- {:ok, header} = Config.json_library().decode(header)
- {:ok, item} = Config.json_library().decode(item)
+ header = decode!(header)
+ item = decode!(item)
[{header, item}]
[""] ->
diff --git a/test_integrations/phoenix_app/config/config.exs b/test_integrations/phoenix_app/config/config.exs
index 1f7d4a0d..a0ce0afe 100644
--- a/test_integrations/phoenix_app/config/config.exs
+++ b/test_integrations/phoenix_app/config/config.exs
@@ -57,8 +57,7 @@ config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
-# Use Jason for JSON parsing in Phoenix
-config :phoenix, :json_library, Jason
+config :phoenix, :json_library, if(Code.ensure_loaded?(JSON), do: JSON, else: Jason)
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
diff --git a/test_integrations/phoenix_app/test/support/test_helpers.ex b/test_integrations/phoenix_app/test/support/test_helpers.ex
index eca6a617..0b3ab7a3 100644
--- a/test_integrations/phoenix_app/test/support/test_helpers.ex
+++ b/test_integrations/phoenix_app/test/support/test_helpers.ex
@@ -1,7 +1,17 @@
defmodule Sentry.TestHelpers do
import ExUnit.Assertions
- alias Sentry.Config
+ @spec decode!(String.t()) :: term()
+ def decode!(binary) do
+ {:ok, data} = Sentry.JSON.decode(binary)
+ data
+ end
+
+ @spec decode!(term()) :: String.t()
+ def encode!(data) do
+ {:ok, binary} = Sentry.JSON.encode(data)
+ binary
+ end
@spec put_test_config(keyword()) :: :ok
def put_test_config(config) when is_list(config) do
@@ -46,7 +56,7 @@ defmodule Sentry.TestHelpers do
@spec decode_envelope!(binary()) :: [{header :: map(), item :: map()}]
def decode_envelope!(binary) do
[id_line | rest] = String.split(binary, "\n")
- {:ok, %{"event_id" => _}} = Config.json_library().decode(id_line)
+ %{"event_id" => _} = decode!(id_line)
decode_envelope_items(rest)
end
@@ -55,8 +65,8 @@ defmodule Sentry.TestHelpers do
|> Enum.chunk_every(2)
|> Enum.flat_map(fn
[header, item] ->
- {:ok, header} = Config.json_library().decode(header)
- {:ok, item} = Config.json_library().decode(item)
+ header = decode!(header)
+ item = decode!(item)
[{header, item}]
[""] ->