StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
logBrowser.C
1 //
2 // $Id: logBrowser.C,v 1.2 2008/02/27 19:12:21 fine Exp $
3 //
4 // Based on critServer.C written by Tonko Ljubicic
5 // Curses functionality and command line args added by M.J. LeVine 01/2003
6 // Age limits on displayed log records added M.J. LeVine 01/2003
7 //
8 // 14-Jan-2003 MJL Added time calculation per file, corrected for midnight effect
9 // 14-Jan-2003 MJL Unlabelled records now have the highest priority
10 // 14-Jan-2003 MJL If no absolute path specified, make it /RTS/log/
11 // 14-Jan-2003 MJL Fixed bug where all files were scanned even if one was specified
12 // 14-Jan-2003 MJL Added trigger string (-s)
13 // 14-Jan-2003 MJL Added termination string (-f)
14 // 14-Jan-2003 MJL Added ignore string (-v)
15 // 14-Jan-2003 MJL Added multiple required strings (-g)
16 // 15-Jan-2003 MJL Added absolute time option for -t
17 // 15-Jan-2003 MJL Added -e scan from EOF
18 // 15-Jan-2003 MJL Added -m monochrome version (no escape sequences)
19 // 16-Jan-2003 MJL Moved parse command line to separate fcn for readability
20 // 17-Jan-2003 MJL Created adjust_time() to move code out of main() for readability
21 // 17-Jan-2003 MJL Made find_starting_point() to perform binary search for first record
22 // 21-Jan-2003 MJL changed printing of scanned file names for clarity
23 // 21-Jan-2003 MJL fix many logical errors in checking for start string
24 // 21-Jan-2003 MJL fix errors in adjusting times for the date change
25 // 22-Jan-2003 MJL remove all absolute time, just use hours and minutes
26 // 22-Jan-2003 MJL assume file starts at 08:00
27 
28 #include <stdio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <strings.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <time.h>
37 #include <curses.h>
38 
39 #define MAX_LOGFILES 128
40 #define MAX_GREPSTRINGS 10
41 
42 //#define DEBUG
43 
44 static char buff[1024] ;
45 static struct stat statb ;
46 static time_t mytime, earliest;
47 static int time_requested = 0;
48 static int triggered, last_file=0;
49 static int got_finished = 0;
50 static int scan_from_end = 0, monochrome = 0;
51 static char start[128] = "", finish[128]="", suppress[128]="";
52 
53 
54 static struct tm *filemodtime, *wtime;
55 static char *timestr;
56 static int level=0, mins, first_buff;
57 static int ngrep ;
58 static int in_hour, in_min, in_sec;
59 static int req_hour, req_min, start_time;;
60 static char level_str[] = "dummy string";
61 
62 static char logfiles[MAX_LOGFILES][128] = {
63  "/RTS/log/rts.log",
64  "/RTS/log/trigger.log",
65  "/RTS/log/evp.log",
66  "/RTS/log/det.log",
67 } ;
68 
69 static char in_buff[MAX_LOGFILES][1024] ;
70 static int in_buff_full[MAX_LOGFILES];
71 
72 static int daysinmonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
73 
74 static char grep_string[MAX_GREPSTRINGS][128] = {
75  "","","","","","",
76 } ;
77 
78 
79 static FILE *files[MAX_LOGFILES] ;
80 static int rectime[MAX_LOGFILES] ;
81 static int found_start[MAX_LOGFILES];
82 static int oldsizes[MAX_LOGFILES] ;
83 
84 // we define the colors here
85 // put near top of file for visibility
86 //============================================================================
87  // palette:
88 #define COL_NONE 30
89 #define COL_BLACK 40
90 #define COL_RED 31
91 #define COL_GREEN 32
92 #define COL_YELLOW 33
93 #define COL_BLUE 34
94 #define COL_MAGENTA 35
95 #define COL_CYAN 36
96 #define COL_WHITE 37
97 #define BOLD 1
98 #define NORMAL 0
99 //============================================================================
100 // used for DEBUG
101 #define PRINT0 printf("\033[%d;%d;%dm%s\033[0m",NORMAL,COL_NONE,COL_NONE,in_buff[first_buff])
102 // used for NOTICE
103 #define PRINT1 printf("\033[%d;%d;%dm%s\033[0m",NORMAL,COL_NONE,COL_NONE,in_buff[first_buff])
104 // used for WARNING
105 #define PRINT2 printf("\033[%d;%d;%dm%s\033[0m",NORMAL,COL_NONE,COL_CYAN,in_buff[first_buff])
106 // used for ERROR
107 #define PRINT3 printf("\033[%d;%d;%dm%s\033[0m",BOLD,COL_NONE,COL_MAGENTA,in_buff[first_buff])
108 // used for OPERATOR
109 #define PRINT4 printf("\033[%d;%d;%dm%s\033[0m",BOLD,COL_YELLOW,COL_MAGENTA,in_buff[first_buff])
110 // used for CRITICAL & TERR
111 #define PRINT5 printf("\033[%d;%d;%dm%s\033[0m",BOLD,COL_BLACK,COL_RED,in_buff[first_buff])
112 //============================================================================
113 
114 void printusage();
115 void parse_arguments(char **argv, int argc);
116 void find_starting_point(int i, int start_time);
117 
118 int main(int argc, char *argv[])
119 {
120  char *fret ;
121  int ret;
122 
123  int i,j;
124  int data_in ;
125  off_t bytes_left[MAX_LOGFILES];
126  int tries = 0;
127 
128 // for (i=0; i<argc; i++) {
129 // printf("argv[%d] :%s:\n",i,argv[i]);
130 // }
131 
132  parse_arguments(argv, argc);
133 
134  i = 0;
135 
136  printf("files to be scanned: \n");
137 
138  while(logfiles[i][0] != 0) {
139 
140  files[i] = fopen(logfiles[i],"r") ; //get a FILE
141 
142  if(files[i] == NULL) {
143  perror(logfiles[i]) ;
144  exit(1);
145  }
146 
147  printf("\t%s\n",logfiles[i++]);
148  }
149  last_file = i;
150 
151  for (i=0; i<last_file; i++) {
152  found_start[i]=0;
153  if (scan_from_end) fseek(files[i], 0, SEEK_END);
154  else if (time_requested != 0) {
155  start_time = 3600*((req_hour+16)%24) + 60*req_min;
156  find_starting_point(i,start_time);
157  }
158  // will move pointer to appropriate place for search
159 
160  ret = stat(logfiles[i],&statb) ;
161 
162  bytes_left[i] = (statb.st_size - ftello(files[i]));
163  in_buff_full[i] = 0;
164  }
165 
166  // printf("number of files: %d\n",last_file);
167  sleep(3);
168 
169  if (!monochrome) {
170  initscr();
171 
172  if(has_colors() == FALSE) {
173  endwin();
174  printf("This terminal does not support color: \n\
175 use /usr/dt/bin/dtterm -background white\n");
176  exit(1);
177  }
178  endwin();
179  }
180 
181  for(;;) {
182 
183  off_t new_bytes_left, rate;
184  int inlevel, ib, still_looking;
185  int eof_found;
186 
187  data_in = 0 ;
188 
189  for (i=0; i<last_file;i++) {
190 
191  if (in_buff_full[i]) continue;
192 
193  still_looking = 1;
194  eof_found = 0;
195 
196  do {
197  errno = 0 ;
198  fret = fgets(in_buff[i],sizeof(in_buff[i]),files[i]) ;
199  if(fret == NULL) {
200  if(errno) {
201  perror(logfiles[i]) ;
202  sleep(1) ;
203  continue ;
204  }
205  // let's check the file
206  ret = stat(logfiles[i],&statb) ;
207  if(ret < 0) {
208  perror(logfiles[i]) ;
209  sleep(1) ;
210  continue ;
211  }
212  // has the file we are scanning been replaced by a newer version?
213  if(statb.st_size < oldsizes[i]) {
214  fclose(files[i]) ;
215  files[i] = fopen(logfiles[i],"r") ; // reopen
216  oldsizes[i] = 0 ;
217  continue ;
218  }
219  if ((eof_found = feof(files[i]))==1) clearerr(files[i]);
220  oldsizes[i] = statb.st_size ;
221  tries++;
222  if ((tries%10000) == 0) {
223  fflush(stdout);
224  printf("\nwaiting for data..%s \r",logfiles[i]);
225  fflush(stdout);
226  }
227  continue ; // no data...
228  }
229 
230  data_in++ ; // we have something
231  sscanf(in_buff[i],"%*s %d:%d:%d",&in_hour,&in_min,&in_sec);
232  if ((data_in%2000) == 0) {
233  fflush(stdout);
234  printf("\nscanning records: %s %02d:%02d \t\t\t\r",
235  logfiles[i],in_hour,in_min);
236  fflush(stdout);
237  }
238  ret = stat(logfiles[i],&statb) ;
239 
240  // Do we have to skip some records to catch up?
241  new_bytes_left = (statb.st_size - ftello(files[i]));
242  rate = new_bytes_left - bytes_left[i];
243  if (rate >2000) { // are we losing ground? Skip some records
244  printf("skipping records to catch up\n");
245  fseeko(files[i], new_bytes_left/2 , SEEK_CUR);
246  // printf("skipping ahead to try to catch up\n");
247  new_bytes_left = (statb.st_size - ftello(files[i]));
248  }
249  bytes_left[i] = new_bytes_left; //update bytes_left
250 
251 
252  rectime[i] = 3600*((in_hour +16)%24) + in_min*60 +in_sec;
253 
254  // compare absolute time
255 
256  if (time_requested && !found_start[i])
257  if (rectime[i] < start_time) {
258 // printf(" record: %02d:%02d rectime %d, start_time %d\n",
259 // in_hour, in_min, rectime[i], start_time);
260  continue;
261  }
262  else {
263  found_start[i] = 1;
264  // printf(" found start for %s\n",logfiles[i]);
265  }
266 
267  //check for trigger string, if one is defined
268  if (!triggered) {
269  if (strstr(in_buff[i],start) != NULL) triggered = 1;
270  else {
271  in_buff_full[i] = 0; //throw this one away
272  continue;
273  }
274  }
275  // printf(" just after triggered comparison\n");
276  // check for finish string, set flag
277  if (finish[0]) {
278  // printf(" here I am in finish compare loop %d\n",i);
279  if (strstr(in_buff[i],finish) != NULL) got_finished = 1;
280  }
281  // check for suppression string
282  else if (suppress[0]) {
283  // printf(" here I am in suppress compare loop\n");
284  if (strstr(in_buff[i],suppress) != NULL) {
285  in_buff_full[i] = 0; //throw this one away
286  continue;
287  }
288  }
289  // check for grep string(s)
290  if (ngrep) {
291  int g=0, compare=1;
292  // printf(" here I am in grep compare loop\n");
293  while (grep_string[g][0]) {
294  if (strstr(in_buff[i],grep_string[g]) == NULL) compare=0;
295  g++;
296  }
297  if ((!compare) && (!got_finished)) {
298  in_buff_full[i] = 0; //throw this one away
299  continue;
300  }
301  }
302 
303  if (strstr(in_buff[i],": DEBUG:" ) != NULL) inlevel=0;
304  else if(strstr(in_buff[i],": NOTICE:" ) != NULL) inlevel = 1;
305  else if(strstr(in_buff[i],": WARNING:" ) != NULL) inlevel = 2;
306  else if(strstr(in_buff[i],": ERROR:" ) != NULL) inlevel = 3;
307  else if(strstr(in_buff[i],": OPERATOR:") != NULL) inlevel = 4;
308  else if(strstr(in_buff[i],": TERR:" ) != NULL) inlevel = 5;
309  else if(strstr(in_buff[i],": CRITICAL:") != NULL) inlevel = 5;
310  else inlevel = 5; // unlabeled messages get highest priority
311 
312  if (inlevel<level) {
313  in_buff_full[i] = 0; //throw this one away
314  continue;
315  }
316 
317  still_looking = 0;
318  in_buff_full[i] = 1; // something useful in in_buff[i]
319 
320  } while ((!eof_found) && still_looking );
321 
322  // printf(" terminated do ..while() loop for %s\n",logfiles[i]);
323 
324  } // loop over input files
325 
326 
327  first_buff = -1;
328  earliest = 100000; // impossibly in the future
329  for (ib=0; ib<last_file; ib++) {
330  if (in_buff_full[ib]) {
331  if (earliest > rectime[ib]) {
332  earliest = rectime[ib];
333  first_buff = ib;
334  }
335  }
336  }
337 
338  if (first_buff < 0) continue;
339 
340  if (monochrome) {
341 #ifdef DEBUG
342  printf("%d: %s",first_buff,in_buff[first_buff]);
343 #else
344  printf("%s",in_buff[first_buff]);
345 #endif
346  fflush(stdout);
347  }
348  else {
349  switch (inlevel) {
350  case 0:
351  PRINT0;
352  break;
353  case 1:
354  PRINT1;
355  break;
356  case 2:
357  PRINT2;
358  break;
359  case 3:
360  PRINT3;
361  break;
362  case 4:
363  PRINT4;
364  break;
365  case 5:
366  PRINT5;
367  break;
368  default:
369  break;
370  }
371  fflush(stdout);
372  }
373 
374  in_buff_full[first_buff] = 0; // can re-use buffer
375 
376 
377  // check for termination string
378 
379  if (got_finished) {
380  printf("finish string encountered...exiting\n");
381  exit(0);
382  }
383  if(!data_in) sleep(1) ;
384 
385  } // for (;;)
386 
387  return -1 ; // UNREACHABLE
388 }
389 
390 
391 void parse_arguments(char **argv, int argc)
392 {
393  int i,j;
394  time_t abstime;
395  struct tm *wtime;
396 
397  for (i=2; i<=argc; i++) {
398  if (strncmp(argv[i-1],"-h",2)==0) {
399  printusage();
400  }
401  }
402  mins = 5;
403  ngrep = 0;
404  time(&mytime); // get wall time
405 
406  for (j=0,i=2; i<=argc; i++) {
407  if (strncasecmp(argv[i-1],"-t",2)==0) {
408  if (argc == i) printusage();
409  if (strstr(argv[i],":") == NULL) {//no ':' in string
410  sscanf(argv[i],"%d",&mins);
411  abstime = mytime - 60*mins;
412  wtime = localtime(&abstime);
413  req_hour = wtime->tm_hour;
414  req_min = wtime->tm_min;
415  }
416  else { // absolute time
417  sscanf(argv[i],"%d:%d",&req_hour,&req_min);
418  }
419  time_requested = 1;
420  printf("display records starting from %02d:%02d\n", req_hour, req_min);
421  i++;
422  }
423  else if (strncasecmp(argv[i-1],"-n",2)==0) {
424  if (argc == i) printusage();
425  strcpy(level_str,argv[i++]);
426  if (strncasecmp(level_str,"DEB",3)==0) level = 0;
427  else if (strncasecmp(level_str,"NOT",3)==0) level = 1;
428  else if (strncasecmp(level_str,"WARN",4)==0) level = 2;
429  else if (strncasecmp(level_str,"ERR",3)==0) level = 3;
430  else if (strncasecmp(level_str,"OPER",4)==0) level = 4;
431  else if (strncasecmp(level_str,"CRIT",4)==0) level = 5;
432  else if (strncasecmp(level_str,"TERR",4)==0) level = 5;
433  else printusage();
434  }
435  else if (strncasecmp(argv[i-1],"-e",2)==0) {
436  // start scanning at current EOF
437  scan_from_end = 1;
438  printf("scanning from current end(s) of file\n");
439  }
440  else if (strncasecmp(argv[i-1],"-m",2)==0) {
441  // monochrome version (no control strings
442  monochrome = 1;
443  printf("monochrome version - no escape sequences\n");
444  }
445  else if (strncasecmp(argv[i-1],"-s",2)==0) {
446  if (argc == i) printusage();
447  strcat(start,argv[i++]);
448  }
449  else if (strncasecmp(argv[i-1],"-f",2)==0) {
450  if (argc == i) printusage();
451  strcat(finish,argv[i++]);
452  }
453  else if (strncasecmp(argv[i-1],"-g",2)==0) {
454  if (argc == i) printusage();
455  if (ngrep==MAX_GREPSTRINGS) printusage();
456  strcat(grep_string[ngrep++],argv[i++]);
457  grep_string[ngrep][0] = 0;
458  }
459  else if (strncasecmp(argv[i-1],"-v",2)==0) {
460  if (argc == i) printusage();
461  strcat(suppress,argv[i++]);
462  }
463  else {
464  strcpy(logfiles[j],argv[i-1]);
465  if (strstr(logfiles[j],"/")==NULL) {
466  //if absolute path not specified, prepend "/RTS/log/"
467  char temp[128];
468  strcpy(temp, logfiles[j]);
469  strcpy(logfiles[j],"/RTS/log/");
470  strcat(logfiles[j],temp);
471  }
472  j++;
473  }
474  }
475 
476  if (j>0) logfiles[j][0] = 0;
477  triggered = (start[0]) ? 0:1; //start string => triggered = 0
478 
479  printf("level=%d\n",level);
480  if (start[0]) printf("start \"%s\"\n",start);
481  if (finish[0]) printf("finish \"%s\"\n",finish);
482  j=0;
483  if (ngrep) {
484  printf("strings required:\n");
485  while (grep_string[j][0]) printf("\t\"%s\"\n",grep_string[j++]);
486  }
487  if (suppress[0]) printf("suppress \"%s\"\n",suppress);
488 
489 }
490 
491 // finds appropriate offset into files[i] to quickly find a timestamped
492 // record close to (earlier than) the time specified
493 void find_starting_point(int i, int start_time)
494 {
495  int ret, niter=0;
496  char * fret;
497  off_t fsize, fpos, lastpos, bytes_rem;
498  int ltime;
499  int req_hour, req_min;
500 
501  req_hour = (start_time/3600+8)%24;
502  req_min = (start_time%3600)/60;
503  printf("find_starting_point(): file %s: start time requested %02d:%02d\n",
504  logfiles[i], req_hour, req_min);
505 
506  lastpos = fpos = ftello(files[i]);
507 
508  ret = stat(logfiles[i],&statb) ;
509  fsize = statb.st_size;
510  bytes_rem = fsize - fpos;
511 
512  while (bytes_rem > 10000) {
513  fret = fgets(in_buff[i],sizeof(in_buff[i]),files[i]) ;
514  if(fret == NULL) {
515  if(errno) {
516  perror(logfiles[i]) ;
517  return;
518  }
519  fseeko(files[i], 0, SEEK_SET); // reset pointer to BOF
520  break ; // no data...
521  }
522  // reading twice is required after an arbitrary jump, to make sure
523  // that we get an entire record, on the 2nd try
524  fret = fgets(in_buff[i],sizeof(in_buff[i]),files[i]) ;
525  if(fret == NULL) {
526  if(errno) {
527  perror(logfiles[i]) ;
528  return;
529  }
530  fseeko(files[i], 0, SEEK_SET); // reset pointer to BOF
531  break ; // no data...
532  }
533 
534  sscanf(in_buff[i],"%*s %d:%d:%d",&in_hour,&in_min,&in_sec);
535 
536 // printf(" find_starting_point(): %d iters record time: %02d:%02d\n",
537 // niter, in_hour, in_min);
538  ltime = in_sec + 60*in_min + 3600*((in_hour+16)%24);
539 
540 // printf("find_starting_point(): ltime %d\t\tstart_time %d\n",
541 // ltime, start_time);
542  if (ltime < start_time) { // must jump forward
543  lastpos = ftello(files[i]);// keep track of last posn before forward step:
544  if (fsize - lastpos <= bytes_rem/2 ) break; // too close to EOF
545  fseeko(files[i], bytes_rem/2, SEEK_CUR);
546  bytes_rem = bytes_rem/2;
547  }
548  else if (ltime > start_time) { // must step backward
549  if (ftello(files[i]) < bytes_rem/2 ) { // safe to step back?
550  fseeko(files[i], 0, SEEK_SET); // no, go to BOF and return
551  break;
552  }
553  fseeko(files[i], -bytes_rem/2, SEEK_CUR); // yes
554  bytes_rem = bytes_rem/2;
555  }
556  niter++;
557  }
558  // if search terminates with file pointer set following the
559  // desired timestamp, set pointer to last position before a
560  // forward step was made
561  if (ltime >= start_time) fseeko(files[i],lastpos,SEEK_SET);
562 
563  printf(" starting %s at time %02d:%02d after %d iterns\n",
564  logfiles[i], in_hour, in_min, niter);
565 
566  return;
567 }
568 
569 void printusage() {
570  printf("usage: logBrowser [-n ALARM_LEVEL] [-t time] [-s \"start string\"]
571 \t[-f \"stop string\"] [-g \"grep string\"] [-v \"suppress string\"]
572 \t[file1 [,file2,....]]
573 
574 ALARM_LEVEL={DEBug,NOTice,WARNing,ERRor,OPERator,CRITical,TERR}
575 specifies the lowest level to be printed. Values of ALARM_LEVEL
576 are not case sensitive. Values may be abbreviated as indicated
577 in upper case. If no alarm level specified all messages will be
578 displayed.
579 
580 Log files specified by filename[s] will be displayed. If none are
581 specified, all log files will be shown. Default path is /RTS/log/.
582 
583  -t\t max age (minutes) of message to be displayed.
584  -t\t hour:min absolute time to start displaying messages.
585 \tDefault value = all messages (beginning of file(s)).
586 \tArguments containing ':' will be treated as absolute time
587 \tusing 24 hour clock. logBrowser assumes that a file starts at 08:00
588 \tand lasts no more than 24 hours. If a start string [-g] is specified,
589 \tthe default time is beginning of file.
590 
591  -s\tstart string: no lines will be shown until this string
592 \tencountered. Quotes are required if whitespace is part of string.
593 
594  -f\tstop string: program will print this string, then terminate.
595 \tQuotes are required if whitespace is part of string.
596 
597  -g\tgrep string: only lines containing (all of) these strings will
598 \tbe displayed. Multiple [up to %d] grep strings may be specified.
599 \tQuotes are required if whitespace is part of string. To specify
600 \tadd'l strings use
601 
602 \t\t-g string1 -g string2 -g \"string 3\".
603 
604  -v\tsuppress string: lines containing this string will not
605 \tbe displayed. Quotes are required if whitespace is part of string.
606 
607  -e\tstart scanning at the end of file(s).
608 
609  -m\tmonochrome version (no escape strings sent to terminal).
610 
611 
612 HINT: In order to get the records interleaved in correct time order from
613 more than one file, it is necessary to wait for a reocrd from each file
614 which meets all of the qualifications (-g, for example). If this is not what
615 you want, then open a separate logBrowser window for each file.\n\n",MAX_GREPSTRINGS);
616 
617  exit(0);
618 }