目的
マルチプロセスのコードをデバッグするためには、ログをそれぞれ別の場所に出すようにしないと大変。個別のファイルに書くようにして、別の窓でtail -f
とかすればいいわけなのだけど、毎回やるのはかなり煩雑。なので自動的にウィンドウをオープンしてその中にログを書くようにしたい。
方法
やることは非常に単純で、ログをテンポラリファイルに書き出すようにしておいて、xterm
の中でそのファイルにtail -f
をすればいい。MacユーザなのでTerminalを使えればもっと良かったのだけど、制御する方法がわからなかったので、xtermを使う。macでxtermを使うにはXquatzが必要だが、brewで簡単に入る。
実装
AbstractLogger
のサブタイプを作る。この際にConsoleLogger
を継承する。継承にはこの記事に書いたようにMacroTools
の@forward
を用いた。
ここで一つ問題が。ConsoleLogger
は書き出したあとにstreamをフラッシュしない。通常はstderr
に書き出していて、stderr
が自動的にフラッシュするので問題ないようなのだが、自動フラッシュになっていないストリームだと、リアルタイムに書き出されないので困る。ということでhandle_message
メソッドだけはオーバライドしている。と言っても、ConsoleLogger
のhandle_message
を呼出したあとで、streamを不ファッシュしているだけ。
module XtermLogging
export XtermLogger
import MacroTools
import Logging
import Dates
function timed_metafmt(level, _module, group, id, file, line)
color, prefix, suffix =
Logging.default_metafmt(level, _module, group, id, file, line)
timestamp = Dates.now()
prefix2 = "$timestamp : $prefix"
return color, prefix2, suffix
end
struct XtermLogger <: Base.CoreLogging.AbstractLogger
sl::Logging.ConsoleLogger
filename
proc::Base.Process
end
function XtermLogger(title, level=Logging.Info;
display=":0",
meta_formatter=timed_metafmt, show_limited=true,
right_justify=0)
tmpfile, stream = Base.Filesystem.mktemp()
xterm = "/usr/local/bin/xterm"
proc = open(`$xterm -display $display -title $title -e tail -f $tmpfile`)
XtermLogger(Logging.ConsoleLogger(stream, level;
meta_formatter=meta_formatter,
show_limited=show_limited,
right_justify=right_justify),
tmpfile, proc)
end
@MacroTools.forward XtermLogger.sl Base.CoreLogging.shouldlog,
Base.CoreLogging.min_enabled_level,
Base.CoreLogging.catch_exceptions
function Base.CoreLogging.handle_message(logger::XtermLogger, level, message, _module, group, id,
filepath, line; maxlog=nothing, kwargs...)
Base.CoreLogging.handle_message(logger.sl, level, message, _module, group, id, filepath, line; maxlog, kwargs...)
flush(logger.sl.stream)
end
end
デフォルトで時刻を表示するtimed_metafmt
を用いるようにしている。このあたりの詳細はこの記事に書いてある。
使い方
XtermLogging
とLogging
をusing
でインポートする。
push!(LOAD_PATH, pwd())
using XtermLogging
using Logging
XtermLogger
を作る。引数はウィンドウのタイトル。
xlogger = XtermLogger("test0")
つくったLoggerは普通のLoggerとして使える。
with_logger(xlogger) do
@error("test $i")
end