2013年3月1日

Python のモジュールスクリプトを使ってアクセスログをだらだら眺める


Python でちょうど良いスクリプトが見つからなかったので、 下記のパッケージのモジュールスクリプト (*) として実装してみました。

アクセスログのファイルが /var/log/nginx/access.log にある場合、 下記のようにして実行できます。

$ pip install clitool
$ tail -f /var/log/nginx/access.log | python -m clitool.accesslog

入力データは標準入力でも引数にファイル指定でも、どちらも受け付けます。 --color オプションを付けると、ステータスコードが400台と500台の場合に色が付きます。 デフォルトのフォーマットであれば Apache でも Nginx でも一緒だと思います。

ログ形式を LTSV (Labeled Tab-separated Values) に変更した方が管理しやすいのかもしれませんが、 過去数年分に遡って変換するとなると気力が持たないので、生ログを処理できるようにしています。

(*) 正式名称が分からなかったので、モジュールとして実行するスクリプト、の意味で使用。

モジュールの実行可能スクリプト

Python のパッケージを作成するとき、スクリプトファイルに if __name__ == '__main__': のブロックを定義しておくと、 Python インタープリタの -m オプションを使って呼び出せます。 今回の場合だと下記のディレクトリ構成で accesslog.py というファイルを実行することになります。

setup.py
clitool/
    |----- __init__.py
    +----- accesslog.py

あまりお行儀の良い方法ではないと思いますが、ちょっとお試しで実装したい、という場合は便利だと思います。 モジュールスクリプトとして隠し機能のようにリリースし、 使い勝手を調整してからコンソールスクリプトとして分離すると良いのではないでしょうか。

pip でインストールしたときにスクリプトとして識別させるためには、 setup.py にエントリーポイントを記述します。 たとえば、ドキュメンテーションビルダー Sphinx をインストールすると sphinx-buildsphinx-quickstart といったコマンドを実行できるようになります。 これは setup.py で次のように記述してあるためです。

entry_points={
    'console_scripts': [
        'sphinx-build = sphinx:main',
        'sphinx-quickstart = sphinx.quickstart:main',
        'sphinx-apidoc = sphinx.apidoc:main',
        'sphinx-autogen = sphinx.ext.autosummary.generate:main',
    ],
    'distutils.commands': [
        'build_sphinx = sphinx.setup_command:BuildDoc',
    ],
},

前述のスクリプトも、もう少し機能を改修したくなったらエントリーポイントを定義しようと思います。 python-tail と組み合わせて、 pure Python の実装で完結すると良いかもしれません。

入出力のハンドリング

Unix/Linux などを全然知らない人にコマンドライン処理に関して説明すると、 多くの場合にパイプライン処理でつまづきます。 ひとつのことに注力したコンポーネントを作成し、それらを組み合わせる、という考えが苦手なようです。 また、GUI からコンピュータに入門すると、CUI の素っ気ない出力には前時代的な感覚を抱くのかもしれません。

ところが、データ量自体が問題になるもの - いわゆるビッグデータ - が登場すると、 「UNIX 哲学」に準じたツールの方が重宝します。

UNIX哲学 (Wikipedia)

これがUNIXの哲学である。 一つのことを行い、またそれをうまくやるプログラムを書け。 協調して動くプログラムを書け。 標準入出力(テキスト・ストリーム)を扱うプログラムを書け。標準入出力は普遍的インターフェースなのだ。 — M. D. マキルロイ, UNIXの四半世紀

上記のスクリプトでは次のように実装してあります。

  • 引数でファイルのパスを複数与えられた場合は、それらを連続して処理する
  • 引数が無い場合は標準入力から読み込む

これだとパイプ "|" と組み合わせて処理しやすくなるはずです。 「パイプ」の基本的な考え方は下記の記事 (英語) が参考になると思います。 標準入力、標準出力、標準エラー出力の3つの組み合わせが図解されています。

蛇足ですが、「UNIXの四半世紀」という書籍には様々な試行錯誤が描かれているので、 歴史的な経緯まで知りたい人にはオススメです。 現在は当たり前のように使っているシステム/ツールの数々が、どのように生まれて来たかが分かります。 何事にも向き不向きがありますので、最初の設計意図も知っておくと選択基準がクリアになるかもしれません。 ただし、日本語が何と言うか難解なので、英語がスラスラ読めるようなら原著を当たった方が良いでしょう。

終わりに

clitool にアクセスログ処理用のモジュールを導入しました。 アクセスログの解釈なんかはたくさんのパッケージが公開されてても良さそうなものですが、 意外と少ないのが不思議でした。 PyPI にアップされているものでは apachelog くらいでしょうか。

アクセスログの処理からその表現までの導入に関しては、下記の記事が参考になると思います。

眼精疲労にならないように CUI とも付き合っていきたいですね。

コメントを投稿