Web サーバのアクセスログを tail してながめている時にパーセントエンコーディングされた文字列をデコードしたり、コマンドラインで数字を sort -n
したあとにでかい数字を丸めて表示したり("1234567" を "1.2M" に、とか)したくなることが多かったので、その辺を汎用的におこなってくれる humr というツールを書きました。
npm でインストールできます:
npm install -g humr
簡単に動きを確認するには、以下のコマンドを試してみてください:
% echo '/search?q=%E6%97%A5%E6%9C%AC%E8%AA%9E 1234567' | humr
入力のうち読みづらいところが色付きで変換されて出力されたことが分かります。冒頭に書いたようにログなどの入力をパイプしてご利用ください。
仕組み
humr は入力を
- Parser によって各行ごとにフィールドに分解し、さらに
- 各フィールドを Formatter によって人間向けの表現にする、
ということを行っています。Parser は1つしか指定できませんが Formatter は複数存在しえて、可能なものを順番に試していってフィールドを解釈できた場合、その結果に色付けされて出力されます。解釈できなかった場合は入力そのまま。
それぞれ、
- Parser
regexp
: 行を正規表現(デフォルトではs+
)で区切られたフィールドの列とみなすltsv
: 入力を LTSV として解析し、それぞれの値をフィールドとするapache
入力を Apache Common Log フォーマットのログとみなす
- Formatter
si
: SI 接頭辞(k、M など)で数値を略記するurl
: 文字列中のパーセントエンコーディングをデコードするdate
: 日付っぽい文字列を解釈して現在のロケールに合わせて表現するemoji
::+1:
みたいな文字列を絵文字に変換する
ってのを同梱して提供しています。
オプション
オプションなしだと Parser には regexp
、Formatter には上記すべてが利用されるのですが、もちろんオプションでこれを変更できます。
humr -p <parser>[=<arg>] -f [<label>:]<formatter>
-p
は Parser を指定します。例えば -p ltsv
とすると入力は LTSV として解釈され、
size:1234567[tab]path:/search?q=%E6%97%A5%E6%9C%AC%E8%AA%9E
な入力のうち、"1234567" と "/search?..." がフォーマット対象となります。
-f
で Formatter を指定できます(複数指定可)。基本的にすべてのフィールドにすべての Formatter が試みられますが、<label>
を指定することで Formatter を特定のフィールドのみに適用できます。上の例だと
humr -p ltsv -f size:si -f path:url
とすることができます。またフィールドの指定は LTSV のように名前か、単純に1オリジンのインデックスを使えます。
humr -p ltsv -f 1:si -f 2:url
カスタマイズ
humr は起動時に ~/.config/humr/*.js
にあるスクリプトを評価するようになっているので、自前の Parser/Formatter を定義できます。
+function () { 'use strict'; let formatters = require('humr/formatter').registry; class JapaneseNumbersFormatter { format (text, hl) { const NUMBERS = '〇一二三四五六七八九'; return text.replace(/[0-9]/g, (n) => hl(NUMBERS[n])); } } formatters.register('japanese_numbers', JapaneseNumbersFormatter); } ();
以上のような内容のファイルを ~/.config/humr/japanese_numbers.js
(ファイル名は .js
で終わっていればなんでもよいです)として配置しておけば、Formatter として "japanese_numbers" を利用できます。
humr -p ltsv -f size:japanese_numbers
Parser, Formatter としてどんなクラスが期待されているかはそれぞれの定義を見るのが手っ取り早いです: Parser、Formatter。
Parser としては行を受け取り、フィールドを { label?: string; text: string }
に、それ以外の文字列を string
に分解する parse
メソッドを持ったクラスを登録します。
Formatter としてはフィールドの文字列とハイライト関数を受け取り、フォーマット可能であればフォーマットした文字列をハイライト関数に渡したうえで文字列を返す format
メソッドを持ったクラスを登録します。
以上
昔 Ruby で書いてたやつなんだけど、拡張のしやすさ考えると JavaScript で書きたいなってことで Node で書きなおした。役立ててくださいね。日付の解釈に moment を使ってるのが良い選択だったか気になってます。