File Coverage

blib/lib/MojoX/Log/Rotate.pm
Criterion Covered Total %
statement 60 82 73.1
branch 12 20 60.0
condition 3 7 42.8
subroutine 6 10 60.0
pod 2 6 33.3
total 83 125 66.4


line stmt bran cond sub pod time code
1             package MojoX::Log::Rotate;
2             $MojoX::Log::Rotate::VERSION = '1.222670';
3             # ABSTRACT: Makes mojolicious log file rotation easy
4 2     2   166273 use Mojo::Base 'Mojo::Log', -signatures;
  2         316460  
  2         12  
5 2     2   78157 use Mojo::File;
  2         4  
  2         2174  
6              
7             has 'need_rotate_cb';
8             has 'rotate_cb';
9             has last_rotate => sub ($s) { $s->path && -f $s->path ? (stat $s->path)[10] : time() };
10             has frequency => 60 * 60 * 24; #every day
11              
12             #for threading supports
13             has '_thread';
14             has '_queue';
15              
16 1     1 1 13520 sub new ($class, %params) {
  1         3  
  1         3  
  1         2  
17 1         5 my %p = map { $_ => delete $params{$_} }
18 1         2 grep { exists $params{$_} }
  5         11  
19             qw(frequency how when threaded on_rotate);
20 1         10 my $self = $class->SUPER::new(%params);
21              
22 1 50       36 $self->frequency($p{frequency}) if exists $p{frequency};
23 1   50     12 $self->rotate_cb($p{how} // \&default_rotation_cb);
24 1   50     10 $self->need_rotate_cb($p{when} // \&default_need_rotation_cb);
25 1 50       6 $self->on(rotate => $p{on_rotate}) if exists $p{on_rotate};
26              
27 1 50       3 if($p{threaded}) {
28             #force calculation of initial last_rotate;
29 0         0 $self->last_rotate;
30 0         0 $self->initialize_thread_support;
31             }
32             else{
33             #inject our message subscrition on the top
34 1         5 my $subscribers = $self->subscribers('message');
35 1         9 $self->unsubscribe('message');
36 1         11 $self->on(message => \&on_message_handler);
37 1         7 $self->on(message => $_) for @$subscribers;
38             }
39              
40              
41 1         7 $self;
42             }
43              
44 4     4 0 2589 sub on_message_handler ($self, $level, @args) {
  4         5  
  4         6  
  4         14  
  4         5  
45 4         12 my $cb_when = $self->need_rotate_cb;
46 4         16 my $cb_how = $self->rotate_cb;
47 4 50 33     23 if($cb_when && $cb_how) {
48 4 100       8 if(my $when_res = $cb_when->($self)) {
49 2         5 $self->last_rotate( time() );
50 2         17 my $how_res = $cb_how->($self, $when_res);
51 2         13 $self->emit('rotate' => { how => $how_res, when => $when_res });
52             }
53             }
54             }
55              
56             # must returns a false value when there is no need to rotate, or a true value.
57             # the returns value will be passed to the rotate_cb so that you can share data between callback.
58 4     4 0 5 sub default_need_rotation_cb ($self) {
  4         5  
  4         4  
59 4 50       7 return unless $self->path;
60 4 100       18 if(time - $self->last_rotate > $self->frequency ) {
61 2         21 my $last_rotate = $self->last_rotate;
62 2         12 return { last_rotate => $last_rotate };
63             }
64 2         46 return;
65             }
66              
67 2     2 0 3 sub default_rotation_cb ($self, $when_res) {
  2         3  
  2         2  
  2         3  
68 2         3 my $res = { }; #nothing to rotate
69 2 50       4 if(my $handle = $self->handle) {
70 2 50       11 if(-f $self->path) {
71 2         37 my ($y, $m, $d, $h, $mi, $s) = (localtime)[5, 4, 3, 2, 1, 0];
72 2         69 my $suffix = sprintf("_%04d%02d%02d_%02d%02d%02d", $y+1900, $m+1, $d, $h, $mi, $s);
73 2         6 my $new_name = $self->path =~ s/(\.[^.]+)$/$suffix$1/r;
74 2         36 $handle->flush;
75 2         8 $handle->close;
76 2 50       36 Mojo::File->new($self->path)->move_to($new_name)
77             or die "MojoX::Log::Rotation failed to move " . $self->path . " into $new_name : $!";
78 2         246 $res = { rotated_file => $new_name };
79             }
80             }
81 2         7 $self->handle(Mojo::File->new($self->path)->open('>>'));
82 2         251 return $res;
83             }
84              
85 0     0 0   sub initialize_thread_support ($self) {#may not works well with multiple loggers...
  0            
  0            
86 0           eval 'use threads; use Thread::Queue; use Mojo::Util';
87 0           my $q = Thread::Queue->new;
88 0           my $appender = \&Mojo::Log::append;
89             my $thlog = threads->create(sub {
90 0     0     while(defined(my $job = $q->dequeue)) {
91 0           $appender->($self, $job->{msg});
92 0           on_message_handler($self, '', '');
93             }
94 0           });
95             Mojo::Util::monkey_patch('Mojo::Log', 'append', sub {
96 0     0     my ($self, $msg) = @_;
97 0           $q->enqueue({type => 'msg', msg => $msg});
98 0           });
99 0           $self->_queue($q);
100 0           $self->_thread($thlog);
101             }
102              
103 0     0 1   sub stop ($self) {
  0            
  0            
104 0           $self->_queue->end;
105 0           $self->_thread->join;
106             }
107             1;
108              
109             __END__