Skip to content

Commit bce6200

Browse files
committed
Add :formatter to ExUnit.CaptureLog options
Mostly modified `:log_capture_on` handler for ExUnit.CaptureServer to respect a `:formatter` option instead `Logger.default_formatter/1`. Updates were made to documentation and tests to reflect this change.
1 parent ef52e13 commit bce6200

File tree

3 files changed

+120
-5
lines changed

3 files changed

+120
-5
lines changed

lib/ex_unit/lib/ex_unit/capture_log.ex

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,17 @@ defmodule ExUnit.CaptureLog do
4444
@compile {:no_warn_undefined, Logger}
4545

4646
@type capture_log_opts :: [
47-
level: Logger.level() | nil
47+
{:level, Logger.level() | nil}
48+
| {:formatter, {module(), term()} | module() | nil}
4849
]
4950

5051
@doc """
5152
Captures Logger messages generated when evaluating `fun`.
5253
5354
Returns the binary which is the captured output. The captured log
54-
messages will be formatted using `Logger.default_formatter/1`. Any
55-
option, besides the `:level`, will be forwarded as an override to
56-
the default formatter.
55+
messages will be formatted using `Logger.default_formatter/1` by
56+
default. Any option, besides `:level` or `formatter`, will be
57+
forwarded as an override to the default formatter.
5758
5859
This function mutes the default logger handler and captures any log
5960
messages sent to Logger from the calling processes. It is possible
@@ -75,6 +76,11 @@ defmodule ExUnit.CaptureLog do
7576
configured in this function, no message will be captured.
7677
The behaviour is undetermined if async tests change Logger level.
7778
79+
It is possible to use an alternative log formatter with `:formatter`,
80+
which must be provided as `{module, config}`. If `:formatter` is not
81+
provided, the formatter configuration from `Logger.default_formatter/1`
82+
will be used.
83+
7884
To get the result of the evaluation along with the captured log,
7985
use `with_log/2`.
8086
"""

lib/ex_unit/lib/ex_unit/capture_server.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ defmodule ExUnit.CaptureServer do
7575
refs = Map.put(config.log_captures, ref, true)
7676

7777
{level, opts} = Keyword.pop(opts, :level)
78-
{formatter_mod, formatter_config} = Logger.default_formatter(opts)
78+
79+
{formatter_mod, formatter_config} =
80+
Keyword.get_lazy(opts, :formatter, fn -> Logger.default_formatter(opts) end)
81+
7982
true = :ets.insert(@ets, {ref, string_io, level || :all, formatter_mod, formatter_config})
8083

8184
if map_size(refs) == 1 do

lib/ex_unit/test/ex_unit/capture_log_test.exs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,112 @@ defmodule ExUnit.CaptureLogTest do
143143
end
144144
end
145145

146+
defmodule ReverseFormatter do
147+
def new(options \\ []) do
148+
{_, opts} = Logger.Formatter.new(options)
149+
{__MODULE__, opts}
150+
end
151+
152+
def format(event, config) do
153+
event
154+
|> reverse()
155+
|> Logger.Formatter.format(config)
156+
end
157+
158+
defp reverse(event) do
159+
msg =
160+
event
161+
|> Logger.Formatter.format_event(:infinity)
162+
|> to_string()
163+
|> String.reverse()
164+
165+
%{event | msg: {:string, msg}}
166+
end
167+
end
168+
169+
describe "with formatter" do
170+
test "log tracking" do
171+
logged =
172+
capture_log([formatter: ReverseFormatter.new()], fn ->
173+
Logger.info("one")
174+
175+
logged = capture_log([formatter: ReverseFormatter.new()], fn -> Logger.error("one") end)
176+
send(test = self(), {:nested, logged})
177+
178+
Logger.warning("two")
179+
180+
spawn(fn ->
181+
Logger.debug("three")
182+
send(test, :done)
183+
end)
184+
185+
receive do: (:done -> :ok)
186+
end)
187+
188+
assert logged
189+
assert logged =~ "[info] eno"
190+
assert logged =~ "[warning] owt"
191+
assert logged =~ "[debug] eerht"
192+
assert logged =~ "[error] eno"
193+
194+
receive do
195+
{:nested, logged} ->
196+
assert logged =~ "[error] eno"
197+
refute logged =~ "[warning] owt"
198+
end
199+
end
200+
201+
test "with_log/2: returns the result and the log" do
202+
{result, log} =
203+
with_log([formatter: ReverseFormatter.new()], fn ->
204+
Logger.error("calculating...")
205+
2 + 2
206+
end)
207+
208+
assert result == 4
209+
assert log =~ "...gnitaluclac"
210+
end
211+
212+
test "respects the :format, :metadata, and :colors options" do
213+
options = [
214+
formatter:
215+
ReverseFormatter.new(
216+
format: "$metadata| $message",
217+
metadata: [:id],
218+
colors: [enabled: false]
219+
)
220+
]
221+
222+
assert {4, log} =
223+
with_log(options, fn ->
224+
Logger.info("hello", id: 123)
225+
2 + 2
226+
end)
227+
228+
assert log == "id=123 | olleh"
229+
end
230+
231+
@tag capture_log: true
232+
test "with_log/2: respect options with capture_log: true" do
233+
{_, opts} =
234+
Logger.Formatter.new(
235+
format: "$metadata| $message",
236+
metadata: [:id],
237+
colors: [enabled: false]
238+
)
239+
240+
options = [formatter: {ReverseFormatter, opts}]
241+
242+
assert {4, log} =
243+
with_log(options, fn ->
244+
Logger.info("hello", id: 123)
245+
2 + 2
246+
end)
247+
248+
assert log == "id=123 | olleh"
249+
end
250+
end
251+
146252
defp wait_capture_removal() do
147253
if ExUnit.CaptureServer in Enum.map(:logger.get_handler_config(), & &1.id) do
148254
Process.sleep(20)

0 commit comments

Comments
 (0)