PostgreSQL FDW を作ってSQLでログ検索してみた


こんにちは、寺岡です。
これは TECHSCORE Advent Calendar 2015 の16日目の記事です。

シェル芸人への道のりは険しい

アプリケーションの運用業務では日々入ってくる問い合わせに応えるため、ログが並んだコンソールと睨めっこする時間は決して短くありません。
毎回 grep, cut, uniq に sed や awk なんかも駆使してログ調査をすることになるわけなのですが、これがなかなか大変なわけで……。

熟練の技術者は驚異的なシェル芸を駆使して目的のデータをザクザクと釣り上げてくるのものですが、
そんなアクロバティックな職人技は一朝一夕でに身につくものではないでしょう。

そんな時、アプリエンジニアならこう思うわけです。
「嗚呼、この作業が SQL で出来れば俺だって……」

そんな望みを叶えるためには、 Fluentd などを使ってログをインポートして……というのが王道でしょう。
でも、今回はもっとカジュアルに生ログから検索する方法を試してみました。

PostgreSQL FDW

今回、ログ検索にはPostgreSQLの Foreign Data Wrapper(以降FDW)という機能を利用します。
FDW は PostgreSQL9.1 から搭載された機能で、日本語ドキュメントでは「外部データラッパ」と訳されており、PostgreSQL サーバ外部のデータをデータベース内の仮想的なテーブルとしてマッピングすることができるようになりました。

PostgreSQL9.3 からは postgres_fdw というモジュールが標準で組み込まれており、
別サーバにある PostgreSQL のテーブルをあたかもローカルDBに存在するかのように扱うことができます。
また、FDW モジュールが対応していれば、SELECT だけではなく INSERT、UPDATE、DELETE に加えトランザクションにも対応という高機能っぷりで、もちろん postgres_fdw はこれらに全て対応しています。

さらに凄いのは、FDWで操作できるのはPostgreSQLだけではない点です。
こちらの公式Wikiにあるように、様々な FDW モジュールが公開されており、
MySQLやOracleはもちろん、MongoDB や Redis といった NoSQLDB 、さらには CSV や Twitter まで PostgreSQL で操作できてしまうのです。

作ってみた

今回はログファイルから任意の正規表現でパースしたデータを扱うための、 logfile_fdw という FDW モジュールを作成してみました。
本来 FDW はC言語で実装するものですが、 Multicorn というサードパーティのライブラリを利用すれば、 Python を使ってとても簡単に実装することができます。
どのくらい簡単かというと、初めて Python を書いた筆者が数時間で実装できてしまうくらい簡単です。
( FDW の作り方についてはまた後日ご紹介したいと思います)

インストール

まずは、前準備としてログが保管してあるサーバに以下のインストールを済ませておく必要があります。
yum や apt-get でさくっとインストールしてしまえばよいでしょう。

  • PostgreSQLサーバ (9.1以上)
  • Python, pip

logfile_fdwは以下のコマンドでインストールすることができます。簡単ですね。

1行目は PGXN クライアントのインストールです。これは PostgreSQL の拡張機能のインストールを自動化してくれるツールで、今回は Multicorn のインストールに利用します。
3行目で logfile_fdw をインストールしています。
5行目は pgxn コマンドを使って Multicorn をインストールしています。
(この時、対象の PostgreSQL サーバの pg_config に対してパスが通っている必要があります。複数の PostgreSQL をインストールしている環境では注意してください)

次に PostgreSQL への Multicorn モジュールの組込みと、FDW用サーバ情報の登録を行います。

Multicorn を使った FDW では、サーバ情報登録時に、wrapper オプションで利用する Python の FDW 実装クラスを指定することができます。
このように設定することで、 Multicorn がこのサーバ情報を利用するテーブルへの処理時に Python クラスと PostgreSQL の橋渡しをしてくれるのです。

外部テーブルの作成

今回はサンプルとして yum のログファイル、 /var/log/yum.log を検索してみることにします。
(開発マシンの /var/log の中で一番パースが楽そうだったので選びました)

まず、PostgreSQL サーバの実行ユーザが読み取るために、権限を付与しておきます。

FDW でデータを操作するためには CREATE FOREIGN TABLE 文でテーブル定義を作成する必要があります。

構文は通常の CREATE TABLE 文とほぼ同じですが、最後に SERVER サーバ名 OPTIONS (FDWに渡すオプション) を与えます。
logfile_fdw では、log_pattern にログ行をパースする正規表現(各項目を名前付きグループで指定)、
file_pattern に対象ファイル名のパターンを設定します。
テーブル定義の項目名は log_pattern の正規表現で指定したグループ名と一致させるようにしてください。

外部テーブルの作成まで完了し、これで検索の準備が整いました。

SELECT してみる

先ほど作った yumlog テーブルを SELECT してみましょう。

ご覧のとおり普通に SELECT できています。WHERE も LIMIT もバッチリ効いていますね。
集約関数なども試してみます。

文句なしの結果です。念の為シェルコマンドでの集計と比べてみましょう。

完璧ですね。SQL とコマンドを見比べると、可読性の違いは一目瞭然です。
さらにシェル芸では難しい JOIN なども簡単にできてしまう訳です。
SQL万歳!!

まとめ

筆者は Python 初挑戦なので色々と苦労しましたが、本体40行に満たないスクリプトで
PostgreSQL に新しい機能を追加することができました。

今回作ったソースはlogfile_fdwにアップしてあります。
Apache ログを簡単にパースできる組込みフォーマットなども実装してみたので、よろしければ遊んでみてください。
gz や bz のような圧縮ログへの対応など、今後も手を入れていければと思っています。

データハブとしての可能性が広がる PostgreSQL FDW の世界を、みなさんも試してみてはいかがでしょうか。


Advent Calendar 2015の連載記事

  1. TECHSCORE Advent Calendar 2015
  2. Redshift と PostgreSQL に同時に JDBC 接続する
  3. Lombok で Spice up your Java!
  4. 画像を指定するだけ!非デザイナーでも簡単にそれっぽい配色ができるツールを作ってみた
  5. 新卒文系エンジニアの記録:配属半年間の失敗を振り返ってみた
  6. 非同期処理のすすめ
  7. ioDrive2の導入で支える、そのIOPS - 導入検討編.
  8. GoでパイプラインからSlackに通知する
  9. fuse でオレオレファイルシステムを作ってみた (Haskell で)
  10. Erlang はじめました
  11. ちょっと地味なビルドとリリースの話 (レガシーシステム改革、はじめの一歩)
  12. Java8 最速 boolean[] to Stream 選手権
  13. Google Apps の Directory API にてWebブラウザを介さずに認証する
  14. 風データをビジュアルに表現する
  15. マイクロフレームワーク「Ninja」を使ってみる
  16. 赤ちゃんvimmerからよちよちvimmerにクラスチェンジを果たすためのTips
  17. PostgreSQL FDW を作ってSQLでログ検索してみた
  18. Goで偽名ジェネレータを作りました
  19. 書き込み中に削除されたファイルを救出する
  20. 運用情報更新のススメ
  21. ちゃんと読んでくれましたか?
  22. Presto コネクターを実装する 第三回
  23. Ruby2.3を触ってみる
  24. Git 困ったときのtips集
  25. 5分で読む入門編:Java 8 ラムダ式 コレクション編(2)リストの検索
  26. CloudFront (+ S3) + JWPLAYER で様々なデバイスのブラウザから動画をストリーミング再生する

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です