The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Build.PL 11
Changes 017
MANIFEST 01
META.json 87
META.yml 1617
README.md 77
cpanfile 32
lib/Apache/LogFormat/Compiler.pm 10671
minil.toml 02
9 files changed (This is a version diff) 141125
@@ -34,7 +34,7 @@ my %args = (
     test_files           => ((-d '.git' || $ENV{RELEASE_TESTING}) && -d 'xt') ? 't/ xt/' : 't/',
     recursive_test_files => 1,
 
-    
+
 );
 if (-d 'share') {
     $args{share_dir} = 'share';
@@ -1,5 +1,22 @@
 Revision history for Perl extension Apache::LogFormat::Compiler
 
+0.32 2014-06-09T02:00:15Z
+
+   - change perl version requirements to 5.8.1
+
+0.30 2014-01-28T07:59:07Z
+
+   - add docs abount POSIX::strftime::Compiler
+   - non trial release
+
+0.24 2014-01-27T03:12:16Z
+
+   - [TRIAL] switch to using POSIX::strftime::Compiler
+
+0.23 2014-01-16T15:53:09Z
+
+   - fixed POSIX::setlocale fails on system without locales (Android) #6 (Thank you dex4er)
+
 0.22 2014-01-08T00:25:14Z
 
    - skip tz test on Windows.
@@ -6,6 +6,7 @@ README.md
 cpanfile
 eg/logbench.pl
 lib/Apache/LogFormat/Compiler.pm
+minil.toml
 t/00_compile.t
 t/01_basic.t
 t/02_custom.t
@@ -4,7 +4,7 @@
       "Masahiro Nagano <kazeburo@gmail.com>"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "Minilla/v0.11.0",
+   "generated_by" : "Minilla/v1.1.0",
    "license" : [
       "perl_5"
    ],
@@ -36,7 +36,8 @@
       "develop" : {
          "requires" : {
             "Test::CPAN::Meta" : "0",
-            "Test::MinimumVersion" : "0.10108",
+            "Test::MinimumVersion::Fast" : "0.04",
+            "Test::PAUSE::Permissions" : "0.04",
             "Test::Pod" : "1.41",
             "Test::Spellunker" : "v0.2.7"
          }
@@ -44,11 +45,9 @@
       "runtime" : {
          "requires" : {
             "POSIX" : "0",
+            "POSIX::strftime::Compiler" : "0.30",
             "Time::Local" : "0",
-            "perl" : "5.008004"
-         },
-         "suggests" : {
-            "POSIX::strftime::GNU" : "0"
+            "perl" : "5.008001"
          }
       },
       "test" : {
@@ -65,7 +64,7 @@
    "provides" : {
       "Apache::LogFormat::Compiler" : {
          "file" : "lib/Apache/LogFormat/Compiler.pm",
-         "version" : "0.22"
+         "version" : "0.32"
       }
    },
    "release_status" : "stable",
@@ -79,7 +78,7 @@
          "web" : "https://github.com/kazeburo/Apache-LogFormat-Compiler"
       }
    },
-   "version" : "0.22",
+   "version" : "0.32",
    "x_contributors" : [
       "Florian Schlichting <fsfs@debian.org>",
       "Piotr Roszatycki <piotr.roszatycki@gmail.com>"
@@ -3,22 +3,22 @@ abstract: 'Compile a log format string to perl-code '
 author:
   - 'Masahiro Nagano <kazeburo@gmail.com>'
 build_requires:
-  HTTP::Request::Common: 0
-  Test::MockTime: 0
-  Test::More: 0.98
-  Test::Requires: 0
-  Try::Tiny: 0.12
-  URI::Escape: 1.60
+  HTTP::Request::Common: '0'
+  Test::MockTime: '0'
+  Test::More: '0.98'
+  Test::Requires: '0'
+  Try::Tiny: '0.12'
+  URI::Escape: '1.60'
 configure_requires:
-  CPAN::Meta: 0
-  CPAN::Meta::Prereqs: 0
-  Module::Build: 0.38
+  CPAN::Meta: '0'
+  CPAN::Meta::Prereqs: '0'
+  Module::Build: '0.38'
 dynamic_config: 0
-generated_by: 'Minilla/v0.11.0, CPAN::Meta::Converter version 2.132830'
+generated_by: 'Minilla/v1.1.0, CPAN::Meta::Converter version 2.141170'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
+  version: '1.4'
 name: Apache-LogFormat-Compiler
 no_index:
   directory:
@@ -33,16 +33,17 @@ no_index:
 provides:
   Apache::LogFormat::Compiler:
     file: lib/Apache/LogFormat/Compiler.pm
-    version: 0.22
+    version: '0.32'
 requires:
-  POSIX: 0
-  Time::Local: 0
-  perl: 5.008004
+  POSIX: '0'
+  POSIX::strftime::Compiler: '0.30'
+  Time::Local: '0'
+  perl: '5.008001'
 resources:
   bugtracker: https://github.com/kazeburo/Apache-LogFormat-Compiler/issues
   homepage: https://github.com/kazeburo/Apache-LogFormat-Compiler
   repository: git://github.com/kazeburo/Apache-LogFormat-Compiler.git
-version: 0.22
+version: '0.32'
 x_contributors:
   - 'Florian Schlichting <fsfs@debian.org>'
   - 'Piotr Roszatycki <piotr.roszatycki@gmail.com>'
@@ -1,3 +1,4 @@
+[![Build Status](https://travis-ci.org/kazeburo/Apache-LogFormat-Compiler.png?branch=master)](https://travis-ci.org/kazeburo/Apache-LogFormat-Compiler)
 # NAME
 
 Apache::LogFormat::Compiler - Compile a log format string to perl-code 
@@ -25,7 +26,7 @@ Compile a log format string to perl-code. For faster generation of access\_log l
 
     Takes a format string (or a preset template `combined` or `custom`)
     to specify the log format. This module implements a subset of
-    [Apache's LogFormat templates](http://httpd.apache.org/docs/2.0/mod/mod\_log\_config.html):
+    [Apache's LogFormat templates](http://httpd.apache.org/docs/2.0/mod/mod_log_config.html):
 
         %%    a percent sign
         %h    REMOTE_ADDR from the PSGI environment, or -
@@ -87,6 +88,10 @@ Compile a log format string to perl-code. For faster generation of access\_log l
             $app
         };
 
+# ABOUT POSIX::strftime::Compiler
+
+This module uses [POSIX::strftime::Compiler](https://metacpan.org/pod/POSIX::strftime::Compiler) for generate datetime string. POSIX::strftime::Compiler provides GNU C library compatible strftime(3). But this module will not affected by the system locale. This feature is useful when you want to write loggers, servers and portable applications.
+
 # ADD CUSTOM FORMAT STRING
 
 Apache::LogFormat::Compiler allows one to add a custom format string
@@ -114,18 +119,13 @@ Your sub is called with two or three arguments: the content inside the `{}`
 from the format (block\_handlers only), the PSGI environment (`$env`),
 and the ArrayRef of the response. It should return the string to be logged.
 
-# RECOMMENDED MODULE
-
-If [POSIX::strftime::GNU](http://search.cpan.org/perldoc?POSIX::strftime::GNU) is available, Apache::LogFormat::Compiler uses it. 
-It's good for Windows and old Unices have limited strftime's formatting.
-
 # AUTHOR
 
 Masahiro Nagano <kazeburo@gmail.com>
 
 # SEE ALSO
 
-[Plack::Middleware::AccessLog](http://search.cpan.org/perldoc?Plack::Middleware::AccessLog), [http://httpd.apache.org/docs/2.2/mod/mod\_log\_config.html](http://httpd.apache.org/docs/2.2/mod/mod\_log\_config.html)
+[Plack::Middleware::AccessLog](https://metacpan.org/pod/Plack::Middleware::AccessLog), [http://httpd.apache.org/docs/2.2/mod/mod\_log\_config.html](http://httpd.apache.org/docs/2.2/mod/mod_log_config.html)
 
 # LICENSE
 
@@ -1,8 +1,7 @@
 requires 'POSIX';
 requires 'Time::Local';
-requires 'perl', '5.008004';
-
-suggests 'POSIX::strftime::GNU';
+requires 'perl', '5.008001';
+requires 'POSIX::strftime::Compiler', '0.30';
 
 on test => sub {
     requires 'HTTP::Request::Common';
@@ -2,12 +2,18 @@ package Apache::LogFormat::Compiler;
 
 use strict;
 use warnings;
-use 5.008004;
+use 5.008001;
 use Carp;
-use POSIX ();
-use Time::Local qw//;
+use POSIX::strftime::Compiler qw//;
+use constant {
+    ENVS => 0,
+    RES => 1,
+    LENGTH => 2,
+    REQTIME => 3,
+    TIME => 4,
+};
 
-our $VERSION = '0.22';
+our $VERSION = '0.32';
 
 # copy from Plack::Middleware::AccessLog
 our %formats = (
@@ -15,50 +21,6 @@ our %formats = (
     combined => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"',
 );
 
-BEGIN {
-    if (eval { require POSIX::strftime::GNU; 1; }) {
-        *_posix_strftime = \&POSIX::strftime::GNU::strftime;
-    } else {
-        *_posix_strftime = \&POSIX::strftime;
-    };
-
-    if (_posix_strftime("%z", localtime) =~ /^[+-]\d{4}$/) {
-        *_has_strftime_z = sub () { !! 1 };
-    } else {
-        *_has_strftime_z = sub () { !! 0 };
-    }
-}
-
-sub _tzoffset {
-    my $self = shift;
-    if ( ! exists $self->{tz_cache} || ! exists $self->{isdst_cache} || $_[8] ne $self->{isdst_cache} ) {
-        $self->{isdst_cache} = $_[8];
-        if ( _has_strftime_z ) {
-            $self->{tz_cache} = _posix_strftime('%z', @_);
-        }
-        else {
-            my $s = Time::Local::timegm(@_) - Time::Local::timelocal(@_);
-            my $min_offset = int($s / 60);
-            $self->{tz_cache} = sprintf '%+03d%02u', $min_offset / 60, $min_offset % 60;
-        }
-    }
-    $self->{tz_cache};
-}
-
-sub _strftime {
-    my $self = shift;
-    my ($fmt, @time) = @_;
-    if (not _has_strftime_z) {
-        my $tz = _tzoffset($self,@time);
-        $fmt =~ s/%z/$tz/g;
-    }
-    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;
-};
-
 sub _safe {
     my $string = shift;
     return unless defined $string;
@@ -70,7 +32,8 @@ sub _string {
     my $string = shift;
     return '-' if ! defined $string;
     return '-' if ! length $string;
-    _safe($string);
+    $string =~ s/([^[:print:]])/"\\x" . unpack("H*", $1)/eg;
+    return $string;
 }
 
 sub header_get {
@@ -90,19 +53,19 @@ sub header_get {
 my $psgi_reserved = { CONTENT_LENGTH => 1, CONTENT_TYPE => 1 };
 
 my $block_handler = sub {
-    my($self,$block, $type) = @_;
+    my($block, $type, $extra) = @_;
     my $cb;
     if ($type eq 'i') {
         $block =~ s/-/_/g;
         $block = uc($block);
         $block = "HTTP_${block}" unless $psgi_reserved->{$block};
-        $cb =  q!_string($env->{'!.$block.q!'})!;
+        $cb =  q!_string($_[ENVS]->{'!.$block.q!'})!;
     } elsif ($type eq 'o') {
-        $cb =  q!_string(header_get($res->[1],'!.$block.q!'))!;
+        $cb =  q!_string(header_get($_[RES]->[1],'!.$block.q!'))!;
     } elsif ($type eq 't') {
-        $cb =  q!"[" . _strftime($this,'!.$block.q!', localtime($time)) . "]"!;
-    } elsif (exists $self->{extra_block_handlers}->{$type}) {
-        $cb =  q!_string($extra_block_handlers->{'!.$type.q!'}->('!.$block.q!',$env,$res,$length,$reqtime))!;
+        $cb =  q!"[" . POSIX::strftime::Compiler::strftime('!.$block.q!', @lt) . "]"!;
+    } elsif (exists $extra->{$type}) {
+        $cb =  q!_string($extra_block_handlers->{'!.$type.q!'}->('!.$block.q!',$_[ENVS],$_[RES],$_[LENGTH],$_[REQTIME]))!;
     } else {
         Carp::croak("{$block}$type not supported");
         $cb = "-";
@@ -113,33 +76,32 @@ my $block_handler = sub {
 
 our %char_handler = (
     '%' => q!'%'!,
-    h => q!($env->{REMOTE_ADDR} || '-')!,
+    h => q!($_[ENVS]->{REMOTE_ADDR} || '-')!,
     l => q!'-'!,
-    u => q!($env->{REMOTE_USER} || '-')!,
-    t => q!"[" . $t . "]"!,
-    r => q!_safe($env->{REQUEST_METHOD}) . " " . _safe($env->{REQUEST_URI}) .
-                       " " . $env->{SERVER_PROTOCOL}!,
-    s => q!$res->[0]!,
-    b => q!(defined $length ? $length : '-')!,
-    T => q!(defined $reqtime ? int($reqtime*1_000_000) : '-')!,
-    D => q!(defined $reqtime ? $reqtime : '-')!,
-    v => q!($env->{SERVER_NAME} || '-')!,
-    V => q!($env->{HTTP_HOST} || $env->{SERVER_NAME} || '-')!,
-    p => q!$env->{SERVER_PORT}!,
+    u => q!($_[ENVS]->{REMOTE_USER} || '-')!,
+    t => q!'[' . $t . ']'!,
+    r => q!_safe($_[ENVS]->{REQUEST_METHOD}) . " " . _safe($_[ENVS]->{REQUEST_URI}) .
+                       " " . $_[ENVS]->{SERVER_PROTOCOL}!,
+    s => q!$_[RES]->[0]!,
+    b => q!(defined $_[LENGTH] ? $_[LENGTH] : '-')!,
+    T => q!(defined $_[REQTIME] ? int($_[REQTIME]*1_000_000) : '-')!,
+    D => q!(defined $_[REQTIME] ? $_[REQTIME] : '-')!,
+    v => q!($_[ENVS]->{SERVER_NAME} || '-')!,
+    V => q!($_[ENVS]->{HTTP_HOST} || $_[ENVS]->{SERVER_NAME} || '-')!,
+    p => q!$_[ENVS]->{SERVER_PORT}!,
     P => q!$$!,
-    m => q!_safe($env->{REQUEST_METHOD})!,
-    U => q!_safe($env->{PATH_INFO})!,
-    q => q!(($env->{QUERY_STRING} ne '') ? '?' . _safe($env->{QUERY_STRING}) : '' )!,
-    H => q!$env->{SERVER_PROTOCOL}!,
+    m => q!_safe($_[ENVS]->{REQUEST_METHOD})!,
+    U => q!_safe($_[ENVS]->{PATH_INFO})!,
+    q => q!(($_[ENVS]->{QUERY_STRING} ne '') ? '?' . _safe($_[ENVS]->{QUERY_STRING}) : '' )!,
+    H => q!$_[ENVS]->{SERVER_PROTOCOL}!,
 
 );
 
 my $char_handler = sub {
-    my $self = shift;
-    my $char = shift;
+    my ($char, $extra) = @_;
     my $cb = $char_handler{$char};
-    if (!$cb && exists $self->{extra_char_handlers}->{$char}) {
-        $cb = q!_string($extra_char_handlers->{'!.$char.q!'}->($env,$res,$length,$reqtime))!;
+    if (!$cb && exists $extra->{$char}) {
+        $cb = q!_string($extra_char_handlers->{'!.$char.q!'}->($_[ENVS],$_[RES],$_[LENGTH],$_[REQTIME]))!;
     }
     unless ($cb) {
         Carp::croak "\%$char not supported.";
@@ -157,49 +119,53 @@ sub new {
 
     my %opts = @_;
 
-    my $self = bless {
-        fmt => $fmt,
-        extra_block_handlers => $opts{block_handlers} || {},
-        extra_char_handlers => $opts{char_handlers} || {},
-    }, $class; 
-    $self->compile();
-    return $self;
+    my ($code_ref, $code) = compile($fmt, $opts{block_handlers} || {}, $opts{char_handlers} || {});
+    bless [$code_ref, $code], $class;
 }
 
 sub compile {
-    my $self = shift;
-    my $fmt = $self->{fmt};
+    my $fmt = shift;
+    my $extra_block_handlers = shift;
+    my $extra_char_handlers = shift;
     $fmt =~ s/!/\\!/g;
     $fmt =~ s!
         (?:
              \%\{(.+?)\}([a-zA-Z]) |
              \%(?:[<>])?([a-zA-Z\%])
         )
-    ! $1 ? $block_handler->($self, $1, $2) : $char_handler->($self, $3) !egx;
-
-    my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
-    my $extra_block_handlers = $self->{extra_block_handlers};
-    my $extra_char_handlers = $self->{extra_char_handlers};
+    ! $1 ? $block_handler->($1, $2, $extra_block_handlers) : $char_handler->($3, $extra_char_handlers) !egx;
     
-    my @lt = localtime(time);
+    my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
+    my $c = {};
     $fmt = q~sub {
-        my ($this,$env,$res,$length,$reqtime,$time) = @_;
-        $time = time() if ! defined $time;
-        my @lt = localtime($time);
-        my $tz = _tzoffset($this,@lt);
-        my $t = sprintf '%02d/%s/%04d:%02d:%02d:%02d %s', $lt[3], $abbr[$lt[4]], $lt[5]+1900, 
-          $lt[2], $lt[1], $lt[0], $tz;
+        $_[TIME] = time() if ! defined $_[TIME];
+        my @lt = localtime($_[TIME]);
+        if ( ! exists $c->{tz_cache} || ! exists $c->{isdst_cache} || $lt[8] != $c->{isdst_cache} ) {
+            $c->{tz_cache} = POSIX::strftime::Compiler::strftime('%z',@lt);
+            $c->{isdst_cache} = $lt[8];
+        }    
+        my $t = sprintf '%02d/%s/%04d:%02d:%02d:%02d %s', $lt[3], $abbr[$lt[4]], $lt[5]+1900,
+          $lt[2], $lt[1], $lt[0], $c->{tz_cache};
         q!~ . $fmt . q~!
     }~;
-    $self->{log_handler_code} = $fmt;
-    $self->{log_handler} = eval $fmt; ## no critic
+    my $code_ref = eval $fmt; ## no critic
+    die $@ . "\n===\n" . $fmt if $@;
+    wantarray ? ($code_ref, $fmt) : $code_ref;
 }
 
 sub log_line {
     my $self = shift;
-    my ($env,$res,$length,$reqtime,$time) = @_;
-    my $log = $self->{log_handler}->($self,$env,$res,$length,$reqtime,$time);
-    $log . "\n";
+    $self->[0]->(@_) . "\n";
+}
+
+sub code {
+    my $self = shift;
+    $self->[1];
+}
+
+sub code_ref {
+    my $self = shift;
+    $self->[0];
 }
 
 1;
@@ -300,6 +266,10 @@ Sample psgi
 
 =back
 
+=head1 ABOUT POSIX::strftime::Compiler
+
+This module uses L<POSIX::strftime::Compiler> for generate datetime string. POSIX::strftime::Compiler provides GNU C library compatible strftime(3). But this module will not affected by the system locale. This feature is useful when you want to write loggers, servers and portable applications.
+
 =head1 ADD CUSTOM FORMAT STRING
 
 Apache::LogFormat::Compiler allows one to add a custom format string
@@ -327,11 +297,6 @@ Your sub is called with two or three arguments: the content inside the C<{}>
 from the format (block_handlers only), the PSGI environment (C<$env>),
 and the ArrayRef of the response. It should return the string to be logged.
 
-=head1 RECOMMENDED MODULE
-
-If L<POSIX::strftime::GNU> is available, Apache::LogFormat::Compiler uses it. 
-It's good for Windows and old Unices have limited strftime's formatting.
-
 =head1 AUTHOR
 
 Masahiro Nagano E<lt>kazeburo@gmail.comE<gt>
@@ -0,0 +1,2 @@
+name = "Apache-LogFormat-Compiler"
+badges = ["travis"]