BookmarkSubscribeRSS Feed

Why Is the Log Report a Programmer’s Best Friend? Q&A, Slides, and On-Demand Recording

Started ‎03-25-2021 by
Modified ‎04-05-2021 by
Views 4,536

Log Report.jpg

 

Enjoy this sneak peek into content from SAS Global Forum 2021. Check out this and more great content coming in May!

 

Join SAS customer Peter Knapp as he discusses why it’s so important to review the log for problems to ensure your programs run without mechanical errors and deliver the desired results. He’ll show you how to automate this process, making your life easier and providing an easy tool to help analysts with the debugging process. See a demo in SAS® Enterprise Guide® and SAS® Studio. During this webinar you will learn:

  • How to create a Log Report macro.
  • How it works and why it’s different than the log summary.
  • How to tailor the macro for your specific needs.

 

 

The questions from the Q&A segment held at the end of the webinar are posted below and the slides from the webinar are attached.

 

Would this code work in a UNIX environment?

Yes, it will work in UNIX. You would need to adapt the filename statement.

https://documentation.sas.com/?cdcId=pgmsascdc&cdcVersion=9.4_3.5&docsetId=hostunx&docsetTarget=n1cw...

 

Are there any SAS system options you recommend activating to flag things in the Log?

I’m not aware of any SAS system options to flag things in the Log. That’s why I created the macro to look for those key words that are indicative of problems. I had the system option MPRINT turned on which shows you the notes associated with the macro. The system option SYMBOLGEN will show the value of macro variables when they are used. There is also a Data Step Debugger available in both SAS EG and SAS Studio 5.2. 

 

How can I program a script to run 24 times in a row for the last 24-month with different prompt (eg: YYYYMM = 201902-202102)?

This seems like this could be accomplished using a do loop that iterates through the 24 values for yyyymm.

 

In SAS if a numeric value is missing it will place (.). How can you make this blank without causing errors?

Use the OPTION MISSING = ""; which will change the period to missing.

 

Could you please show again how to copy a log file from an interactive SAS session?

Please watch the on-demand recording to catch this again.

 

Is _SASPROGRAMFILE only available if you use EG?  Is there an equivalent if you are not using EG?

The automatic variable _SASPROGRAMFILE is available for use in both SAS EG and SAS Studio.

 

You can write Put and PutLog messages to the log file then add them to the keywords you search for. Another system option to check out is DSOptions=Note2Err. This is an undocumented option used by SAS Tech help to run in stricter error checking mode. It turns some of the notes like uninitialized into errors which will then get picked up by keyword in the log report macro.

Thank you for those suggestions. I will check those out.

 

For my understanding, regarding the code you showed in your presentation, you place it at the top of every program you run? Or is there some way to place that block of code into the SAS EG settings to run every time?

Because the trade analysts I support star their programming with template programs, the Log Report code is always at the top of their programming. There are ways to have SAS run initialization code, such as autoexec files which are invoked on server initialization. Within Tools/Options/SAS Program ‘SAS Programs within the Options there is checkbox labeled ‘Submit SAS Code When server is connected’ that can used to specify code to run when opening SAS EG.

 

Where can we get your Log report macro code?

All the code I maintain is available at:

https://enforcement.trade.gov/sas/programs/amcp.html

It’s lot of code though. I’ve condensed the code relative to the Log Macro which I’ve attached and pasted below with some comments.

If someone wants to run part of a program after the log has been redirected to the external file, they won’t see the log of the piece of code they just ran. Therefor we added language at the top and bottom of the main program to selectively run the Log Report. That language at the top of the main program calls this macro:

/*--------------------------------------------------------------------*/

/* PART 1: WRITE LOG TO THE PROGRAM LOCATION                          */

/*--------------------------------------------------------------------*/

 

