Skip to content

Commit 462a9de

Browse files
author
José Valim
committed
Add missing erlang handler
1 parent c9d6ea5 commit 462a9de

File tree

2 files changed

+242
-0
lines changed

2 files changed

+242
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
defmodule Logger.ErlangHandler do
2+
@moduledoc false
3+
4+
@doc """
5+
Hook required by `:logger`.
6+
"""
7+
def log(%{meta: %{domain: [:otp, :sasl | _]}}, %{sasl_reports?: false}) do
8+
:ok
9+
end
10+
11+
def log(%{meta: %{domain: [:supervisor_report]}}, %{sasl_reports?: false}) do
12+
:ok
13+
end
14+
15+
def log(%{level: level, msg: msg, meta: erl_meta}, _config) do
16+
level = erlang_level_to_elixir_level(level)
17+
18+
Logger.bare_log(level, fn ->
19+
try do
20+
meta = extract_metadata(erl_meta)
21+
22+
case msg do
23+
{:string, string} ->
24+
{string, meta}
25+
26+
{:report, %{label: label, report: report} = complete} when map_size(complete) == 2 ->
27+
translate(level, :report, {label, report}, meta, erl_meta)
28+
29+
{:report, %{label: {:error_logger, _}, format: format, args: args}} ->
30+
translate(level, :format, {format, args}, meta, erl_meta)
31+
32+
{:report, report} ->
33+
translate(level, :report, {:logger, report}, meta, erl_meta)
34+
35+
{format, args} ->
36+
translate(level, :format, {format, args}, meta, erl_meta)
37+
end
38+
rescue
39+
e ->
40+
[
41+
"Failure while translating Erlang's logger event\n",
42+
Exception.format(:error, e, System.stacktrace())
43+
]
44+
end
45+
end)
46+
end
47+
48+
defp erlang_level_to_elixir_level(:emergency), do: :error
49+
defp erlang_level_to_elixir_level(:alert), do: :error
50+
defp erlang_level_to_elixir_level(:critical), do: :error
51+
defp erlang_level_to_elixir_level(:error), do: :error
52+
defp erlang_level_to_elixir_level(:warning), do: :warn
53+
defp erlang_level_to_elixir_level(:notice), do: :info
54+
defp erlang_level_to_elixir_level(:info), do: :info
55+
defp erlang_level_to_elixir_level(:debug), do: :debug
56+
57+
defp extract_metadata(map) do
58+
metadata = []
59+
60+
metadata =
61+
case map do
62+
%{mfa: {mod, fun, arity}} -> [module: mod, function: form_fa(fun, arity)] ++ metadata
63+
_ -> metadata
64+
end
65+
66+
metadata =
67+
case map do
68+
%{file: file, line: line} -> [file: List.to_string(file), line: line] ++ metadata
69+
_ -> metadata
70+
end
71+
72+
metadata =
73+
case map do
74+
%{pid: pid} -> [pid: pid] ++ metadata
75+
_ -> metadata
76+
end
77+
78+
metadata
79+
rescue
80+
_ -> []
81+
end
82+
83+
defp form_fa(fun, arity) do
84+
Atom.to_string(fun) <> "/" <> Integer.to_string(arity)
85+
end
86+
87+
@doc """
88+
Shared translation convenience.
89+
"""
90+
def translate(level, kind, data, meta, erl_meta) do
91+
%{
92+
level: min_level,
93+
truncate: truncate,
94+
translators: translators
95+
} = Logger.Config.__data__()
96+
97+
case translate(translators, min_level, level, kind, data, meta) do
98+
:none -> {translate_fallback(kind, data, erl_meta, truncate), meta}
99+
other -> other
100+
end
101+
end
102+
103+
defp translate([{mod, fun} | t], min_level, level, kind, data, meta) do
104+
case apply(mod, fun, [min_level, level, kind, data]) do
105+
{:ok, chardata, transdata} -> {chardata, Keyword.merge(meta, transdata)}
106+
{:ok, chardata} -> {chardata, meta}
107+
:skip -> :skip
108+
:none -> translate(t, min_level, level, kind, data, meta)
109+
end
110+
end
111+
112+
defp translate([], _min_level, _level, _kind, _data, _meta) do
113+
:none
114+
end
115+
116+
defp translate_fallback(:report, {:logger, data}, %{report_cb: callback} = meta, truncate) do
117+
translate_fallback(:format, callback.(data), meta, truncate)
118+
end
119+
120+
defp translate_fallback(:format, {format, args}, _meta, truncate) do
121+
format
122+
|> Logger.Utils.scan_inspect(args, truncate)
123+
|> :io_lib.build_text()
124+
end
125+
126+
defp translate_fallback(:report, {_type, %{} = data}, _meta, _truncate) do
127+
Kernel.inspect(Map.to_list(data))
128+
end
129+
130+
defp translate_fallback(:report, {_type, data}, _meta, _truncate) do
131+
Kernel.inspect(data)
132+
end
133+
end
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
defmodule Logger.ErlangHandlerTest do
2+
use Logger.Case
3+
@moduletag :logger
4+
5+
defmodule CustomTranslator do
6+
def t(:debug, :info, :format, {'hello: ~p', [:ok]}) do
7+
:skip
8+
end
9+
10+
def t(:debug, :info, :format, {'world: ~p', [:ok]}) do
11+
{:ok, "rewritten"}
12+
end
13+
14+
def t(:debug, :info, :report, {:logger, %{hello: :ok}}) do
15+
:skip
16+
end
17+
18+
def t(:debug, :info, :report, {:logger, %{world: :ok}}) do
19+
{:ok, "rewritten"}
20+
end
21+
22+
def t(:debug, :info, :report, {:logger, %{error: error}}) do
23+
raise(error)
24+
end
25+
26+
def t(_, _, _, _) do
27+
:none
28+
end
29+
end
30+
31+
test "add_translator/1 and remove_translator/1 for error_logger" do
32+
assert Logger.add_translator({CustomTranslator, :t})
33+
34+
assert capture_log(fn ->
35+
:error_logger.info_msg('hello: ~p', [:ok])
36+
end) == ""
37+
38+
assert capture_log(fn ->
39+
:error_logger.info_msg('world: ~p', [:ok])
40+
end) =~ "[info] rewritten"
41+
after
42+
assert Logger.remove_translator({CustomTranslator, :t})
43+
end
44+
45+
test "add_translator/1 and remove_translator/1 for logger formats" do
46+
assert Logger.add_translator({CustomTranslator, :t})
47+
48+
assert capture_log(fn ->
49+
:logger.info('hello: ~p', [:ok])
50+
end) == ""
51+
52+
assert capture_log(fn ->
53+
:logger.info('world: ~p', [:ok])
54+
end) =~ "[info] rewritten"
55+
56+
assert capture_log(fn ->
57+
:logger.info(%{hello: :ok})
58+
end) == ""
59+
60+
assert capture_log(fn ->
61+
:logger.info(%{world: :ok})
62+
end) =~ "[info] rewritten"
63+
after
64+
assert Logger.remove_translator({CustomTranslator, :t})
65+
end
66+
67+
test "handles translation error" do
68+
assert Logger.add_translator({CustomTranslator, :t})
69+
70+
message = capture_log(fn -> :logger.info(%{error: "oops"}) end)
71+
assert message =~ "[info] Failure while translating Erlang's logger event\n"
72+
assert message =~ "** (RuntimeError) oops\n"
73+
after
74+
assert Logger.remove_translator({CustomTranslator, :t})
75+
end
76+
77+
test "converts Erlang metadata" do
78+
Logger.configure_backend(:console, metadata: [:file, :line, :module, :function])
79+
80+
message =
81+
capture_log(fn ->
82+
:logger.info("ok", %{file: 'file.erl', line: 13, mfa: {Foo, :bar, 3}})
83+
end)
84+
85+
assert message =~ "module=Foo"
86+
assert message =~ "function=bar/3"
87+
assert message =~ "file=file.erl"
88+
assert message =~ "line=13"
89+
after
90+
Logger.configure_backend(:console, metadata: [])
91+
end
92+
93+
test "uses reporting callback with Elixir inspection" do
94+
assert capture_log(fn ->
95+
callback = fn %{hello: :world} -> {"~p~n", [:formatted]} end
96+
:logger.info(%{hello: :world}, %{report_cb: callback})
97+
end) =~ "[info] :formatted"
98+
end
99+
100+
test "converts log levels" do
101+
assert capture_log(fn -> :logger.emergency('ok') end) =~ "[error] ok"
102+
assert capture_log(fn -> :logger.alert('ok') end) =~ "[error] ok"
103+
assert capture_log(fn -> :logger.critical('ok') end) =~ "[error] ok"
104+
assert capture_log(fn -> :logger.error('ok') end) =~ "[error] ok"
105+
assert capture_log(fn -> :logger.warning('ok') end) =~ "[warn] ok"
106+
assert capture_log(fn -> :logger.info('ok') end) =~ "[info] ok"
107+
assert capture_log(fn -> :logger.debug('ok') end) =~ "[debug] ok"
108+
end
109+
end

0 commit comments

Comments
 (0)