runtime_infoをファイルに出力する

iex には runtime_info/0 という関数が用意されている。実行すると現在動作している VM の情報をターミナルへと表示する。

この実装は関数の中で IO.puts/2 IO.write/2 を使って行われている。また runtime_info/0 は IO の出力先を変更できるような引数は取らない。この制約下で、通常の IO.puts/2 は標準出力へと出力し、 runtime_info の結果のみをファイルへと出力することはできるだろうか。プログラムを書き換えなくとも Process.group_leader/2 をうまく使うとできることがわかった。

❯ iex
Erlang/OTP 22 [erts-10.6.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Interactive Elixir (1.10.0-rc.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> runtime_info()

## System and architecture

Elixir version:     1.10.0-rc.0
Erlang/OTP version: 22
ERTS version:       10.6.2
Compiled for:       x86_64-apple-darwin18.7.0
Schedulers:         8
Schedulers online:  8

## Memory

Total:              36 MB
Atoms:              331 KB
Binaries:           256 KB
Code:               5 MB
ETS:                538 KB
Processes:          19 MB

## Statistics / limits

Uptime:             4 seconds
Run queue:          1
Atoms:              11856 / 1048576 (1% used)
ETS:                23 / 8192 (0% used)
Ports:              3 / 65536 (0% used)
Processes:          57 / 262144 (0% used)

Showing topics:     [:system, :memory, :limits]
Additional topics:  [:allocators, :applications]

To view a specific topic call runtime_info(topic)
# puts_tuntime_info_to_file.exs

#
# セットアップ
#
# 出力の変更先に使うため、ファイルのio_deviceを取得する
file = File.open!("./runtime_info.txt", [:write])
# 変更後また元に戻せるように、デフォルトのgroup_leaderを変数に保持しておく
# (デフォルトのgroup_leaderは文字を標準出力へと出力するio_deviceとなっている)
original = Process.group_leader()
# 新しいgroup_leaderとして、文字をファイルへと出力するio_deviceを設定する
Process.group_leader(self(), file)

#
# 実行
#
IEx.Helpers.runtime_info()

#
# 後始末
#
# group_leaderをデフォルトへと戻す
Process.group_leader(self(), original)
# デフォルトのgroup_leaderに戻っているので、この文字列は標準出力から出力される
IO.puts("back to original group leader")

ので期待通りに動いていることがわかる。

❯ cat runtime_info.txt
cat: runtime_info.txt: No such file or directory

~/src/niku.github.io source*
❯ elixir puts_runtime_info_to_file.exs
back to original group leader

~/src/niku.github.io source*
❯ cat runtime_info.txt

## System and architecture

Elixir version:     1.10.0-rc.0
Erlang/OTP version: 22
ERTS version:       10.6.2
Compiled for:       x86_64-apple-darwin18.7.0
Schedulers:         8
Schedulers online:  8

## Memory

Total:              24 MB
Atoms:              387 KB
Binaries:           14 KB
Code:               7 MB
ETS:                547 KB
Processes:          4 MB

## Statistics / limits

Uptime:             0 seconds
Run queue:          1
Atoms:              14791 / 1048576 (1% used)
ETS:                19 / 8192 (0% used)
Ports:              3 / 65536 (0% used)
Processes:          48 / 262144 (0% used)

Showing topics:     [:system, :memory, :limits]
Additional topics:  [:allocators, :applications]

To view a specific topic call runtime_info(topic)

このやり方は ExUnit の CaputureIO が標準出力を取得していたのを思い出して 調べた結果 思いついた。