ロガー用 vital モジュールを作った (ただし Vim 8 専用)
まだ vim-jp/vital.vim には PR してないけど十分使える (使えてる)。
https://github.com/tyru/nesk.vim/blob/0775e7d6fb3502ce52b64911d84b19cde32825aa/autoload/vital/__nesk__/Nesk/Log.vim https://github.com/tyru/nesk.vim/tree/0775e7d6fb3502ce52b64911d84b19cde32825aa/autoload/vital/__nesk__/Nesk/Log
ちなみに使ってるのはここら辺。
https://github.com/tyru/nesk.vim/blob/9d0422c3ac064183d64a137116ab305786c99248/autoload/vital/__nesk__/Nesk.vim#L21-L36 https://github.com/tyru/nesk.vim/blob/9d0422c3ac064183d64a137116ab305786c99248/autoload/vital/__nesk__/Nesk.vim#L395-L420
注意点
- Vim 7.4 には対応してない (Vim 8 専用)
Log.XXX
を実装する場合logger.flush()
を読んだ時点で出力するlogger.log(level, msg)
を読んだ時点では出力しないLog.File
で1行ずつwritefile()
したらめっちゃ遅かったので flush 前提の API とした
- 遅い場合は
{autoflush: 0}
を指定する- デフォルトでは毎行 flush する
- それでも遅い、特に
Log.File
が遅い場合は{file_redir: 1, autoflush: 0}
を指定するwritefile()
での追記が遅い ので:redir
を使える場合は指定すると速くなる- デフォルトじゃないのは
:redir
はネストして使用した場合にエラーとなるため、 プラグインが:redir
を使用している可能性があるのでデフォルトはwritefile()
の追記で実装している
出力先
Log.File
: ファイル出力ロガーLog.Echomsg
::echomsg
出力ロガーLog.Nop
: 何も出力しないロガー
Log.Nop
は logger.log(msg)
を呼ぶのに if
で分岐せずに済むようにするため。
レベルによらず全く出力させたくない場合はこの出力先にする。
上記のモジュールのオブジェクトは new() で生成できる。
Log.new({'output': 'File'})
file_path
(String
)- 例: '/path/to/logfile'
file_format
(Funcref
){options -> {level,msg -> ...}}
のような Funcref を指定する。- 例 (デフォルト値):
{options -> {level,msg -> printf('[%s] %s', get(options.levels[0], level, '?'), msg)}}
file_redir
(Number | Bool
)- もし true 値が指定されたら
:redir
を使ってファイル末尾に追記する。 - デフォルトは
writefile()
で追記する。
- もし true 値が指定されたら
Log.new({'output': 'Echomsg'})
echomsg_hl
:echohl
の引数をリストで渡す。それぞれの要素の位置はoptions.levels
と対応している。- デフォルト値:
[]
- 例:
['None', 'WarningMsg', 'ErrorMsg']
echomsg_format
- File の
file_format
と同じ - デフォルト値は
file_format
と違ってタイムスタンプも出力される{options -> {level,msg -> printf('[%s] %s %s', get(options.levels[0], level, '?'), strftime('%Y-%m-%d %H:%M'), msg)}}
- File の
echomsg_nomsg
- もし true 値が指定されたら
:echo
を使って出力する。 - デフォルトは
:echomsg
で出力する。
- もし true 値が指定されたら
Log.new({'output': 'Nop'})
Log.File.new(path)
や Log.Echomsg.new()
としないのは切り替えをより楽にするため。
例えばデバッグのために Log.Echomsg
を Log.File
に切り替えたいとする。
そうすると Log.File.new(path)
の場合
s:V.import()
の引数 (モジュール名)Log.new()
の引数
の両方を変えなければいけない。
Logger API
logger.set_level(level Number)
logger.log(level Number, msg String | Funcref)
ImplLogger.log()
を呼び出す。- 第二引数に Funcref が渡された場合、その関数を実行した返り値がログ出力される。
logger.flush()
ImplLogger.flush()
を呼び出す。
デフォルトではそれぞれのレベルに対応した以下のメソッドとラベルが追加される。
レベルに対応したメソッドは Logger.new(options Dictionary)
の引数 options.levels
で指定できる。
logger.info(msg String | Funcref)
logger.warn(msg String | Funcref)
logger.error(msg String | Funcref)
logger.INFO
(0)logger.WARN
(1)logger.ERROR
(2)
ラベルは既存メソッド名(Logger.log)と被らなければ何でもいい。
[['INFO', 'info'], ['WARN', 'warn'], ['ERROR', 'error']]
のような配列を渡せば
メソッドとそれに対応するラベル定数を生成してくれる (ちなみにこの値は levels
のデフォルト値)。
この場合それぞれのレベルは INFO < WARN < ERROR
となる。
Dictionary ではなく List で指定するのはこのため。
出力先モジュールの API
出力先のモジュールは Log.new()
によって裏で import() され new() される。
切替可能にするため以下のメソッドを実装しなければならない。以下がその API。
implLogger.log(level Number, msg String)
implLogger.flush()