Skip to content

Commit aca50d4

Browse files
committed
Handle OTP 20 GenServer log messages (#6071)
Signed-off-by: James Fish <james@fishcakez.com>
1 parent e33f300 commit aca50d4

File tree

2 files changed

+117
-6
lines changed

2 files changed

+117
-6
lines changed

lib/logger/lib/logger/translator.ex

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ defmodule Logger.Translator do
3636
opts = Application.get_env(:logger, :translator_inspect_opts)
3737

3838
case message do
39-
{'** Generic server ' ++ _, [name, last, state, reason]} ->
39+
{'** Generic server ' ++ _, [name, last, state, reason | client]} ->
4040
msg = ["GenServer #{inspect name} terminating", format_stop(reason),
41-
"\nLast message: #{inspect last, opts}"]
41+
"\nLast message#{format_from(client)}: #{inspect last, opts}"]
4242
if min_level == :debug do
43-
{:ok, [msg | "\nState: #{inspect state, opts}"]}
43+
{:ok, [msg, "\nState: #{inspect state, opts}" |
44+
format_client(client)]}
4445
else
4546
{:ok, msg}
4647
end
@@ -375,4 +376,24 @@ defmodule Logger.Translator do
375376

376377
defp format_mfa(mod, fun, :undefined), do: [inspect(mod), ?., to_string(fun) | "/?"]
377378
defp format_mfa(mod, fun, args), do: Exception.format_mfa(mod, fun, args)
379+
380+
defp format_from([]),
381+
do: ""
382+
defp format_from([from]),
383+
do: " (from #{inspect(from)})"
384+
defp format_from([from, stacktrace]) when is_list(stacktrace),
385+
do: " (from #{inspect(from)})"
386+
defp format_from([from, node_name]) when is_atom(node_name),
387+
do: " (from #{inspect(from)} on #{inspect(node_name)})"
388+
389+
defp format_client([from]) do
390+
"\nClient #{inspect(from)} is dead"
391+
end
392+
defp format_client([from, stacktrace]) when is_list(stacktrace) do
393+
["\nClient #{inspect(from)} is alive\n" |
394+
Exception.format_stacktrace(stacktrace)]
395+
end
396+
defp format_client(_) do
397+
[]
398+
end
378399
end

lib/logger/test/logger/translator_test.exs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@ defmodule Logger.TranslatorTest do
55
defmodule MyGenServer do
66
use GenServer
77

8+
def handle_cast(:error, _) do
9+
raise "oops"
10+
end
11+
812
def handle_call(:error, _, _) do
913
raise "oops"
1014
end
15+
def handle_call(:error_on_down, {pid, _}, _) do
16+
mon = Process.monitor(pid)
17+
assert_receive {:DOWN, ^mon, _, _, _}
18+
raise "oops"
19+
end
1120
end
1221

1322
defmodule MyGenEvent do
@@ -93,20 +102,101 @@ defmodule Logger.TranslatorTest do
93102
Application.put_env(:logger, :translator_inspect_opts, [])
94103
end
95104

96-
test "translates GenServer crashes on debug" do
105+
# TODO: Remove this check once we depend only on 20
106+
if :erlang.system_info(:otp_release) >= '20' do
107+
test "translates GenServer crashes on debug" do
108+
{:ok, pid} = GenServer.start(MyGenServer, :ok)
109+
110+
assert capture_log(:debug, fn ->
111+
catch_exit(GenServer.call(pid, :error))
112+
end) =~ ~r"""
113+
\[error\] GenServer #PID<\d+\.\d+\.\d+> terminating
114+
\*\* \(RuntimeError\) oops
115+
.*
116+
Last message \(from #PID<\d+\.\d+\.\d+>\): :error
117+
State: :ok
118+
Client #PID<\d+\.\d+\.\d+> is alive
119+
.*
120+
"""s
121+
end
122+
123+
test "translates GenServer crashes with named client on debug" do
124+
{:ok, pid} = GenServer.start(MyGenServer, :ok)
125+
126+
assert capture_log(:debug, fn ->
127+
Process.register(self(), :named_client)
128+
catch_exit(GenServer.call(pid, :error))
129+
end) =~ ~r"""
130+
\[error\] GenServer #PID<\d+\.\d+\.\d+> terminating
131+
\*\* \(RuntimeError\) oops
132+
.*
133+
Last message \(from :named_client\): :error
134+
State: :ok
135+
Client :named_client is alive
136+
.*
137+
"""s
138+
end
139+
140+
test "translates GenServer crashes with dead client on debug" do
141+
{:ok, pid} = GenServer.start(MyGenServer, :ok)
142+
143+
assert capture_log(:debug, fn ->
144+
mon = Process.monitor(pid)
145+
spawn_link(fn() ->
146+
catch_exit(GenServer.call(pid, :error_on_down, 0))
147+
end)
148+
assert_receive {:DOWN, ^mon, _, _, _}
149+
end) =~ ~r"""
150+
\[error\] GenServer #PID<\d+\.\d+\.\d+> terminating
151+
\*\* \(RuntimeError\) oops
152+
.*
153+
Last message \(from #PID<\d+\.\d+\.\d+>\): :error_on_down
154+
State: :ok
155+
Client #PID<\d+\.\d+\.\d+> is dead
156+
"""s
157+
end
158+
else
159+
test "translates GenServer crashes on debug" do
160+
{:ok, pid} = GenServer.start(MyGenServer, :ok)
161+
162+
assert capture_log(:debug, fn ->
163+
catch_exit(GenServer.call(pid, :error))
164+
end) =~ ~r"""
165+
\[error\] GenServer #PID<\d+\.\d+\.\d+> terminating
166+
\*\* \(RuntimeError\) oops
167+
.*
168+
Last message: :error
169+
State: :ok
170+
"""s
171+
end
172+
end
173+
174+
test "translates GenServer crashes with no client" do
97175
{:ok, pid} = GenServer.start(MyGenServer, :ok)
98176

99177
assert capture_log(:debug, fn ->
100-
catch_exit(GenServer.call(pid, :error))
178+
mon = Process.monitor(pid)
179+
GenServer.cast(pid, :error)
180+
assert_receive {:DOWN, ^mon, _, _, _}
101181
end) =~ ~r"""
102182
\[error\] GenServer #PID<\d+\.\d+\.\d+> terminating
103183
\*\* \(RuntimeError\) oops
104184
.*
105-
Last message: :error
185+
Last message: {:"\$gen_cast", :error}
106186
State: :ok
107187
"""s
108188
end
109189

190+
test "translates GenServer crashes with no client on debug" do
191+
{:ok, pid} = GenServer.start(MyGenServer, :ok)
192+
193+
refute capture_log(:debug, fn ->
194+
mon = Process.monitor(pid)
195+
GenServer.cast(pid, :error)
196+
assert_receive {:DOWN, ^mon, _, _, _}
197+
end) =~ "Client"
198+
end
199+
110200
test "translates :gen_event crashes" do
111201
{:ok, pid} = :gen_event.start()
112202
:ok = :gen_event.add_handler(pid, MyGenEvent, :ok)

0 commit comments

Comments
 (0)