BI Tools

Tips and tricks for building information maps, OLAP cubes, reports, and dashboards

BI Admin

Learn your way around a SAS BI installation.

Visual Analytics

Learn your way around the SAS Visual Analytics tool

Coding & Data

Extract, transform, and load your data into the SAS BI toolset

Stored Processes

Create and design stored processes like a rock star

Home » Coding & Data

SAS Code: Show the Values of All Macro Variables in Your SAS Session

Submitted by on 2012-10-12 – 8:17 AM

Earth

SAS Business Intelligence applications use global macro variables to store information about the environment as well as prompt values entered by a user. When developing a Stored Process it is often helpful to review the values of these macro variables. A simple %PUT statement, %PUT _GLOBAL_ ; , will write a listing of the values of all global macro variables to the log, but the formatting leaves a lot to be desired. This post describes a simple utility macro, %PrintMacVars, which can be used to create a report listing macro variables and their values.

Background

It never hurts to spend free time reading SAS Users Group papers. My favorite site to find papers is www.lexjansen.com. Recently I came a across a paper by Frank DiIorio, Building the Better Macro: Best Practices for the Design of Reliable, Effective Tools. Frank has a lot of great papers, and tools, available at his website,codecraftersinc.com. Among a plethora of excellent recommendations in the paper, was a macro utility %PrintMacVars, which prints a simple report to the log, showing the values of all global macro variables.  As I started playing with it, I decided it would be more useful if it had a SCOPE parameter to allow a user to report on local macro variables as wells as global variables, and also a parameter where the user could specify a list of macro variables to be printed.  The version below is an extended version of Frank’s macro.

%PrintMacVars

%macro PrintMacVars
  (scope=_local_ /*_local_ | _global_ | _user_ | _automatic_ | _ALL_ | macro name  */
  ,mvar =        /*OPTIONAL space-delimited list of macro vars to print.           */
  )
;

%local holdop ;

%let holdop=%sysfunc(getoption(ls,keyword)) ;

options ls=120;

%let scope=%upcase(&scope);

%*If scope is _LOCAL_ but %PrintMacVars was called in open code, set scope to _GLOBAL_;
%if &scope=_LOCAL_ and %sysmexecdepth=1 %then %let scope=_GLOBAL_;

proc sql noprint; 
  create table __macvars as 
  select
    scope
   ,name
   ,offset
   ,value

  from dictionary.macros 
  where (scope ne "PRINTMACVARS") 
        %if       &scope=_LOCAL_     %then %do;
          and (scope="%sysmexecname(%sysmexecdepth - 1)") /*name of macro that called %PrintMacVars*/
        %end;
        %else %if &scope=_GLOBAL_    %then %do;
          and (scope="GLOBAL")
        %end;
        %else %if &scope=_USER_      %then %do;
          and (scope ne "AUTOMATIC")
        %end;
        %else %if &scope=_AUTOMATIC_ %then %do;
          and (scope = "AUTOMATIC")
        %end;
        %else %if &scope=_ALL_       %then ;
        %else %do;
          and (scope="&scope") /*user passed a macro name as scope*/
        %end;

        %if %superq(mvar) ne %str() %then %do;
          and (findw(%upcase("&mvar"),trim(name)))
        %end;
  order by scope, name, offset
; 
quit; 

data _null_; 
  if _n_ = 1 then 
    putlog 117*'='
      /'%PrintMacVars listing of macro variables' 
      / '  Called by: ' 
           "%sysmexecname(%sysmexecdepth - 1)"
      / "  Scope: &scope"
      %if %superq(mvar) ne %str() %then %do;
        / "  Macro vars specified: &mvar"
      %end;
      /
      / 'Macro Variable' @35 'First 50 Characters' @86 'Scope' 
      / 33*'=' +1 50*'=' +1 32*'='
  ; 

  if eof then put 117*'=' ; 

  set __macvars end=eof; 
  where (offset=0);

  putlog '&' name $33. value $char50. +1 scope $33. ; 
run; 

*cleanup;
options &holdop;

proc datasets library=work memtype=data nolist;
   delete __macvars;
quit;

%mexit:
%mend PrintMacVars;

The macro has only two parameters. &SCOPE allows the user to specify the scope to be reported (_local_, _global_, _user_, _automatic_, _ALL_, or the name of a macro). The default is to report on all macro variables in the current scope (so if %PrintMacVars is called outside of a macro, it reports on global macro variables, if it is called within a macro, it reports on local macro variables). &MVAR allows the user to specify a list of macro variables to be reported.