%MACRO CMAC1_WRITE_LOG;

    %IF %UPCASE(&LOG_SUMMARY) = YES %THEN

    %DO;

        FILENAME LOGFILE "&LOG.";

 

        PROC PRINTTO LOG = LOGFILE NEW;

        RUN;

    %END;

%MEND CMAC1_WRITE_LOG;

This macro will capture the number of observations in a dataset fed to the macro (first parameter) and save that number of observations in a macro variable specified by the second parameter:

 

/*--------------------------------------------------------------------*/

/* PART 2: MACRO TO GET COUNTS OF THE DATASETS                        */

/*     THAT NEED BE TO REVIEWED                                       */

/*--------------------------------------------------------------------*/

 

%MACRO CMAC2_COUNTER (DATASET =, MVAR =);

    %GLOBAL COUNT_&MVAR.;

    PROC SQL NOPRINT;

          SELECT COUNT(*)

           INTO :COUNT_&MVAR.

           FROM &DATASET.;

       QUIT;

%MEND CMAC2_COUNTER;

 

This is the Log Macro. It copies the contents of the external file back into the default log, reads that external file, and looks for keywords to accumulate, accumulates the keywords, stores those totals in macro variables, runs the CMAC2_COUNTER macro (see above) to find dataset totals, and prints a report to the bottom of log:

 

/*-------------------------------------------------------------------------*/

/* PART 3: REVIEW LOG AND REPORT SUMMARY AT THE END OF THE LOG FOR:        */

/*     (A) GENERAL SAS ALERTS SUCH AS ERRORS, WARNINGS, UNINITIALIZED ETC. */

/*     (B) PROGRAM SPECIFIC ALERTS THAT WE NEED TO LOOK OUT FOR.           */

/*-------------------------------------------------------------------------*/

 

