Oct 12, 2012

SAS Macros to Search and Report Errors and Warnings from your SAS Logs

How many a times at work have you gone thru the logs looking for ERROR, WARNINGS, NOTES etc for more information about the generated problem?…I’ve been doing a lot like that lately for developing / debugging / lines of code for more information…I just wished I had a piece of code that would search and report me the location / line number and few more information about….luckily I found this macro in a SAS SUGI paper…here’s the link….www2.sas.com/proceedings/sugi25/25/po/25p219.pdf
Then I had to customize it a little more to make the macro browse thru a folder of log files with a common runtime so that I can get all the errors into one report file…
Here’s an example and the full code below….
I intentionally induced couple of errors highlighted in RED font locations in these datasteps below….
%let LogLoc=C:\SASDATA;
%let cyle_runtime=10OCT2010102001;
 
proc printto log="&LogLoc.\example1_&cyle_runtime..log"; run;
 
DATA example1;
INPUT ID $ 1 SBP 2-4 DBP 5-7 GENDER $ 8 AGE 9-10 WT 11-13;
DATALINES;
1120 80M15115
2130 70F25180
3140100M89170
4120 80F30150
5125 80F20110
;
RUN;
PROC MENS;
RUN;
proc printto; run;
 
proc printto log="&LogLoc.\example2_&cyle_runtime..log"; run;
DATA CDS2;
     INPUT @1 CATEGORY $9. @10 NUMBER 3.;
DATALINES;
JAZZ     252
POP       49
CLASSICAL 59
RAP       21
GOSPEL    44
JAZZ      21
;
run;
ODS RTF;
PROC FREQ DATA=CDS ORDER=FREQ; WEIGHT NUMBER;
  TITLE3 'READ IN SUMMARIZED DATA';
  TABLES CATEGORY;
RUN;
proc printto; run;
This code produces 2 log files at C:\SASDATA\ with timestamp on them..
Error
error
error
Now running my SAS Macro %ReportErrorWarnings with the right parameters would result in a Error file at the same location…
%ReportErrorsWarnings(searchstr,searchloc,searchfiles,outfile,lines2display);
error
The macro from the SUGI paper was good…it created an arrow against the error / filename and linenumber…I customized the macro to browse thru a folder / location of logs etc… I agree it isn’t pretty but it works for me….This error file gives me the exact location (for eg line 20 in example 1 file and 2 lines above and below….). The number of lines to display above and below is again customizable though as the last parameter…
Similarly one can search for “ERROR:”,”WARNING:”, “NOTE:”,”not resolved”, “uninitialized”, “invalid data”, “_ERROR_=1″, “missing values”, “lock held by” etc…any other usual errors that you might want to track.
%ReportErrorsWarnings(searchstr,searchloc,searchfiles,outfile,lines2display);
Here’s the full code…with the repeatition of the above code again…for context…
%macro recsinds(table,macvar);                                      
/*----------------------------------------------------------------------
  this macro can be used to get the count of observations in a dataset
  into a macrovariable that you give.                                 
 
  usage: %recsinds(sashelp.class,macvar);                             
                                                                         
  &macvar is the observation count of your dataset                    
 ----------------------------------------------------------------------*/                                                                     
 
     %global &macvar;                                                 
     %local handle rc;                                                
     %let handle = %sysfunc(open(&table)); /* open the table */       
     %if &handle %then                                                 
         %do;                                                         
        /* get the observation count into the macvar macro variable */
            %let &macvar = %sysfunc(attrn(&handle, nlobs));           
             %let rc = %sysfunc(close(&handle)); /* close the dataset */
      %end;                                                           
     /* write the record count to the log */                          
      %put recsinds &table: &&&macvar.    &macvar=&&&macvar.;         
                                                                        
%mend recsinds;
 
%macro SearchErrorsWarnings(strg,loc,dsx,mm=1);
* mm=# of lines to display before and after target lines. mm=0,1,2 ...etc.;
 
filename dd "&loc.\&dsx.";
%global nnn;  *nnn=# of records in dsx;
 
