DBIx::Class で sql_maker が生成した SQL をロギングする方法

DBIC いろいろ使って検証を続けているのですが、O/R Mapper って当たり前だけど万能じゃぁないなぁ〜とすごく思う今日この頃。正直、リレーションとか張りまくってる場合、自前で JOIN とか View 定義して書いた方が遙かに効率の良い SQL が記述できる。

複雑な SQL を表現するには、O/R Mapper だと逆に見づらいなぁ〜と思ったり。

とはいえ、単純な SQL の場合はやはり便利。コードも見た目、OO っぽくてかっこいいし。

でも、どうにも解析できなかったことが一つ。DBIC しか見てないんですが、sql_maker が生成した SQL をロギングする方法がわかりません。なんかコードを深追いしていくと、

DBIx::Class::Storage->debugfh とか DBI_TRACE

に行き着いちゃいます。でもよけいなデバッグ情報多すぎで使い物にならないので、ちょっとした Hack で実現。

- スポンサーリンク -

メンドウなので、モジュールにしてなくってとってつけたような感じで実現。たとえば、Catalyst だと MyApp.pm で

use UNIVERSAL::require;
our $REGISTER_FLG = 0;

〜中略〜

__PACKAGE__->setup;

&_register_dbi_logging() unless($REGISTER_FLG);

sub _register_dbi_logging {
    my $log_yaml = helpdesk->path_to('log.yml');
    no strict 'refs';
    my $superclass = DBIx::Class::Storage::DBI->can('_execute');
    *{"DBIx::Class::Storage::DBI::_execute"} = sub {
        my ($self, $op, $extra_bind, $ident, @args) = @_;
        my ($sql, @bind) = $self->sql_maker->$op($ident, @args);

        Log::Dispatch::Config->require or die $@;
        Log::Dispatch::Configurator::YAML->require or die $@;
        my $yaml = Log::Dispatch::Configurator::YAML->new($log_yaml);
        Log::Dispatch::Config->configure($yaml);
        my $log = Log::Dispatch::Config->instance;

        $sql = qq{"$sql ",};
        map { $sql .= qq{"$_",}; } @bind;
        $log->info($sql);

        &$superclass(@_);
    };

    $REGISTER_FLG = 1;
}

なんて感じで、実装しちゃいました。

ログ設定は、Log::Dispatch::Config と Log::Dispatch::Configurator::YAML 使って設定しています。
ロギングは DBIx::Class::Storage::DBI::_execute を上書きしてロギング機能を追加して、元々のメソッドを call するなんてよく使う?姑息なやり方です。

これで、SQL が execute される度に勝手にロギングされていくので、アプリ側でロギングについて考える必要は無くなります。何かあったときにログから調査。そんな考えが DBIC には抜けているようなので作ってみたところです。

- スポンサーリンク -