]> dxcluster.org Git - dweather.git/blob - logf
start a real(ish) dweather webapp
[dweather.git] / logf
1 #!/usr/bin/perl
2 #
3 # I have finally got sick of typing something like:
4 #
5 # tail -f logs/debug/2014/0623.log | grep -i mdip | grep -vi p[io]ng
6 #
7 # Do a tail -f of one of the current day's log files, the default is 'debug' but
8 # one can follow any of the others by putting enough of the directory
9 # as an argument so that it can find it eg: 
10 #
11 # logf [perl regex] ...
12 # logf sys [perl regex] ...
13 # logf -100 [dir] [perl regex] ...
14 #
15 # NOTE: You can have many regexes and they all have to match (an implied '&&')
16 # NOTE: Also you preceed any regex with the '!' character to indicate negation (like "| grep -v regex")
17 #
18 # logf udpr           - yields all udpr messages
19 # logf \!udpr         - yields everything except udpr messages. Note the shell escape character
20 # logf udpr ping      - yields udpr ping messages
21 # logf udpr \!p[io]ng - yields all udpr messages that aren't pings
22 #
23 # Copyright (c) 2014 Dirk Koopman, Tobit Computer Co Ltd
24 #
25
26 use strict;
27 use IO::Handle;
28 use IO::Select;
29 use File::Basename;
30 use Cwd;
31
32 my $me = fileparse($0);
33 my $cwd = fileparse(getcwd);
34
35 my $base = "logs";
36 my $ofn;
37
38 if (@ARGV[0] =~ /^-[\?h]/) {
39         print "usage:   $0 [-<count>] [<directory name fragment>] [<regex>]\n";
40         print "  e.g:   $0\n"; 
41     print "         $0 deb\n";
42         print "         $0 -100 cdr\n";
43         print "         $0 udpr\n";
44         print "         $0 sys tcp\n";
45         print "\n    any regexes are caseless\n\n";
46         print "default: $0 -40 debug\n";
47
48         exit(0);
49 }
50
51 my $lines = shift if ($ARGV[0] =~ /\-\d+/);
52 $lines ||= -40;
53
54 my $sort = shift || "debug";
55 my @dirs;
56 my $end;
57
58 opendir(my $dh, $base) or die "cannot open log directory '$base' ($!)";
59 @dirs = grep {!/^\./} readdir($dh);
60 closedir $dh;
61
62 my @pattern;
63 my ($dir) = grep {/^$sort/} @dirs;
64 if ($dir) {
65         @pattern = @ARGV;
66 } else {
67         @pattern = ($sort, @ARGV);
68         $dir = "debug";
69 }
70
71 my $s = IO::Select->new;
72 $s->add(\*STDIN);
73 autoflush STDIN, 0;
74 autoflush STDOUT, 0;
75
76 $SIG{TERM} = $SIG{INT} = sub {++$end};
77
78 while (!$end) {
79         my $fn;
80         my ($dd,$mm,$yy) = (gmtime)[3,4,5];
81         ++$mm;
82         $yy += 1900;
83
84         my $fn = sprintf "$base/$dir/%04d/%02d%02d", $yy, $mm, $dd;
85         if (-e "$fn.log") {
86                 $fn = "$fn.log";
87         } elsif (-e "$fn.csv") {
88                 $fn = "$fn.csv";
89         } else {
90                 if ($fn ne $ofn) {
91                         print "Waiting for $fn to appear...\n";
92                         STDOUT->flush;
93                         $ofn = $fn;
94                 }
95                 sleep 1;
96                 next;
97         }
98
99         my $state = 1;
100
101         # open the file, seek to the end, then seek backward from the end a bit and start reading
102         # but ignore the first line 'cos it will be incomplete. 
103         open I, $fn or die "cannot open $fn ($!)\n";
104         seek(I, 0, 2);
105         my $pos = tell(I);
106         if ($pos <= int(abs($lines * 80))) {
107                 seek(I, 0, 0);
108         } else {
109                 seek(I, $pos + ($lines * 80), 0);     # remember lines is (-)ve
110         }
111
112         my $buf;                                                # input overflow buffer
113         my $count;
114         while (!$end) {
115
116                 if ($state) {
117                         my $l = <I>;
118                         if (defined $l) {
119                                 if ($l =~ /\cJ$/) {
120                                         my $s = "$buf$l";
121                                         if (@pattern) {
122                                                 unless (match($s)) {
123                                                         $buf = '';
124                                                         next;
125                                                 }
126                                         }
127                                         print $s;
128                                         $buf = '';
129                                         next;
130                                 } else {
131                                         $buf .= $l;
132                                 }
133                         }
134                         $count = 0;
135                 }
136
137                 if (wait_for_stdin(0.1)) {
138                         $state ^= 1;
139                         print $state ? "\nRunning..." : "\nStopped...";
140                 }
141                 seek(I, 0, 1);
142                 STDOUT->flush;
143
144                 # runout any stored stuff if we haven't seen anything recently
145                 if ($state && length $buf && ++$count > 2) {
146                         if (@pattern) {
147                                 print $buf if match($buf);scalar @pattern == grep $buf =~ m{$_}i, @pattern;
148                         } else {
149                                 print $buf;
150                         }
151 #                       print " *** XTRA! ***";
152                         $buf = '';
153                 }
154
155                 # move onto the next file if we roll over midnight
156                 my ($d) = (gmtime)[3];
157                 last if ($d != $dd);
158         }
159         close I;
160 }
161
162 sub match
163 {
164         my $count = 0;
165         foreach my $p (@pattern) {
166                 if ($p =~ /^!/) {
167                         my $r = substr $p, 1;
168                         last if $_[0] =~ m{$r}i;
169                 } else {
170                         last unless $_[0] =~ m{$p}i;
171                 }
172                 ++$count;
173         }
174         return $count == @pattern;
175 }
176
177 sub wait_for_stdin
178 {
179         my $t = shift;
180         if ($s->can_read($t)) {
181                 my $l = <STDIN>;
182                 if ($l =~ /^q/i) {
183                         print "\n";
184                         exit(0);
185                 }
186                 return 1;
187         }
188         return 0;
189 }
190
191 exit(0);
192
193