Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

use Apache::LogFormat::Compiler in Plack::Middleware::AccessLog #394

Merged
merged 4 commits into from

2 participants

@kazeburo

Apache::LogFormat::Compiler is 6 times faster than the Plack::Middleware::AccessLog's original implements. And it keeps compatibility.

Benchmark code is here. https://gist.github.com/kazeburo/5302308

@miyagawa
Owner

Looks great!

@miyagawa miyagawa merged commit 23d1635 into plack:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 3, 2013
  1. @kazeburo
  2. @kazeburo

    docs

    kazeburo authored
  3. @kazeburo

    requires

    kazeburo authored
  4. @kazeburo
This page is out of date. Refresh to see the latest.
Showing with 38 additions and 103 deletions.
  1. +2 −1  Makefile.PL
  2. +1 −0  cpanfile
  3. +35 −102 lib/Plack/Middleware/AccessLog.pm
View
3  Makefile.PL
@@ -38,7 +38,8 @@ my %WriteMakefileArgs = (
"Test::TCP" => "1.02",
"Try::Tiny" => 0,
"URI" => "1.59",
- "parent" => 0
+ "parent" => 0,
+ "Apache::LogFormat::Compiler" => "0.12",
},
"TEST_REQUIRES" => {
"Test::More" => "0.88",
View
1  cpanfile
@@ -14,6 +14,7 @@ requires 'Test::TCP', '1.02';
requires 'Try::Tiny';
requires 'URI', '1.59';
requires 'parent';
+requires 'Apache::LogFormat::Compiler', '0.12';
on test => sub {
requires 'Test::More', '0.88';
View
137 lib/Plack/Middleware/AccessLog.pm
@@ -2,27 +2,20 @@ package Plack::Middleware::AccessLog;
use strict;
use warnings;
use parent qw( Plack::Middleware );
-use Plack::Util::Accessor qw( logger format );
-
-use Carp ();
-use Plack::Util;
+use Plack::Util::Accessor qw( logger format compiled_format);
+use Apache::LogFormat::Compiler;
my %formats = (
common => '%h %l %u %t "%r" %>s %b',
combined => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"',
);
-use POSIX ();
-
-my $tzoffset = POSIX::strftime("%z", localtime) !~ /^[+-]\d{4}$/ && do {
- require Time::Local;
- my @t = localtime;
- my $seconds = Time::Local::timegm(@t) - Time::Local::timelocal(@t);
- my $min_offset = int($seconds / 60);
- sprintf '%+03d%02u', $min_offset / 60, $min_offset % 60;
-};
-
-my $psgi_reserved = { CONTENT_LENGTH => 1, CONTENT_TYPE => 1 };
+sub prepare_app {
+ my $self = shift;
+ my $fmt = $self->format || "combined";
+ $fmt = $formats{$fmt} if exists $formats{$fmt};
+ $self->compiled_format(Apache::LogFormat::Compiler->new($fmt));
+}
sub call {
my $self = shift;
@@ -30,100 +23,40 @@ sub call {
my $res = $self->app->($env);
+ if ( ref($res) && ref($res) eq 'ARRAY' ) {
+ my $content_length = Plack::Util::content_length($res->[2]);
+ my $log_line = $self->log_line($res->[0], $res->[1], $env, { content_length => $content_length });
+ if ( my $logger = $self->logger ) {
+ $logger->($log_line);
+ }
+ else {
+ $env->{'psgi.errors'}->print($log_line);
+ }
+ return $res;
+ }
+
return $self->response_cb($res, sub {
my $res = shift;
- my $logger = $self->logger || sub { $env->{'psgi.errors'}->print(@_) };
-
my $content_length = Plack::Util::content_length($res->[2]);
- $logger->( $self->log_line($res->[0], $res->[1], $env, { content_length => $content_length }) );
+ my $log_line = $self->log_line($res->[0], $res->[1], $env, { content_length => $content_length });
+ if ( my $logger = $self->logger ) {
+ $logger->($log_line);
+ }
+ else {
+ $env->{'psgi.errors'}->print($log_line);
+ }
});
}
sub log_line {
my($self, $status, $headers, $env, $opts) = @_;
- my $h = Plack::Util::headers($headers);
-
- my $strftime = sub {
- my ($fmt, @time) = @_;
- $fmt =~ s/%z/$tzoffset/g if $tzoffset;
- my $old_locale = POSIX::setlocale(&POSIX::LC_ALL);
- POSIX::setlocale(&POSIX::LC_ALL, 'C');
- my $out = POSIX::strftime($fmt, @time);
- POSIX::setlocale(&POSIX::LC_ALL, $old_locale);
- return $out;
- };
-
- my $block_handler = sub {
- my($block, $type) = @_;
- if ($type eq 'i') {
- $block =~ s/-/_/g;
- $block = uc($block);
- $block = "HTTP_${block}" unless $psgi_reserved->{$block};
- my $val = _safe($env->{$block});
- return defined $val ? $val : "-";
- } elsif ($type eq 'o') {
- return scalar $h->get($block) || "-";
- } elsif ($type eq 't') {
- return "[" . $strftime->($block, localtime) . "]";
- } else {
- Carp::carp("{$block}$type not supported");
- return "-";
- }
- };
-
-
- my %char_handler = (
- '%' => sub { '%' },
- h => sub { $env->{REMOTE_ADDR} || '-' },
- l => sub { '-' },
- u => sub { $env->{REMOTE_USER} || '-' },
- t => sub { "[" . $strftime->('%d/%b/%Y:%H:%M:%S %z', localtime) . "]" },
- r => sub { _safe($env->{REQUEST_METHOD}) . " " . _safe($env->{REQUEST_URI}) .
- " " . $env->{SERVER_PROTOCOL} },
- s => sub { $status },
- b => sub { $opts->{content_length} || $h->get('Content-Length') || "-" },
- T => sub { $opts->{time} ? int($opts->{time}) : "-" },
- D => sub { $opts->{time} ? $opts->{time} * 1000000 : "-" },
- v => sub { $env->{SERVER_NAME} || '-' },
- V => sub { $env->{HTTP_HOST} || $env->{SERVER_NAME} || '-' },
- p => sub { $env->{SERVER_PORT} },
- P => sub { $$ },
- m => sub { _safe($env->{REQUEST_METHOD}) },
- U => sub { _safe($env->{PATH_INFO}) },
- q => sub { ($env->{QUERY_STRING} ne '') ? '?' . _safe($env->{QUERY_STRING}) : '' },
- H => sub { $env->{SERVER_PROTOCOL} },
+ $self->compiled_format->log_line(
+ $env,
+ [$status,$headers],
+ $opts->{content_length},
+ $opts->{time}
);
-
- my $char_handler = sub {
- my $char = shift;
-
- my $cb = $char_handler{$char};
- unless ($cb) {
- Carp::carp "\%$char not supported.";
- return "-";
- }
- $cb->($char);
- };
-
- my $fmt = $self->format || "combined";
- $fmt = $formats{$fmt} if exists $formats{$fmt};
-
- $fmt =~ s!
- (?:
- \%\{(.+?)\}([a-z]) |
- \%(?:[<>])?([a-zA-Z\%])
- )
- ! $1 ? $block_handler->($1, $2) : $char_handler->($3) !egx;
-
- return $fmt . "\n";
-}
-
-sub _safe {
- my $string = shift;
- $string =~ s/([^[:print:]])/"\\x" . unpack("H*", $1)/eg
- if defined $string;
- $string;
}
1;
@@ -175,8 +108,8 @@ default C<development> environment.
format => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"';
Takes a format string (or a preset template C<combined> or C<custom>)
-to specify the log format. This middleware implements a subset of
-L<Apache's LogFormat templates|http://httpd.apache.org/docs/2.0/mod/mod_log_config.html>:
+to specify the log format. This middleware uses L<Apache::LogFormat::Compiler> to
+generate access_log lines. See more details on perldoc L<Apache::LogFormat::Compiler>
%% a percent sign
%h REMOTE_ADDR from the PSGI environment, or -
@@ -219,7 +152,7 @@ output stream by default.
=head1 SEE ALSO
-L<http://httpd.apache.org/docs/2.2/mod/mod_log_config.html> Rack::CustomLogger
+L<Apache::LogFormat::Compiler>, L<http://httpd.apache.org/docs/2.2/mod/mod_log_config.html> Rack::CustomLogger
=cut
Something went wrong with that request. Please try again.