%MACRO C_MAC3_READLOG (LOG = , ME_OR_NME =);

    /*---------------------------------------------*/

    /* PRINT FULL LOG TO THE SAS ENTERPRISE WINDOW */

    /*---------------------------------------------*/

 

    DATA _NULL_;

        INFILE LOGFILE;

        INPUT;

        PUTLOG _INFILE_;

    RUN;

 

    /*------------------------------------------------------------------*/

    /* CHECK THE JOB LOG FOR ERRORS, WARNINGS, UNINTIALIZED VARIABLES,  */

    /* CONVERTED, MISSING, REPEATS AND LICENSE.                         */

    /* PRINT THE SUMMARY TO THE JOB LOG                                 */

    /*------------------------------------------------------------------*/

 

    DATA _NULL_;

        INFILE "&LOG." END = END MISSOVER PAD;

        INPUT LINE $250.;

 

        IF SUBSTR(LINE,1,6) = "ERROR " THEN DO;

        PUT 'ERROR: SYNTAX - CHECK FOR ERRORS WITHOUT A COLON USUALLY WITH A LINE NUMBER RANGE (Ex: ERROR 200-322)';

        END;

 

        IF UPCASE(COMPRESS(SUBSTR(LINE,1,6))) = "ERROR:"  OR

           UPCASE(COMPRESS(SUBSTR(LINE,1,6))) = "ERROR " THEN

            ERROR + 1;

 

        ELSE IF UPCASE(COMPRESS(SUBSTR(LINE,1,8))) = "WARNING:" THEN

        DO;

            WARNING + 1;

            LICENSE_I = INDEX((LINE),'THE BASE PRODUCT');

            LICENSE_W = INDEX((LINE),'WILL BE EXPIRING SOON');

            LICENSE_X = INDEX((LINE),'THIS UPCOMING EXPIRATION');

            LICENSE_Y = INDEX((LINE),'INFORMATION ON YOUR WARNING PERIOD');

            LICENSE_Z = INDEX((LINE),'YOUR SYSTEM IS SCHEDULED TO EXPIRE');

 

            IF LICENSE_I OR LICENSE_W OR LICENSE_X OR LICENSE_Y OR LICENSE_Z THEN

                LICENSE + 1;

        END;

        ELSE

        IF UPCASE(COMPRESS(SUBSTR(LINE,1,5))) = "NOTE:" THEN

        DO;

            UNINIT_I = INDEX(UPCASE(LINE),'UNINITIALIZED');

            IF UNINIT_I THEN

                UNINIT + 1;

 

            REPEAT_I = INDEX(UPCASE(LINE),'REPEATS OF BY VALUES');

            IF REPEAT_I THEN

                REPEAT + 1;

 

            CONVERTED_I = INDEX(UPCASE(LINE), 'CONVERTED');

            IF CONVERTED_I THEN

                CONVERTED + 1;

 

            MISSING_I = INDEX(UPCASE(LINE), 'MISSING VALUES WERE');

            IF MISSING_I THEN

                MISSING + 1;

 

            DIVISION_I = INDEX(UPCASE(LINE), 'DIVISION BY ZERO DETECTED');

            IF DIVISION_I THEN

                DIVISION + 1;

 

            Invalid_I = INDEX(UPCASE(LINE),'INVALID');

            IF Invalid_I THEN

                Invalid + 1;

        END;

 

        /*--------------------------------------------*/

        /* CREATE MACRO VARIABLES FOR REPORTING LATER */

        /*--------------------------------------------*/

 

        CALL SYMPUTX('ERROR', ERROR);

        CALL SYMPUTX('WARNING', (WARNING-LICENSE));

        CALL SYMPUTX('LICENSE', LICENSE);

        CALL SYMPUTX('UNINIT', UNINIT);

        CALL SYMPUTX('REPEAT', REPEAT);

        CALL SYMPUTX('CONVERTED', CONVERTED);

        CALL SYMPUTX('MISSING', MISSING);

        CALL SYMPUTX('DIVISION', DIVISION);

        CALL SYMPUTX('INVALID', INVALID);

    RUN;

 

    /*----------------------------------------------------------------*/

    /*  REVIEW THE JOB LOG FOR PROGRAM SPECIFIC ALERTS. GET COUNTS OF */

    /*  THE DATASETS THAT WERE CREATED FOR VALIDATION PURPOSES. THE   */

    /*  LIST OF DATASETS CAN VARY BASED ON THE PROGRAM EXECUTED.      */

    /*----------------------------------------------------------------*/

 

    %IF %UPCASE("&ME_OR_NME.") = "MEHOME" %THEN

    %DO;

        %CMAC2_COUNTER (DATASET = NEGDATA_HM, MVAR = NEGDATA_HM);

        %CMAC2_COUNTER (DATASET = OUTDATES_HM, MVAR = OUTDATES_HM);

        %CMAC2_COUNTER (DATASET = NOCOST_HMSALES, MVAR = NOCOST_HMSALES);

 

        %IF &RUN_ARMSLENGTH. = YES %THEN

        %DO;

            %CMAC2_COUNTER (DATASET = HMAFFOUT, MVAR = HMAFFOUT);       

        %END;

 

        %IF &RUN_DOWNSTREAM. = YES %THEN

        %DO;

            %CMAC2_COUNTER (DATASET = COMPANY.&DOWNSTREAMDATA, MVAR = ORIG_DSSALES);

            %CMAC2_COUNTER (DATASET = NOCOST_DOWNSTREAM, MVAR = NOCOST_DSSALES);

        %END;

 

        %IF &COMPARE_BY_TIME. = YES %THEN

        %DO;

            %CMAC2_COUNTER (DATASET = RECOVERED, MVAR = RECOVERED);       

        %END;

 

        %CMAC2_COUNTER (DATASET = HMABOVE, MVAR = HMABOVE);

        %CMAC2_COUNTER (DATASET = HMBELOW, MVAR = HMBELOW);

        %CMAC2_COUNTER (DATASET = HM, MVAR = HMWTAVG);

        %CMAC2_COUNTER (DATASET = HMAVG, MVAR = HMAVG);

    %END;

    %ELSE

    %IF %UPCASE("&ME_OR_NME.") = "MEMARG" %THEN

    %DO;

        %CMAC2_COUNTER (DATASET = NEGDATA_US, MVAR = NEGDATA_US);

        %CMAC2_COUNTER (DATASET = OUTDATES_US, MVAR = OUTDATES_US);

        %CMAC2_COUNTER (DATASET = NOCOST_USSALES, MVAR = NOCOST_USSALES);

        %CMAC2_COUNTER (DATASET = NORATES, MVAR = NORATES);

        %CMAC2_COUNTER (DATASET = NO_DP_REGION_TEST, MVAR = NO_DP_REGION_TEST);

        %CMAC2_COUNTER (DATASET = NO_DP_PURCHASER_TEST, MVAR = NO_DP_PURCHASER_TEST);

        %CMAC2_COUNTER (DATASET = NO_DP_PERIOD_TEST, MVAR = NO_DP_PERIOD_TEST);

       

        PROC FREQ DATA = USNETPR NOPRINT;

             TABLES NVMATCH / OUT = NVMATCH_OUTPUT;

        RUN;

 

        PROC SORT DATA = NVMATCH_OUTPUT OUT = NVMATCH_OUTPUT;

            BY NVMATCH;

        RUN;

 

        %LET NVMATCH_TYPE1  = IDENTICAL;

        %LET NVMATCH_VALUE1 = 0;

        %LET NVMATCH_TYPE2  = SIMILAR;

        %LET NVMATCH_VALUE2 = 0;

        %LET NVMATCH_TYPE3  = CONSTRUCTED VALUE;

        %LET NVMATCH_VALUE3 = 0;

        %LET NVMATCH_TYPE4  = FACTS AVAILABLE;

        %LET NVMATCH_VALUE4 = 0;

       

        DATA _NULL_;

            SET NVMATCH_OUTPUT;

            SUFFIX = PUT(NVMATCH, 1.);

            CALL SYMPUTX(CATS('NVMATCH_VALUE', SUFFIX), PUT(COUNT, 8.) , 'G');

        RUN;

    %END;

    %ELSE

    %IF %UPCASE("&ME_OR_NME.") = "NME" %THEN

    %DO;

        %CMAC2_COUNTER (DATASET = NEGDATA, MVAR = NEGDATA);

        %CMAC2_COUNTER (DATASET = OUTDATES, MVAR = OUTDATES);

        %CMAC2_COUNTER (DATASET = NOFOP, MVAR = NOFOP);

        %CMAC2_COUNTER (DATASET = NOEXRATE, MVAR = NOEXRATE);

        %CMAC2_COUNTER (DATASET = NEGATIVE_NVALUES, MVAR = NEGATIVE_NVALUES);

        %CMAC2_COUNTER (DATASET = NEGATIVE_USPRICES, MVAR = NEGATIVE_USPRICES);

        %CMAC2_COUNTER (DATASET = NO_DP_REGION_TEST, MVAR = NO_DP_REGION_TEST);

        %CMAC2_COUNTER (DATASET = NO_DP_PERIOD_TEST, MVAR = NO_DP_PERIOD_TEST);

        %CMAC2_COUNTER (DATASET = NO_DP_PURCHASER_TEST, MVAR = NO_DP_PURCHASER_TEST);

    %END;

    %ELSE

    %IF %UPCASE("&ME_OR_NME.") = "DATAINT" %THEN

    %DO;

    %END;

 

    /*---------------------------------------------------------------------*/

    /*  PRINTING SUMMARY OF GENERAL SAS ALERTS AS WELL AS PROGRAM SPECIFIC */

    /*  ALERTS SUMMARY TO THE JOB LOG                                      */

    /*---------------------------------------------------------------------*/

 

    %PUT ************************************************************************************;

    %PUT ************************************************************************************;

    %PUT * GENERAL SAS ALERTS:                                                              *;

    %PUT ************************************************************************************;

    %PUT *   NORMALLY, BELOW ALERTS SHOULD BE ZERO                                          *;

    %PUT *   IF THEY DO NOT HAVE ZERO INSTANCES DETERMINE IF THERE IS AN ISSUE.             *;

    %PUT ************************************************************************************;

    %PUT # OF ERRORS                       = &ERROR;

    %PUT # OF WARNINGS                     = &WARNING;

    %PUT # OF UNINITIALIZED VARIABLES      = &UNINIT;

    %PUT # OF MISSING VALUES               = &MISSING;

    %PUT # OF REPEATS OF BY VALUES         = &REPEAT;

    %PUT # OF CONVERTED VARIABLES          = &CONVERTED;

    %PUT # OF DIVISION BY ZERO DETECTED    = &DIVISION;

    %PUT # OF INVALID DATA VALUES          = &INVALID;

    %PUT # OF LICENSE WARNINGS             = &LICENSE;

 

    %MACRO HDR;

        %PUT ************************************************************************************;

        %PUT * PROGRAM SPECIFIC ALERTS TO VERIFY:                                               *;

        %PUT ************************************************************************************;

        %PUT *   NORMALLY, COUNTS FOR THE BELOW LISTED DATATSETS HAVE ZERO OBSERVATIONS.        *;

        %PUT *   IF THEY DO NOT HAVE ZERO RECORDS DETERMINE IF THERE IS AN ISSUE.               *;

        %PUT ************************************************************************************;

    %MEND HDR;

 

    %MACRO DF;

        %PUT ************************************************************************************;

        %PUT * DATA FLOW:                                                                       *;

        %PUT ************************************************************************************;

        %PUT *   THIS SECTION SHOWS THE FLOW OF SALES IN THE PROGRAM.                           *;

        %PUT ************************************************************************************;

    %MEND DF;

 

    %IF %UPCASE("&ME_OR_NME.") = "MEHOME" %THEN

    %DO;

        %HDR;

        %PUT # OF HM SALES WITH PRICES AND/OR QTY <=0 (WORK.NEGDATA_HM)               = %CMPRES(&COUNT_NEGDATA_HM);

        %PUT # OF HM SALES OUTSIDE DATE RANGE (WORK.OUTDATES_HM)                      = %CMPRES(&COUNT_OUTDATES_HM);

        %PUT # OF HM SALES WITH NO COST DATA (WORK.NOCOST_HMSALES)                    = %CMPRES(&COUNT_NOCOST_HMSALES);

        %IF &RUN_DOWNSTREAM. = YES %THEN

        %DO;

            %PUT # OF DOWNSTREAM SALES WITH NO COST DATA (WORK.NOCOST_DOWNSTREAM)     = %CMPRES(&COUNT_NOCOST_DSSALES);

        %END;

        %DF;

        %PUT # OF TOTAL COST OBS GOING IN (COMPANY.&COST_DATA)                        = %CMPRES(&COUNT_ORIG_COST);

        %PUT # OF COST OBS TO BE WEIGHT AVERAGED (WORK.COST)                          = %CMPRES(&COUNT_PRE_AVGCOST);

        %PUT # OF WEIGHT AVERAGED COST MODELS (WORK.AVGCOST)                          = %CMPRES(&COUNT_AVGCOST);

        %PUT # OF TOTAL HM SALES GOING IN (COMPANY.&HMDATA)                           = %CMPRES(&COUNT_ORIG_HMSALES);

        %IF &RUN_ARMSLENGTH. = YES %THEN

        %DO;

            %PUT # OF HM SALES FAILING ARMS LENGTH TEST (WORK.HMAFFOUT)               = %CMPRES(&COUNT_HMAFFOUT);

        %END;

        %IF &RUN_DOWNSTREAM. = YES %THEN

        %DO;

            %PUT # OF TOTAL DS SALES GOING IN (COMPANY.&DOWNSTREAMDATA)               = %CMPRES(&COUNT_ORIG_DSSALES);

        %END;

        %PUT # OF HM SALES ABOVE COST TEST (WORK.HMABOVE)                             = %CMPRES(&COUNT_HMABOVE);

        %PUT # OF HM SALES FAILING THE COST TEST (WORK.HMBELOW)                       = %CMPRES(&COUNT_HMBELOW);

        %IF &COMPARE_BY_TIME. = YES %THEN

        %DO;

            %PUT # OF HM SALES PASSING THE COST RECOVERY TEST (WORK.RECOVERED)        = %CMPRES(&COUNT_RECOVERED);

        %END;

        %PUT # OF HM TO BE WEIGHT AVERAGED (WORK.HM)                                  = %CMPRES(&COUNT_HMWTAVG);

        %PUT # OF WEIGHT AVERAGED HM MODELS (WORK.HMAVG)                              = %CMPRES(&COUNT_HMAVG);

        %PUT ************************************************************************************;

        %PUT ************************************************************************************;

    %END;

    %ELSE

    %IF %UPCASE("&ME_OR_NME.") = "MEMARG" %THEN

    %DO;

        %HDR;

        %PUT # OF US SALES WITH PRICES AND/OR QTY <=0 (WORK.NEGDATA_US)                    = %CMPRES(&COUNT_NEGDATA_US);

        %PUT # OF US SALES OUTSIDE DATE RANGE (WORK.OUTDATES_US)                           = %CMPRES(&COUNT_OUTDATES_US);

        %PUT # OF US SALES WITH NO COST DATA (WORK.NOCOST_USSALES)                         = %CMPRES(&COUNT_NOCOST_USSALES);

        %PUT # OF US SALES WITH NO EXCHANGE RATES (WORK.NORATES)                           = %CMPRES(&COUNT_NORATES);

        %PUT # OF US SALES WITH INVALID REGIONAL VALUES (WORK.NO_DP_REGION_TEST)           = %CMPRES(&COUNT_NO_DP_REGION_TEST);

        %PUT # OF US SALES WITH INVALID PURCHASER VALUES (WORK.NO_DP_PURCHASER_TEST)       = %CMPRES(&COUNT_NO_DP_PURCHASER_TEST);

        %PUT # OF US SALES WITH INVALID TIME VALUES (WORK.NO_DP_PERIOD_TEST)               = %CMPRES(&COUNT_NO_DP_PERIOD_TEST);

        %DF;

        %IF &COST_TYPE = CV %THEN

        %DO;

            %PUT # OF TOTAL COST OBS GOING IN (COMPANY.&COST_DATA)                         = %CMPRES(&COUNT_ORIG_COST);

            %PUT # OF COST OBS TO BE WEIGHT AVERAGED (WORK.COST)                           = %CMPRES(&COUNT_PRE_AVGCOST);

            %PUT # OF WEIGHT AVERAGED COST MODELS (WORK.AVGCOST)                           = %CMPRES(&COUNT_AVGCOST);

        %END;

        %PUT # OF TOTAL US SALES1 (COMPANY.&USDATA)                                        = %CMPRES(&COUNT_ORIG_USSALES);

        %PUT # OF USSALES WITH IDENTICAL MODEL MATCHES (DERIVED FROM WORK.USNETPR)         = %CMPRES(&NVMATCH_VALUE1);

        %PUT # OF USSALES WITH SIMILAR MODEL MATCHES (DERIVED FROM WORK.USNETPR)           = %CMPRES(&NVMATCH_VALUE2);

        %PUT # OF USSALES WITH CONSTRUCTED VALUE MODEL MATCHES (DERIVED FROM WORK.USNETPR) = %CMPRES(&NVMATCH_VALUE3);

        %PUT # OF USSALES WITH FACTS AVAILABLE MODEL MATCHES (DERIVED FROM WORK.USNETPR)   = %CMPRES(&NVMATCH_VALUE4);

        %PUT # OF US SALES USED IN WEIGHT AVERAGING (WORK.WT_AVG_USSALES)                  = %CMPRES(&COUNT_WT_AVG_USSALES);

        %PUT ************************************************************************************;

        %PUT ************************************************************************************;

    %END;

    %ELSE

    %IF %UPCASE("&ME_OR_NME.") = "NME" %THEN

    %DO;

        %HDR;

        %PUT # OF US SALES WITH PRICES AND/OR QTY <=0 (WORK.NEGDATA)                 = %CMPRES(&COUNT_NEGDATA);

        %PUT # OF US SALES OUTSIDE DATE RANGE (WORK.OUTDATES)                        = %CMPRES(&COUNT_OUTDATES);

        %PUT # OF US SALES WITH NO MATCHING FACTORS OF PRODUCTION (WORK.NOFOP)       = %CMPRES(&COUNT_NOFOP);

        %PUT # OF US SALES WITH NO EXCHANGE RATES (WORK.NOEXRATE)                    = %CMPRES(&COUNT_NOEXRATE);

        %PUT # OF US SALES WITH INVALID REGIONAL VALUES (WORK.NO_DP_REGION_TEST)     = %CMPRES(&COUNT_NO_DP_REGION_TEST);

        %PUT # OF US SALES WITH INVALID PURCHASER VALUES (WORK.NO_DP_PURCHASER_TEST) = %CMPRES(&COUNT_NO_DP_PURCHASER_TEST);

        %PUT # OF US SALES WITH INVALID TIME VALUES (WORK.NO_DP_PERIOD_TEST)         = %CMPRES(&COUNT_NO_DP_PERIOD_TEST);

        %DF;

        %PUT # OF US SALES GOING IN (COMPANY.&USDATA)                                = %CMPRES(&COUNT_ORIG_USSALES);

        %PUT # OF FOP OBS GOING IN (COMPANY.&FOPDATA)                                = %CMPRES(&COUNT_ORIG_FOPDATA);

        %PUT # OF US SALES WITH NEGATIVE NORMAL VALUES (WORK.NEGATIVE_NVALUES)       = %CMPRES(&COUNT_NEGATIVE_NVALUES);

        %PUT # OF US SALES WITH NEGATIVE NET US PRICES (WORK.NEGATIVE_USPRICES)      = %CMPRES(&COUNT_NEGATIVE_USPRICES);

        %PUT # OF US PRICES AFTER WEIGHT AVERAGING (WORK.USPRICES)                   = %CMPRES(&COUNT_USPRICES);

        %PUT ************************************************************************************;

        %PUT ************************************************************************************;

    %END;