The first step in the macro uses PROC SQL to read records from a dictionary table, DICTIONARY.MACROS, to generate a dataset that has the name of each macro variable, as well as its value and its scope. If you are not familiar with dictionary tables, they are automatically created by SAS, and they contain a wealth of metadata useful to SAS programmers (data about data, system options, titles, formats, etc.). Many users’ group papers have been written about dictionary tables, and Frank has an excellent dictionary table reference card available on his website. DICTIONARY.MACROS holds the values of all macro variables, so it is used as the source data for this utility. The WHERE clause on the SQL query subsets the data to select the macro variables of interest, according to the values the user specified for &SCOPE and &MVAR.

The second step is an old-fashioned DATA _NULL_ step, which reads in the data set generated by the SQL query, and uses PUTLOG statements to write the report to the log. The report lists each macro variable selected by the query, as well as its scope, and the first 50 characters of the value.

Using %PrintMacVars in a Stored Process

I often use PROC PRINTTO to write the log from a Stored Process to a permanent file, and find it helpful to have information on global macro variables recorded in the log. If a user emails me on Friday to say that they received an error from a Stored Process report they ran on Wednesday, I can open the log file, and the report on macro variables will tell me what client application they used to run the Stored Process, what ODS options were in effect (&_odsdest, &_odsstyle, etc.), and what prompt values the user provided. This information is critical to replicating the problem and debugging it.

Suppose I create a simple stored process, MakeReport, which has two prompts defined: DATA (name of source data set) and TITLE (title for report). If I start my stored process with a call to %PrintMacVars(), it will write the values of all macro variables to the log (both those entered by the user as values for prompts, and those created by the client application). So my Stored Process looks like:

MakeReport Stored Process

When I run the stored process, I specify Data=sashelp.shoes, and Title=Printout of SASHELP.shoes.  The log  shows the report generated by %PrintMacVars:

47        +*write values of all global macro variables to the log;
48        +%PrintMacVars()
====================================================================================================================
%PrintMacVars listing of macro variables
  Called by: OPEN CODE
  Scope: _GLOBAL_

