如何在 Elixir 中 "inspect to file" (或字符串)?

How to "inspect to file" (or to string) in Elixir?

在 Elixir 中,我们可以 IO.inspect anyStructure 得到 anyStructure 的内部打印输出。是否有类似的方法将其输出到文件(或者,作为更灵活的解决方案,输出到字符串)?

我浏览了 debugging and io 上的一些文章,但没有找到解决方案。我也试过

{:ok, file} = File.open("test.log", [:append, {:delayed_write, 100, 20}])
structure = %{ a: 1, b: 2 }
IO.binwrite(file, structure)
File.close file

但这会导致

no function clause matching in IO.binwrite/2 [...]
def binwrite(device, iodata) when is_list(iodata) or is_binary(iodata)

我还用谷歌搜索了一些“长生不老药序列化”和“长生不老药对象到字符串”,但没有找到任何有用的东西(比如 :erlang.term_to_binary 哪个 returns,嗯,二进制)。有没有一种简单的方法可以将 IO.inspect 打印到文件或字符串中的结果相同?

你可以给 IO.inspect 一个额外的参数来告诉它写到哪里:

{:ok, pid} = StringIO.open("")
IO.inspect(pid, %{test: "data"}, label: "IO.inspect options work too \o/")
{:ok, {_in, out}} = StringIO.close(pid)

out # "IO.inspect options work too o/: %{test: \"data\"}\n"

它接受要写入的进程的 pidStringIO 提供了这样一个过程,在关闭时返回一个字符串。

已经有inspect/2功能(和IO.inspect不一样),随它去吧:

#> inspect({1,2,3})
"{1, 2, 3}"

#> h inspect/2
                         def inspect(term, opts \ [])

  @spec inspect(
          Inspect.t(),
          keyword()
        ) :: String.t()

Inspects the given argument according to the Inspect protocol. The second
argument is a keyword list with options to control inspection.


之后你可以对字符串做任何你想做的事情。

您只需将 inspect/2 which returns a binary and File.write/3 或任何其他函数转储到文件中。

File.write("test.log", inspect(%{a: 1, b: 2}, limit: :infinity))

注意 limit: :infinity 选项,如果没有它,在检查 stdout.

时,长结构将被截断以提高可读性

In Elixir, we can IO.inspect anyStructure to get anyStructure's internals printed to output.

这不完全正确; IO.inspect 使用 Inspect protocol. What you see is not the internals of the struct, but whatever that struct's implementation of the Inspect protocol is written to produce. There are different options you can give to inspect, defined in Inspect.Opts,其中之一是 structs: false,它将结构打印为映射。

例如,检查范围结构:

iex> inspect(1..10)
"1..10"
iex> inspect(1..10, structs: false)
"%{__struct__: Range, first: 1, last: 10, step: 1}"

为了回答您的问题并添加到其他答案中,这里有一种方法使用 File.open!/3 重用打开的文件并记录对同一文件的多次检查调用,然后关闭文件:

File.open!("test.log", [:write], fn file ->
  IO.inspect(file, %{ a: 1, b: 2 }, [])
  IO.inspect(file, "logging a string", [])
  IO.inspect(file, DateTime.utc_now!(), [])
  IO.inspect(file, DateTime.utc_now!(), structs: false)
end)

这会生成以下 test.log 文件:

%{a: 1, b: 2}
"logging a string"
~U[2022-04-29 09:51:46.467338Z]
%{
  __struct__: DateTime,
  calendar: Calendar.ISO,
  day: 29,
  hour: 9,
  microsecond: {485474, 6},
  minute: 51,
  month: 4,
  second: 46,
  std_offset: 0,
  time_zone: "Etc/UTC",
  utc_offset: 0,
  year: 2022,
  zone_abbr: "UTC"
}