%MEND C_MAC3_READLOG;

If someone wants to run part of a program after the log has been redirected to the external file, they won’t see the log of the piece of code they just ran. Therefor we added language at the top and bottom of the main program to selectively run the Log Report. That language at the bottom of the main program calls this macro:

 

/*---------------------------------------*/

/* PART 4: CALL LOG SCAN MACRO ON DEMAND */

/*---------------------------------------*/

 

%MACRO CMAC4_SCAN_LOG (ME_OR_NME =);

    %IF %UPCASE(&LOG_SUMMARY) = YES %THEN

    %DO;

        PROC PRINTTO LOG = LOG;

        RUN;

 

        OPTIONS NOSYMBOLGEN NOMLOGIC NOMPRINT;  

        %C_MAC3_READLOG (LOG = &LOG., ME_OR_NME = &ME_OR_NME.);

        OPTIONS SYMBOLGEN NOMLOGIC MPRINT;

    %END;

%MEND CMAC4_SCAN_LOG;

  

In the main program, this macro variable will drive whether to run the Log Macro:

 

%LET LOG_SUMMARY = YES;                /*(T) Default value is "YES" (no    */

                                       /*    quotes). Use "NO" (no quotes) */

                                       /*    to run program in parts for   */

                                       /*    troubleshooting.              */