Macro Variable                    First 50 Characters                                Scope
================================= ================================================== ================================
&DATA                             sashelp.shoes                                      GLOBAL                           
&SYS_SQL_IP_ALL                   -1                                                 GLOBAL                           
&SYS_SQL_IP_STMT                                                                     GLOBAL                           
&TITLE                            Printout of SASHELP.shoes                          GLOBAL                           
&_CLIENT                          StoredProcessService 9.3 JVM 1.6.0_27 Linux (am GLOBAL                           
&_ENCODING                                                                           GLOBAL                           
&_GOPTIONS                                                                           GLOBAL                           
&_GOPT_DEVICE                     javaimg                                            GLOBAL                           
&_GOPT_HSIZE                                                                         GLOBAL                           
&_GOPT_VSIZE                                                                         GLOBAL                           
&_GOPT_XPIXELS                                                                       GLOBAL                           
&_GOPT_YPIXELS                                                                       GLOBAL                           
&_GRAFLOC                         /sasweb/graph                                      GLOBAL                           
&_HTCOOK                          JSESSIONID=5B4A9FBAF73A9274ED812DF7D1A603B4        GLOBAL                           
&_HTUA                            Mozilla/4.0 (compatible MSIE 7.0 Windows NT 5.1 GLOBAL                           
&_METAFOLDER                      /User Folders/Quentin McMullen/                    GLOBAL                           
&_METAPERSON                      Quentin McMullen                                   GLOBAL                           
&_METAUSER                        XXXXXXXX                                           GLOBAL                           
&_NAMEVALUE                                                                          GLOBAL                           
&_ODSDEST                         HTML                                               GLOBAL                           
&_ODSDOC                                                                             GLOBAL                           
&_ODSOPTIONS                                                                         GLOBAL                           
&_ODSSTYLE                                                                           GLOBAL                           
&_ODSSTYLESHEET                                                                      GLOBAL                           
&_PRINTOPTIONS                                                                       GLOBAL                           
&_PROGRAM                         /User Folders/Quentin McMullen/MakeReport          GLOBAL                           
&_REPLAY                          "&_URL?_sessionid=624C4000-0132-11E2-8001-35363238 GLOBAL                           
&_REQMETH                         GET                                                GLOBAL                           
&_RESULT                          STREAM                                             GLOBAL                           
&_RMTADDR                         ###.###.###.#                                      GLOBAL                           
&_RMTHOST                         ###.###.###.#                                      GLOBAL                           
&_RR_CACHE                                                                           GLOBAL                           
&_RR_DOMAIN                                                                          GLOBAL                           
&_RR_FILEREF                                                                         GLOBAL                           
&_RR_PASSWORD                                                                        GLOBAL                           
&_RR_URL                                                                             GLOBAL                           
&_RR_USER                                                                            GLOBAL                           
&_SECUREUSERNAME                  XXXXXXXX                                           GLOBAL                           
&_SRVNAME                         XXXXXXXX                                           GLOBAL                           
&_SRVPORT                         8080                                               GLOBAL                           
&_STPERROR                        0                                                  GLOBAL                           
&_TMPCAT                          APSWORK.TCAT08AB                                   GLOBAL                           
&_URL                             /SASStoredProcess/do                               GLOBAL                           
&_USERLOCALE                      en_US                                              GLOBAL                           
&_USERNAME                        Quentin McMullen                                   GLOBAL                           
&_VERSION                         Version 9.3 (Build 473)                            GLOBAL                           
=====================================================================================================================

The first thing I notice is that the Stored Process Web Application creates a LOT of global macro variables. Many of these can be useful to the Stored Process developer. I often use  &_odsdest, &_username, and &_program in my code.  Near the top of the list you can see the two stored process prompts, DATA and TITLE, and the values they were given when the stored process was called.  If you would rather limit the list of macro variables to be printed, you can specify the list when you call %PrintMacVars, e.g. : %PrintMacVars(mvar=_username _program data title).

Using %PrintMacVars in a Macro

When developing and debugging a macro, it is often helpful to see the values of macro variables defined in the macro. This is particularly true when one macro calls another macro, and passes values to it through macro parameters.

Consider a simple macro which generates a printout of a few records from a dataset, and has a &DEBUG parameter.  When the macro is called with &DEBUG=1 (on) it calls %PrintMacVars to write all of the local macro variables to the log.

%macro tprint
  (data=
  ,obs=10
  ,title=
  ,debug=0
  );

  %let title=%sysfunc(propcase(&title));

  %*If in debug mode, dump local macro vars to log;
  %if &debug=1 %then %do;
    %printMacVars()
  %end;

  title2 "&title";
  proc print data=&data (obs=&obs);
  run;
  title2;

%mend tprint;

%tprint(data=sashelp.shoes
       ,title=printout of SASHELP.shoes
       ,obs=3
       ,debug=1
       )

The log shows a report of the parametet values passed by the user:

96   %tprint(data=sashelp.shoes
97          ,title=printout of SASHELP.shoes
98          ,obs=3
99          ,debug=1
100         )
=====================================================================================================================
%PrintMacVars listing of macro variables
  Called by: %TPRINT
  Scope: _LOCAL_

Macro Variable                    First 50 Characters                                Scope
================================= ================================================== ================================
&DATA                             sashelp.shoes                                      TPRINT
&DEBUG                            1                                                  TPRINT
&OBS                              3                                                  TPRINT
&TITLE                            Printout Of Sashelp.Shoes                          TPRINT
=====================================================================================================================

With a simple macro like %TPRINT, debugging could be done without %PRINTMACVARS.  But with a complex macro design, where macros have several parameters, and values are passed between macros, and new macro variables are generated, often the first step of debugging is to check the values of macro variables at key points in the code.  Adding calls to %PRINTMACVARS throughout the macro code can help dramatically.

Conclusion

One of the things I like most about the macro language is the ability to write little utility macros, like %PrintMacVars, and store them in an autocall library, so they are always there for you when you need them.  Like many utility macros, %PrintMacVars does not provide you with a new functionality. You could always inspect the values of macro variables with %PUT statements, or with the SYMBOLGEN option.  But %PrintMacVars makes it easy to generate a basic report that is easy to read.  And once you have an easy way to print macro variables, you will find you do it more often, and find debugging macro code and stored processes much easier.

Never miss a BI Notes post!

Click here for free subscription. Once you subscribe you'll be asked to confirm your subscription through your email account. You email address is kept private and you can unsubscribe anytime.
The following two tabs change content below.

Quentin McMullen

Quentin McMullen has been programming in SAS for 15 years, and for the past year has been working on SAS BI projects. He has presented at national and regional SAS user group conferences, and can often be found corresponding with colleagues on SAS-L.

Tags: