Skip to content

Commit

Permalink
Provide ability to ignore custom Jason.Encoder implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
andersonmcook authored and sorentwo committed Sep 23, 2024
1 parent 01c47af commit 2412069
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Logger.info(event: %{name: :stuff, safe: false}, user: %User{id: 123})

Outputs nested JSON like this:

```
```json
{
"level":"info",
"message":{
Expand Down Expand Up @@ -92,7 +92,7 @@ If you're using Ecto, you should disable the default logger:
config :my_app, MyApp.Repo, log: false
```

Now replace it with a simple `:telemetry`-based handler that logs structured
Now replace it with a simple [telemetry](https://hexdocs.pm/telemetry/readme.html)-based handler that logs structured
queries with metadata:

```elixir
Expand All @@ -108,6 +108,7 @@ end
```

And attach:

```elixir
:telemetry.attach(
"ecto-logger",
Expand All @@ -117,6 +118,14 @@ And attach:
)
```

Structs that implement [Jason.Encoder](https://hexdocs.pm/jason/Jason.Encoder.html) will use that protocol.
If any implementation is undesirable, as is the case with `Ecto.Association.NotLoaded`
and `Ecto.Schema.Metadata` which both raise errors as of `3.12.3`, it can be disabled at runtime.

```elixir
config, :medea, except: [Ecto.Association.NotLoaded, Ecto.Schema.Metadata]
```

Additional documentation can be found at [https://hexdocs.pm/medea](https://hexdocs.pm/medea).

[form]: https://hexdocs.pm/logger/Logger.Formatter.html
Expand Down
10 changes: 5 additions & 5 deletions lib/medea/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ defmodule Medea.Utils do
def clean(%Time{} = time), do: time

# Structs should implement Jason.Encoder
def clean(%_{} = struct) do
if Encoder.Any != Encoder.impl_for(struct) do
map = clean_struct(struct)
struct!(struct, map)
else
def clean(%module{} = struct) do
if Encoder.Any == Encoder.impl_for(struct) or
module in Application.get_env(:medea, :except, []) do
clean_struct(struct)
else
struct!(struct, clean_struct(struct))
end
end

Expand Down
19 changes: 17 additions & 2 deletions test/medea/translator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ defmodule Medea.TranslatorTest do
require Protocol

defmodule Struct do
@moduledoc false

defstruct id: 1,
private: %{data: :hidden},
public: %{data: :visible}
end

defmodule RaiseStruct do
defimpl Jason.Encoder do
def encode(_, _), do: raise("error")
end

defstruct key: :value
end

describe "translate/4" do
property "all terms are safely encoded to iodata" do
check all message <- message() do
Expand All @@ -38,6 +44,15 @@ defmodule Medea.TranslatorTest do
assert ~s({"id":1,"public":{"data":"visible"}}) == IO.chardata_to_string(iodata)
end

test "custom implementations can be ignored" do
Application.put_env(:medea, :except, [RaiseStruct])

assert {:ok, iodata} =
Translator.translate(:info, :info, :report, {:logger, %RaiseStruct{}})

assert ~s({"key":"value"}) == IO.chardata_to_string(iodata)
end

test "all non-logger formats or reports are ignored" do
assert :none = Translator.translate(:info, :info, :report, {:not, :from, :logger})
end
Expand Down

0 comments on commit 2412069

Please sign in to comment.