In the main program, define a path and filename to reroute the log to. I need to update my production code with the much simpler solution I developed for the Webinar:

 

FILENAME FILEREF "%SYSFUNC(TRANWRD(%UPCASE(&_SASPROGRAMFILE), .SAS, .LOG))";

 

especially since this solution does not work in SAS Studio. (The automatic macro variable (_CLIENTTASKLABEL is specific to SAS Enterprise Guide.):

 

/*--------------------------------------------*/

/* GET PROGRAM PATH/NAME AND CREATE THE SAME  */

/* NAME FOR THE LOG FILE WITH .LOG EXTENSION. */

/*--------------------------------------------*/

%GLOBAL MNAME LOG;

%LET MNAME = %SYSFUNC(SCAN(%SYSFUNC(pathname(C_MACS)), 1, '.'));

%LET LOG = %SYSFUNC(substr(&MNAME, 1, %SYSFUNC(length(&MNAME)) - %SYSFUNC(indexc(%SYSFUNC(

           reverse(%SYSFUNC(trim(&MNAME)))), '\'))))%STR(\)%SYSFUNC(DEQUOTE(&_CLIENTTASKLABEL.))%STR(.log); 

 

%CMAC1_WRITE_LOG;

In the main program, run this macro to define a specified macro variables that contains the number of observations in the specified dataset:

 

/*------------------------------------------------------------------*/

/* GET USSALES COUNT FOR LOG REPORTING PURPOSES                     */

/* DO NOT EDIT CMAC2_COUNTER MACRO                                  */

/*------------------------------------------------------------------*/

 

%CMAC2_COUNTER (DATASET = COMPANY.&USDATA, MVAR = ORIG_USSALES);

At the bottom of the main program, run this macro (the Log Macro) to generate the Log Report:

 

/***************************************************************************/

/* PART 20: REVIEW LOG AND REPORT SUMMARY AT THE END OF THE LOG FOR:       */

/*          (A) GENERAL SAS ALERTS SUCH AS ERRORS, WARNINGS, MISSING, ETC. */

/*          (B) PROGRAM SPECIFIC ALERTS THAT WE NEED TO LOOK OUT FOR.      */

/***************************************************************************/

%CMAC4_SCAN_LOG (ME_OR_NME = NME);

 

Is there a way to automate the exporting of the log or does it need to be manually exported each time to generate the file for the macro to analyze?

Please see the answer to the previous question that starts with, “From my understanding...”

 

Do you have some code that will print this Log or Log Report to OneNote?

Sorry, I don’t.

 

Recommended Resources

Log Reviewing Made Easy

Debugging 101

The Little SAS Book: A Primer, Sixth Edition

SAS Certification Practice Exams

 

Want more tips? Be sure to subscribe to the Ask the Expert board to receive follow up Q&A, slides and recordings from other SAS Ask the Expert webinars.  

Comments

If you are looking to process a LOG in SAS that has been saved as a permanent *.log file you convert the LOG as a SAS data set.

DATA LOGFILE;
  INFILE "code.log"
         LENGTH=LINELEN
         LRECL=200;
  INPUT @;
  VARLEN=LINELEN;
  INPUT @1 full_line $VARYING200. VARLEN;
 RUN;

 

Though running your log code against the LOGFILE data can be more of a pain.  Do you have an editor that can tag log errors, missing values, warnings based on generating a simple key stroke macro?  I sometimes use TextPad, an inexpensive editor, to tag errors, warnings, missing values, etc. and then hit F2 to get to each error to read the issue.  I also run mostly in batch mode since my large log files cannot always be generated using SAS/EG or SAS STUDIO.

Version history
Last update:
‎04-05-2021 08:04 AM
Updated by:
Contributors

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Article Tags