data aa (keep= filen arrow linum xx);
      infile dd length=ln end=last;
      length llprted pthru 8 arrow $3 xx $120 filen $400;
      retain llprted pthru 0;
      *LLPRTed:line # of Last Line Printed; * PThru: to be line # of the last of: * the 2mm+1 lines to be displayed;
      length %do jj=1 %to &mm; x&jj %end; XT $ 120;
      retain filen XT ' ' %do jj=1 %to &mm; x&jj %end;;;
 
      filen="&dsx.";
 
      * These store the mm lines that are; * to be printed prior to a Find; * Next save these mm lines;
      %do jj=%eval(&mm-1) %to 1 %by -1;
            x%eval(&jj+1)=x&jj;;
      %end;
 
      x1=XT;
      input XT $varying120. ln;
      XT=upcase(XT);
      if index (XT,"THE SAS SYSTEM")>0 then delete;
      if index (XT,upcase("&strg"))>0 then
    do;
            filen="========================================";linum=.; arrow='====='; xx='========================================================================================';
            output;
            filen="&dsx."; arrow='';
 
            * Found one!;     * Insert a blank line between finds;      * provided the scopes of two finds ;    * do not overlap;
            if (_n_ > pthru + &mm + 1) & pthru > 0
            then
             do;
                  filen="========================================";linum=.; arrow='====='; xx='========================================================================================';
                  output;
                  filen="&dsx."; arrow='';
             end;
            *Output mm lines preceding the find;
            %do jj=&mm %to 1 %by -1;
                  if &jj<_n_ -="" code="" pthru="" then="">
                do;
                        xx=x&jj;
                        linum=_n_ - &jj;
                        output;
                  end;
            %end;
            linum=_n_;
            xx=XT;
            if &mm > 0 then arrow="-->";
            llprted=_n_;
            output; * Output the Found line;        
 
            * Compute pthru, the line # of the ;            * last line of the scope so that the;        * next mm lines can be printed;
            pthru=_n_+&mm;
            arrow=' ';
      end;
else
      do;
            if _n_<=pthru then
            do;
                  linum=_n_;
                  xx=XT;
                  output;
                  * Outputting the next mm lines;
            end;
      end;
if last then call symput('nnn',left(put(_n_,5.)));
run;
 
proc append base=allout data=aa; run;
%mend SearchErrorsWarnings;
 
%macro ReportErrorsWarnings(searchstr,searchloc,searchfiles,outfile,lines2display);
 
proc sql;
drop table allout;
drop table aa;
drop table new;
quit;
 
%let dircmd0=%nrbquote(%str(dir /b /s %")%str(&searchloc.\&searchfiles.)%str(%"));
FILENAME rootloc pipe "&dircmd0"
 
/*data _null_; run;*/
data new;
length file $ 1000;
infile rootloc;
input file;
cnt=count(file,"\");
file=translate(file,' ','\');
file=scan(file,cnt+1,' ');
call symput(compress('filen'||_N_),file);
run;
 
%recsinds(new,nobs);
%if &nobs gt 0 %then %do;
      %do k=1 %to &nobs;
      %SearchErrorsWarnings(&searchstr.,&searchloc.,&&filen&k.,mm=&lines2display.);
      %end;
 
      data allout;
      retain filen linum arrow xx;
      set allout;
      run;
 
      data _null_;
      file "&outfile." lrecl=32767;
      set allout;
      if _n_ =1 then
      do;
            put "FILENAME" '09'x "LINE #" '09'x "ARROW" '09'x "MESSAGE";
            put "*****************************************************************************************************";
      end;
      put filen '09'x linum '09'x arrow '09'x xx '09'x;
      run;
%end;
%else
%do;
      %put ==================;
      %put NO LOGS TO PROCESS;
      %put ==================;
%end;
 
%mend ReportErrorsWarnings;
 
 
%let LogLoc=C:\SASDATA;
%let cyle_runtime=10OCT2010102001;
 
proc printto log="&LogLoc.\example1_&cyle_runtime..log"; run;
 
DATA example1;
INPUT ID $ 1 SBP 2-4 DBP 5-7 GENDER $ 8 AGE 9-10 WT 11-13;
DATALINES;
1120 80M15115
2130 70F25180
3140100M89170
4120 80F30150
5125 80F20110
;
RUN;
PROC MENS;
RUN;
proc printto; run;
 
proc printto log="&LogLoc.\example2_&cyle_runtime..log"; run;
DATA CDS2;
     INPUT @1 CATEGORY $9. @10 NUMBER 3.;
DATALINES;
JAZZ     252
POP       49
CLASSICAL 59
RAP       21
GOSPEL    44
JAZZ      21
;
run;
ODS RTF;
PROC FREQ DATA=CDS ORDER=FREQ; WEIGHT NUMBER;
  TITLE3 'READ IN SUMMARIZED DATA';
  TABLES CATEGORY;
RUN;
proc printto; run;
 
%ReportErrorsWarnings(ERROR:,&LogLoc.,*_&cyle_runtime..log,&LogLoc.\error_&cyle_runtime..txt,2);