File Coverage

blib/lib/App/JobLog/Command/truncate.pm
Criterion Covered Total %
statement 24 108 22.2
branch 0 26 0.0
condition n/a
subroutine 8 16 50.0
pod 3 6 50.0
total 35 156 22.4


line stmt bran cond sub pod time code
1             package App::JobLog::Command::truncate;
2             $App::JobLog::Command::truncate::VERSION = '1.041';
3             # ABSTRACT: decapitate the log
4              
5              
6 2     2   1682 use App::JobLog -command;
  2         4  
  2         16  
7 2     2   748 use autouse 'App::JobLog::TimeGrammar' => qw(parse);
  2         5  
  2         11  
8             use Class::Autouse
9 2     2   114 qw(IO::File App::JobLog::Log App::JobLog::Log::Line File::Temp File::Spec);
  2         4  
  2         12  
10 2     2   260 use autouse 'App::JobLog::Time' => qw(now);
  2         3  
  2         9  
11 2     2   163 use autouse 'App::JobLog::Config' => qw(log dir);
  2         3  
  2         9  
12 2     2   176 use autouse 'File::Copy' => qw(move);
  2         4  
  2         8  
13              
14 2     2   146 use Modern::Perl;
  2         4  
  2         16  
15 2     2   325 no if $] >= 5.018, warnings => "experimental::smartmatch";
  2         4  
  2         14  
16              
17             sub execute {
18 0     0 1   my ( $self, $opt, $args ) = @_;
19 0           my $expression = join ' ', @$args;
20 0           my ( $s, $is_interval );
21 0           eval { ( $s, undef, $is_interval ) = parse $expression; };
  0            
22 0 0         $self->usage_error($@) if $@;
23 0 0         $self->usage_error('truncation date must not be a interval')
24             if $is_interval;
25              
26             # determine name of head log
27 0           my $log = App::JobLog::Log->new;
28 0           my ($p) = $log->find_previous($s);
29 0 0         $self->usage("no event in log prior to $expression") unless $p;
30 0           my ($e) = $log->first_event;
31 0           my $base = 'log-' . $e->start->ymd . '--' . $p->start->ymd;
32              
33             # create output handle for head log
34 0 0         my $io =
35             $opt->compression ? _pick_compression( $opt->compression ) : 'IO::File';
36 0           my $suffix = '';
37 0           my @args = ();
38 0           for ($io) {
39 0           when ('IO::File') { push @args, 'w' }
  0            
40 0           when ('IO::Compress::Zip') {
41 0           $suffix = '.zip';
42 0           push @args, Name => $base;
43             }
44 0           when ('IO::Compress::Gzip') { $suffix = '.gz' }
  0            
45 0           when ('IO::Compress::Bzip2') { $suffix = '.bz2' }
  0            
46 0           when ('IO::Compress::Lzma') { $suffix = '.lzma' }
  0            
47 0           default { die "unprepared to handle $io; please report bug" };
  0            
48             }
49 0           my $old_f = File::Spec->catfile( dir, $base . $suffix );
50 0           my $old_fh = $io->new( $old_f, @args );
51 0           my $fh = File::Temp->new;
52 0           my $current_fh = $old_fh;
53 0           my $log_handle = IO::File->new( log, 'r' );
54 0           my ( $unswitched, @buffer, $previous ) = (1);
55 0           while ( defined( my $line = $log_handle->getline ) ) {
56 0           my $ll = App::JobLog::Log::Line->parse($line);
57 0 0         if ( $ll->is_event ) {
58 0 0         if ($unswitched) {
59 0 0         $previous = $ll if $ll->is_beginning;
60 0 0         if ( $ll->time > $s ) {
    0          
61 0 0         if ($previous) { # event spanning border
62 0           my $end_time = $s->clone->subtract( seconds => 1 );
63 0           $current_fh->print(
64             App::JobLog::Log::Line->new(
65             done => 1,
66             time => $end_time
67             )
68             );
69 0           $previous->time = $s;
70 0           $line = $previous->to_string . "\n";
71             }
72 0           $current_fh->close;
73 0           $current_fh = $fh;
74 0           _header( $base, $suffix, \@buffer );
75 0           $unswitched = undef;
76             }
77             elsif ( $ll->is_end ) {
78 0           $previous = undef;
79             }
80             }
81 0           while (@buffer) {
82 0           $current_fh->print( shift @buffer );
83             }
84 0           $current_fh->print($line);
85             }
86             else {
87 0           push @buffer, $line;
88             }
89             }
90 0           while (@buffer) {
91 0           $current_fh->print( shift @buffer );
92             }
93 0           $current_fh->close;
94 0           move( "$fh", log );
95 0           print "truncated portion of log saved in $old_f\n";
96             }
97              
98             sub validate {
99 0     0 0   my ( $self, $opt, $args ) = @_;
100 0 0         $self->usage_error('no time expression provided') unless @$args;
101 0 0         if ( $opt->compression ) {
102 0           my $alg = _pick_compression( $opt->compression );
103 0           eval "require $alg";
104 0 0         $self->usage_error(
105             "$@: you must install $alg to use compression option --"
106             . $opt->compression )
107             if $@;
108             }
109             }
110              
111             sub usage_desc {
112 0     0 1   '%c ' . __PACKAGE__->name . ' %o ';
113             }
114              
115             sub abstract {
116 0     0 1   'shorten the log to contain only those moments after a given date';
117             }
118              
119             sub options {
120             return (
121             [
122 0     0 0   compression => hidden => {
123             one_of => [
124             [ 'zip|z', 'pass truncated head of log through zip', ],
125             [ 'gzip|g', 'pass truncated head of log through gzip', ],
126             [ 'bzip2|b', 'pass truncated head of log through bzip2', ],
127             [ 'lzma|l', 'pass truncated head of log through lzma', ],
128             ]
129             }
130             ]
131             );
132             }
133              
134             sub full_description {
135             <
136             Over time your log will fill with cruft: work no one is interested in any longer,
137             tags whose meaning you've forgotten. What you want to do at this point is chop off
138             all the old stuff, stash it somewhere you can find it if need be, and retain in
139             your active log only the more recent events. This is what truncate is for. You give
140             it a starting date and it splits your log into two with the active portion containing
141             all moments on that date or after. The older portion is retained in your joblog hidden
142             directory.
143             END
144 0     0 0   }
145              
146             # comment header added to truncated log
147             sub _header {
148 0     0     my ( $base, $suffix, $buffer ) = @_;
149             unshift @$buffer,
150 0           map { App::JobLog::Log::Line->new( comment => "$_\n" ) }
  0            
151             <
152 0           Log file truncated on @{[now]}.
153             Head of log to be found in $base$suffix
154             END
155             }
156              
157             # converts chosen compression opt into appropriate IO:: algorithm
158             sub _pick_compression {
159 0     0     my $alg = shift;
160 0           for ($alg) {
161 0           when ('zip') { return 'IO::Compress::Zip' }
  0            
162 0           when ('gzip') { return 'IO::Compress::Gzip' }
  0            
163 0           when ('bzip2') { return 'IO::Compress::Bzip2' }
  0            
164 0           when ('lzma') { return 'IO::Compress::Lzma' }
  0            
165             }
166             }
167              
168             1;
169              
170             __END__