ロガー用 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()