Send wind even if it hasn't changed
[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         my $pos = sysseek(I, 0, 2);
105         if ($pos <= int(abs($lines * 80))) {
106                 sysseek(I, 0, 0);
107         } else {
108                 sysseek(I, $pos + ($lines * 80), 0);     # remember lines is (-)ve
109         }
110
111         my $count;
112         while (!$end) {
113
114                 if ($state) {
115                         my $l;
116                         my $r = sysread(I, $l, 4096);
117                         if (defined $r && length $l) {
118                                 my @lines = split /\cM?\cJ/, $l;
119                                 foreach my $s (@lines) {
120                                         if (@pattern) {
121                                                 unless (match($s)) {
122                                                         next;
123                                                 }
124                                         }
125                                         print "\n".$s;
126                                 }
127                         }
128                         $count = 0;
129                 }
130
131                 if (wait_for_stdin(0.1)) {
132                         $state ^= 1;
133                         print $state ? "\nRunning..." : "\nStopped...";
134                 }
135                 sysseek(I, 0, 1);
136                 STDOUT->flush;
137
138                 # move onto the next file if we roll over midnight
139                 my ($d) = (gmtime)[3];
140                 last if ($d != $dd);
141         }
142         close I;
143 }
144
145 print "\n";
146 exit(0);
147
148 sub match
149 {
150         my $count = 0;
151         foreach my $p (@pattern) {
152                 if ($p =~ /^!/) {
153                         my $r = substr $p, 1;
154                         last if $_[0] =~ m{$r}i;
155                 } else {
156                         last unless $_[0] =~ m{$p}i;
157                 }
158                 ++$count;
159         }
160         return $count == @pattern;
161 }
162
163 sub wait_for_stdin
164 {
165         my $t = shift;
166         if ($s->can_read($t)) {
167                 my $l = <STDIN>;
168                 if ($l =~ /^q/i) {
169                         print "\n";
170                         exit(0);
171                 }
172                 return 1;
173         }
174         return 0;
175 }
176
177