File Coverage

blib/lib/Dancer/Logger/File/PerRequest.pm
Criterion Covered Total %
statement 53 94 56.3
branch 5 38 13.1
condition 1 23 4.3
subroutine 16 18 88.8
pod 1 2 50.0
total 76 175 43.4


line stmt bran cond sub pod time code
1             package Dancer::Logger::File::PerRequest;
2              
3 1     1   203184 use strict;
  1         1  
  1         29  
4 1     1   4 use warnings;
  1         2  
  1         20  
5 1     1   16 use 5.008_005;
  1         6  
  1         46  
6             our $VERSION = '0.03';
7              
8 1     1   3 use Carp;
  1         1  
  1         50  
9 1     1   3 use base 'Dancer::Logger::Abstract';
  1         1  
  1         70  
10 1     1   5 use Dancer::FileUtils qw(open_file);
  1         1  
  1         36  
11 1     1   4 use Dancer::Config 'setting';
  1         1  
  1         33  
12 1     1   4 use Dancer::Hook;
  1         1  
  1         14  
13 1     1   3 use Dancer::Factory::Hook;
  1         1  
  1         21  
14 1     1   4 use Dancer::SharedData;
  1         1  
  1         13  
15 1     1   3 use IO::File;
  1         1  
  1         105  
16 1     1   4 use Fcntl qw(:flock SEEK_END);
  1         3  
  1         94  
17 1     1   9 use Scalar::Util ();
  1         1  
  1         579  
18              
19             Dancer::Factory::Hook->instance->install_hooks(
20             qw/before_file_per_request_close after_file_per_request_close/
21             );
22              
23             sub init {
24 1     1 1 24 my $self = shift;
25 1         5 $self->SUPER::init(@_);
26              
27 1         3 my $logdir = logdir();
28 1 50       3 return unless ($logdir);
29 1 50       17 mkdir($logdir) unless -d $logdir;
30              
31             my $logfile_callback = setting('logfile_callback') || sub {
32             ## timestamp + pid + request->id
33 0     0   0 my @d = localtime();
34 0         0 my $file = sprintf('%04d%02d%02d%02d%02d%02d', $d[5] + 1900, $d[4] + 1, $d[3], $d[2], $d[1], $d[0]);
35 0 0       0 my $request_id = Dancer::SharedData->request ? Dancer::SharedData->request->id : '';
36 0         0 return $file . '-' . $$ . '-' . $request_id . '.log';
37 1   50     2 };
38 1         12 $self->{logfile_callback} = $logfile_callback;
39              
40             # per request
41 1         2 Scalar::Util::weaken $self;
42             my $on_end = sub {
43 1 50   1   2337 return unless $self->{fh};
44              
45 0         0 Dancer::Factory::Hook->execute_hooks('before_file_per_request_close', $self->{fh}, $self->{logfile});
46 0         0 close($self->{fh}); # close
47 0         0 undef $self->{fh};
48 0         0 Dancer::Factory::Hook->execute_hooks('after_file_per_request_close', $self->{logfile}, Dancer::SharedData->response);
49 0         0 undef $self->{logfile};
50 1         4 };
51 1         4 Dancer::Hook->new('after' => $on_end);
52 1 50       143 if (setting('serializer')) { # when serializer, on error, it's not call 'after' hook
53 0         0 Dancer::Hook->new('after_error_render' => $on_end);
54             }
55             }
56              
57             sub _log {
58 0     0   0 my ($self, $level, $message) = @_;
59              
60 0         0 my $fh = $self->{fh};
61 0 0       0 unless ($fh) {
62 0         0 my $logfile = $self->{logfile_callback}->();
63 0 0       0 my $logdir = logdir() or return;
64 0         0 $logfile = File::Spec->catfile($logdir, $logfile);
65              
66 0 0       0 unless($fh = open_file('>>', $logfile)) {
67 0         0 carp "unable to create or append to $logfile";
68 0         0 return;
69             }
70              
71             # looks like older perls don't auto-convert to IO::File
72             # and can't autoflush
73             # see https://github.com/PerlDancer/Dancer/issues/954
74 0         0 eval { $fh->autoflush };
  0         0  
75              
76 0         0 $self->{fh} = $fh;
77 0         0 $self->{logfile} = $logfile;
78             }
79              
80 0 0 0     0 return unless(ref $fh && $fh->opened);
81              
82 0 0       0 flock($fh, LOCK_EX)
83             or carp "locking logfile $self->{logfile} failed";
84 0 0       0 seek($fh, 0, SEEK_END)
85             or carp "seeking to logfile $self->{logfile} end failed";
86 0 0       0 $fh->print($self->format_message($level => $message))
87             or carp "writing to logfile $self->{logfile} failed";
88 0 0       0 flock($fh, LOCK_UN)
89             or carp "unlocking logfile $self->{logfile} failed";
90             }
91              
92             # Copied from Dancer::Logger::File
93             sub logdir {
94 1 50   1 0 2 if ( my $altpath = setting('log_path') ) {
95 1         8 return $altpath;
96             }
97              
98 0           my $logroot = setting('appdir');
99              
100 0 0 0       if ( $logroot and ! -d $logroot and ! mkdir $logroot ) {
      0        
101 0           carp "app directory '$logroot' doesn't exist, am unable to create it";
102 0           return;
103             }
104              
105 0 0         my $expected_path = $logroot ?
106             Dancer::FileUtils::path($logroot, 'logs') :
107             Dancer::FileUtils::path('logs');
108              
109 0 0 0       return $expected_path if -d $expected_path && -x _ && -w _;
      0        
110              
111 0 0 0       unless (-w $logroot and -x _) {
112 0           my $perm = (stat $logroot)[2] & 07777;
113 0           chmod($perm | 0700, $logroot);
114 0 0 0       unless (-w $logroot and -x _) {
115 0           carp "app directory '$logroot' isn't writable/executable and can't chmod it";
116 0           return;
117             }
118             }
119 0           return $expected_path;
120             }
121              
122             1;
123             __END__