tokuhirom's Blog

データベースへのクエリをトレースする PSGI ミドルウェア

これをつくってる途中で、DBIx::QueryLog がめんどくなってきて DBIx::Tracer をつくってしまったのでした。

なんか無駄に threashold_sum_total_cnt とかで設定できるようになってるけど、手でいじって適当に自分のプロジェクトにぶっこんだ方が便利な気がします。

とくに CPAN にあげたりする予定はありません。

package Plack::Middleware::DBProfile;
use strict;
use warnings;
use utf8;
use 5.10.0;

use parent qw(Plack::Middleware);

use Plack::Util::Accessor qw(path_re threashold_sum_total_cnt threashold_total_time threashold_total_cnt);
use DBIx::Tracer;
use List::Util qw(sum);

sub prepare_app {
    my $self = shift;
}

sub call {
    my($self, $env) = @_;

    if (!$self->path_re || ($env->{PATH_INFO} =~ $self->path_re)) {
        my %QUERY_LOG;
        my $tracer = DBIx::Tracer->new(
            sub {
                my %args = @_;
                push @{$QUERY_LOG{$args{sql}}->{times}}, $args{time};
                ($QUERY_LOG{$args{sql}}->{sql} = $args{sql}) =~ s/\n//;
                $QUERY_LOG{$args{sql}}->{total_time} += $args{time};
                $QUERY_LOG{$args{sql}}->{total_cnt}++;
            }
        );
        my $res = $self->app->($env);

        my $sum_total_time = (sum map { $_->{total_time} } values %QUERY_LOG) || 0;
        my $sum_total_cnt  = (sum map { $_->{total_cnt} } values %QUERY_LOG) || 0;

        if (!defined $self->threashold_sum_total_cnt || $sum_total_cnt < $self->threashold_sum_total_cnt) {
            for my $row (sort { $a->{total_time} <=> $b->{total_time} } values %QUERY_LOG) {
                if (!defined $self->threashold_total_time || $row->{total_time} > $self->threashold_total_time) {
                    if (!defined $self->threashold_total_cnt || $row->{total_cnt} > $self->threashold_total_cnt) {
                        say "----";
                        say $row->{sql};
                        say "total time: $row->{total_time}";
                        say "count: $row->{total_cnt}";
                    }
                }
            }
            if ($sum_total_cnt > 0) {
                say "[$env->{PATH_INFO}] Total $sum_total_cnt queries.";
            }
        }
        return $res;
    } else {
        return $self->app->($env);
    }
}

1;