defmodule NLdoc.Test.Logs do @moduledoc """ This module helps with capturing log output in tests. """ @doc """ Captures logs produced by the given function and returns them as a string. The logs could be formatted using the formatter settings as set in `config/config.exs`, `config/config_test.exs` plus what is configured at runtime. By default, that means the logs could be formatted as Elastic Common Schema (ECS) JSON. Use `decode/1` to JSON decode the logs. ## Examples iex> require Logger iex> logs = :info |> NLdoc.Test.Logs.capture(fn -> Logger.info("Hello") end) iex> logs |> String.contains?("[info] Hello") true """ @spec capture(Logger.level(), (-> any())) :: String.t() def capture(level \\ :debug, fun) do current_level = Logger.level() try do Logger.configure(level: level) ExUnit.CaptureIO.capture_io(:user, fn -> fun.() Logger.flush() end) after Logger.configure(level: current_level) end end @doc """ Captures logs produced by the given function and returns them as a string, alongside the return value of the given function. The logs could be formatted using the formatter settings as set in `config/config.exs`, `config/config_test.exs` plus what is configured at runtime. By default, that means the logs could be formatted as Elastic Common Schema (ECS) JSON. Use `decode/1` to JSON decode these logs. ## Examples iex> require Logger iex> {result, logs} = NLdoc.Test.Logs.with_capture(:info, fn -> ...> Logger.info("Hello") ...> 2 + 2 ...> end) iex> result 4 iex> logs |> String.contains?("[info] Hello") true """ @spec with_capture(Logger.level(), (-> return)) :: {return, String.t()} when return: var def with_capture(level \\ :debug, fun) do current_level = Logger.level() try do Logger.configure(level: level) ExUnit.CaptureIO.with_io(:user, fn -> return = fun.() Logger.flush() return end) after Logger.configure(level: current_level) end end @doc """ Decodes each line in the given log string with `Jason.decode/1`. ## Examples iex> logs = "{\\"message\\": \\"test\\"}\\n{\\"message\\": \\"another test\\"}\\n\\n" iex> logs |> NLdoc.Test.Logs.decode() [%{"message" => "test"}, %{"message" => "another test"}] iex> [%{"message" => "test"}] |> match?(logs |> NLdoc.Test.Logs.decode()) """ @spec decode(String.t()) :: [map()] def decode(logs) do logs |> String.split("\n", trim: true) |> Enum.map(&Jason.decode!/1) end end