add help text for watchdbg and grepdbg
[spider.git] / perl / grepdbg
1 #!/usr/bin/perl
2 #
3 # Program to do a grep with dates and times on the debug
4 # files
5 #
6 # grepdbg [nn] [-mm] <regular expression>
7 #
8 # nn - is the day you what to look at: 1 is yesterday, 0 is today
9 # and is optional if there is only one argument
10 #
11 # -mmm - print the mmm lines before the match. So -3 will print
12 # 4 lines altogether, the 3 lines before the line matching
13 # the regular expression. 
14 #
15 # <regexp> is the regular expression you are searching for, 
16 # a caseless search is done. There can be more than one <regexp>
17 # a <regexp> preceeded by a '!' is treated as NOT <regexp>. Each
18 # <regexp> is implcitly ANDed together. 
19 #
20 # If you specify something that likes a filename and that filename
21 # has a .pm on the end of it and it exists then rather than doing
22 # the regex match it executes the "main::handle()" function passing
23 # it one line at a time.
24 #
25 #
26
27 require 5.004;
28
29 package main;
30
31 # search local then perl directories
32 BEGIN {
33         # root of directory tree for this system
34         $root = "/spider"; 
35         $root = $ENV{'DXSPIDER_ROOT'} if $ENV{'DXSPIDER_ROOT'};
36         
37         unshift @INC, "$root/perl";     # this IS the right way round!
38         unshift @INC, "$root/local";
39 }
40
41 use SysVar;
42 use DXUtil;
43 use DXLog;
44 use Julian;
45
46 use strict;
47
48 use vars qw(@days $fp $today $string);
49
50
51 $fp = DXLog::new('debug', 'dat', 'd');
52 $today = $fp->unixtoj(time()); 
53 my $nolines = 1;
54 my @prev;
55 my @patt;
56
57 foreach my $arg (@ARGV) {
58         if ($arg =~ /^-/) {
59                 $arg =~ s/^-+//;
60                 if ($arg =~ /\?|^he?l?p?/) {
61                         usage();
62                         exit(0);
63                 }
64                 $nolines += $arg if $arg =~ /^\d+$/;
65         } elsif ($arg =~ /^\d+$/) {
66                 push @days, $arg;
67         } elsif ($arg =~ /\.pm$/) {
68                 if (-e $arg) {
69                         my $fn = $arg;
70                         $fn =~ s/\.pm$//;
71                         eval { require $arg};
72                         die "requiring $fn failed $@" if $@;
73                         die "required $fn does not contain 'sub handle' (check that 'package main;' exists)" unless main->can('handle');
74                 } else {
75                         die "$arg not found";
76                 }
77         } else {
78                 push @patt, $arg;
79         }
80 }
81
82 push @patt, '.*' unless @patt;
83
84 push @days, "0" unless @days;
85 for my $entry (@days) {
86         my $now = $today->sub($entry); 
87         my $fh = $fp->open($now); 
88         my $line;
89         my $do;
90
91
92         begin() if main->can('begin');
93         if ($fh) {
94                 while (<$fh>) {
95                         if (main->can('handle')) {
96                                 handle($_);
97                         } else {
98                                 process($_);
99                         }
100                 }
101                 $fp->close();
102         }
103         end() if main->can('end');
104 }
105
106 total() if main->can('total');
107 exit 0;
108
109 sub process
110 {
111         my $line = shift;
112         chomp $line;
113         push @prev, $line;
114         shift @prev while @prev > $nolines;
115         my $flag = 0;
116         foreach my $p (@patt) {
117                 if ($p =~ /^!/) {
118                         my $r = substr $p, 1;
119                         last if $line =~ m{$r}i;
120                 } else {
121                         last unless $line =~ m{$p}i;
122                 }
123                 ++$flag;
124         }
125         if ($flag == @patt) {
126                 for (@prev) {
127                         s/([\x00-\x1f\x7f-\xff])/sprintf("\\x%02X", ord($1))/eg; 
128                         my ($t, $l) =  split /\^/, $_, 2;
129                         print atime($t), ' ', $l, "\n";
130                 }
131                 print "------------------\n" if $nolines > 1;
132                 @prev = ();
133         }
134 }
135
136 sub usage
137 {
138         print << "XXX";
139
140  usage: grepdbg [nn days before] [-nnn lines before] [<perl filter module>] [<regexp>|!<regexp>]...
141
142         You can have more than one <regexp> with an implicit 'and' between them. All 
143         <regexes> are caseless. It's recommended to put 'not' (!<regex>) first in any list.
144         Don't forget that you are doing this in a shell and you may need to quote your
145         <regex>s.
146
147         grepdbg with no arguments will simply list the current debug log with the timestamp
148         for each line decoded into a human readable form. 
149
150           grepdbg | less
151
152         is a handy way of scrolling through the debug log.
153
154           grepdbg -2 progress
155
156         will display any line containing 'progress' and also the two lines before that.
157
158         You can install your own content and display arrangement (useful for filtering data 
159         in some complicated way). You call it like this (assuming it is called 'filter.pm').
160         This is what is meant by <perl filter module>.
161
162         grepdbg filter.pm
163
164         All the other arguments to grepdbg are available to limit the input to your filter. 
165         If you want them.
166
167         The filter module MUST contain at least:
168
169                   package main;
170
171                   sub handle
172                   {
173                      your code goes here
174                   }
175                   1;
176
177         It can also have a 'sub begin {...}' and / or 'sub end {...}' which are executed
178         immediately after opening a logfile and then just before closing it, respectively.
179
180         You can also add a 'sub total {...}' which executes after the last line is 
181         printed and grepdbg exits.
182
183         Read the code of this program and copy'n'paste the 'sub
184         process' code into a new file. Then change 'sub process'
185         to 'sub handle'. Add the line 'package main;' at the beginning
186         of the file and a line '1;' at the end and then modify it to
187         your requirements...
188
189 XXX
190 }