Plack::Middlewareでリクエスト終了後になにがしかのか処理をしたい場合
sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    my $ela = Time::HiRes::tv_interval($t0);
};
と書きそうになりますが、これだと $res が CodeRef になるStreaming形式のレスポンスでは正しく処理ができません。 Streaming形式のレスポンスは多くないだろうとか思ってると、Catalystがstreaming形式のレスポンスを返したりするので注意が必要です
そこで Plack::Util::response_cb を使うと楽です
sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    Plack::Util::response_cb($res, sub {
        my $res = shift;
        sub {
            my $chunk = shift;
            if ( ! defined $chunk ) {
                my $ela = Time::HiRes::tv_interval($t0);
            }
            return $chunk;
        }
    });
};
リクエスト処理が終わると、$chunk が undefined になるのでそこで、ごにょごにょ処理します。
ただ response_cb はレスポンスのbodyにフィルタをかける用に作られているためか、Content-Length ヘッダが消されてしまいます。レアケースでContent-Typeを消したくない時、またresponse_cbをスキップして処理を高速化(ベンチマーク未実施)したい時は、responseが配列かどうか確認して処理してしまうのが良いでしょう
sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    if ( ref($res) && ref($res) eq 'ARRAY' ) {
        my $ela = Time::HiRes::tv_interval($t0);
        return $res;
    }
    Plack::Util::response_cb($res, sub {
        my $res = shift;
        sub {
            my $chunk = shift;
            if ( ! defined $chunk ) {
                my $ela = Time::HiRes::tv_interval($t0);
            }
            return $chunk;
        }
    });
};
以上、Plack::Middleware::AxsLogやServerStatus::Liteはこんな実装になってますよという話でした
 
 