File Coverage

blib/lib/App/Sysadmin/Log/Simple/File.pm
Criterion Covered Total %
statement 93 95 97.8
branch 12 24 50.0
condition 4 9 44.4
subroutine 11 11 100.0
pod 3 3 100.0
total 123 142 86.6


line stmt bran cond sub pod time code
1             package App::Sysadmin::Log::Simple::File;
2 4     4   5099 use strict;
  4         11  
  4         157  
3 4     4   25 use warnings;
  4         28  
  4         196  
4             # ABSTRACT: a file-logger for App::Sysadmin::Log::Simple
5             our $VERSION = '0.009'; # VERSION
6              
7 4     4   24 use Carp;
  4         7  
  4         293  
8 4     4   26 use Try::Tiny;
  4         10  
  4         246  
9 4     4   22 use autodie qw(:file :filesys);
  4         8  
  4         42  
10 4     4   11655 use Path::Tiny;
  4         8422  
  4         4751  
11              
12              
13             sub new {
14 6     6 1 19451 my $class = shift;
15 6         26 my %opts = @_;
16 6         13 my $app = $opts{app};
17              
18 6   66     229 return bless {
19             logdir => path( $app->{logdir} || $opts{logdir} || qw(/ var log sysadmin) ),
20             index_preamble => $app->{index_preamble},
21             view_preamble => $app->{view_preamble},
22             date => $app->{date},
23             user => $app->{user},
24             do_file => $app->{do_file},
25             }, $class;
26             }
27              
28              
29             sub view {
30 2     2 1 6 my $self = shift;
31 2         19 my $year = $self->{date}->year;
32 2         25 my $month = $self->{date}->month;
33 2         22 my $day = $self->{date}->day;
34 2         1817 require IO::Pager;
35              
36 2         20524 my $logfile = path($self->{logdir}, $year, $month, "$day.log");
37 2 50       111 die "No log for $year/$month/$day\n" unless $logfile->is_file;
38              
39 2         110 my $logfh = $logfile->openr_utf8;
40 2 50       15692 local $STDOUT = IO::Pager->new(*STDOUT)
41             unless $ENV{__PACKAGE__.' under test'};
42 2 50       11 say($self->{view_preamble}) if $self->{view_preamble};
43 2         59 print while (<$logfh>);
44 2         250 close $logfh;
45              
46 2         1228 return;
47             }
48              
49              
50             sub log {
51 2     2 1 4 my $self = shift;
52 2         6 my $line = shift;
53              
54 2 50       10 return unless $self->{do_file};
55              
56 2 50       16 $self->{logdir}->mkpath unless $self->{logdir}->is_dir;
57              
58 2         159 my $year = $self->{date}->year;
59 2         26 my $month = $self->{date}->month;
60 2         25 my $day = $self->{date}->day;
61              
62 2         18 my $dir = path($self->{logdir}, $year, $month);
63 2 50       94 $dir->mkpath unless $dir->is_dir;
64 2         823 my $logfile = path($self->{logdir}, $year, $month, "$day.log");
65              
66             # Start a new log file if one doesn't exist already
67 2 50       73 unless ($logfile->is_file) {
68 2         75 open my $logfh, '>>', $logfile;
69 2         4669 my $line = $self->{date}->day_name . ' ' . $self->{date}->month_name . " $day, $year";
70 2         149 say $logfh $line;
71 2         47 say $logfh '=' x length($line), "\n";
72 2         23 close $logfh; # Explicitly close before calling generate_index() so the file is found
73 2         1280 $self->_generate_index();
74             }
75              
76 2         14 open my $logfh, '>>', $logfile;
77 2         364 my $timestamp = $self->{date}->hms;
78 2   33     49 my $user = $ENV{SUDO_USER} || $ENV{USER}; # We need to know who wrote this
79 2         186 say $logfh " $timestamp $user:\t$line";
80              
81             # This might be run as root, so fix up ownership and
82             # permissions so mortals can log to files root started
83 2         307 my ($uid, $gid) = (getpwnam($self->{user}))[2,3];
84 2         15 chown $uid, $gid, $logfile;
85 2         1030 chmod 0644, $logfile;
86              
87 2         724 return "Logged to $logfile";
88             }
89              
90             sub _generate_index {
91 3     3   492 my $self = shift;
92 3         1932 require File::Find::Rule;
93              
94 3         17358 my $indexfh = path($self->{logdir}, 'index.log')->openw_utf8; # clobbers the file
95 3 50       30559 say $indexfh $self->{index_preamble} if defined $self->{index_preamble};
96              
97             # Find relevant log files
98 3         40 my @files = File::Find::Rule->mindepth(3)->in($self->{logdir});
99 3         2856 my @dates;
100 3         12 foreach (@files) {
101 2 50       22 if (m{
102             (?\d{4})
103             /
104             (?\d{1,2})
105             /
106             (?\d{1,2})
107             }x) { # Extract the date
108 2     2   1977 push @dates, [$+{year}, $+{month}, $+{day}];
  2         1137  
  2         671  
  2         46  
109             }
110             else {
111 0         0 warn "WTF: $_";
112             }
113             }
114             # Sort by year, then by month, then by day
115 2         9 @dates = map { $_->[0] }
  0         0  
116 2         17 sort { $b->[1] <=> $a->[1] }
117 3         12 map { [ $_, $_->[0]*1000 + $_->[1]*10 + $_->[2] ] }
118             @dates;
119              
120             # Keep track of
121 3         7 my $lastyear = 0;
122 3         7 my $lastmonth = 0;
123 3         8 for my $date (@dates) {
124 2         8 my $year = $date->[0];
125 2         4 my $month = $date->[1];
126 2         5 my $day = $date->[2];
127              
128 2 50       52 if ($year != $lastyear) {
129 2         18 say $indexfh "\n$year";
130 2         39 say $indexfh "-" x length($year);
131 2         18 $lastyear = $year;
132 2         5 $lastmonth = 0;
133             }
134 2 50       52 if ($month != $lastmonth) {
135 2         12 say $indexfh "\n### $month ###\n";
136 2         17 $lastmonth = $month;
137             }
138 2 50 33     19 if ($year == $lastyear and $month == $lastmonth) {
139 2         16 say $indexfh "[$day]($year/$month/$day)"
140             }
141             }
142 3         32 close $indexfh;
143 3         1260 return;
144             }
145              
146             1;
147              
148             __END__