Humanity

Edit the world by your favorite way

ロガー用 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.Noplogger.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() で追記する。
  • 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)}}
    • echomsg_nomsg
      • もし true 値が指定されたら :echo を使って出力する。
      • デフォルトは :echomsg で出力する。
  • Log.new({'output': 'Nop'})

Log.File.new(path)Log.Echomsg.new() としないのは切り替えをより楽にするため。 例えばデバッグのために Log.EchomsgLog.File に切り替えたいとする。 そうすると Log.File.new(path) の場合

  1. s:V.import() の引数 (モジュール名)
  2. Log.new() の引数

の両方を変えなければいけない。

Logger API

  • logger.set_level(level Number)

  • logger.log(level Number, msg String | 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()

TODO

  • Vim 7.4 対応
  • vim-jp/vital.vim に PR する