Including SAS Logs in Documents

Doug Hemken

February 2017

Background

SAS is unusual software in that it generates two distinct output streams. In addition to the typical tables and graphs (in a variety of formats, including text and html), SAS echos your code and writes notes and messages in a separate log file. Most software directs all of this into just one document.

When using R Markdown and knitr to create simple SAS documentation, we can often skip showing the user the contents of the log file. The default output in knitr is the listing (text) output.

However, from time to time it is useful to show the reader what they will find in a log file. How do we get knitr to include that?

There are several possibilities here, depending on just what we need to show the reader. Here I'll look at some ways to use SAS commands to save log output. In another document I'll look at how to rewrite knitr's "sas engine" to do this. The advantage of using SAS code is that a SAS programmer ought to know these commands anyway. The disadvantage is that if you need to show the reader a lot of logs, the setup will get repetitive.

For either approach, if you want to fine tune how much of the logs you show the reader, you need to know some R.

There are two options for using commands within SAS, PROC PRINTTO or the PUT and FILE statements within a DATA step. These use SAS to create separate files which can then be included in an R Markdown document.

Using PROC PRINTTO

There is a utility procedure in SAS, PROC PRINTTO, that allows you to redirect the log, the listing output, or both.

```{r, engine='sas', engine.path=saspath, engine.opts=sasopts}
proc printto log="saslog.log" new;
proc means data=sashelp.class;
run;
```
                            The MEANS Procedure

 Variable    N           Mean        Std Dev        Minimum        Maximum
 -------------------------------------------------------------------------
 Age        19     13.3157895      1.4926722     11.0000000     16.0000000
 Height     19     62.3368421      5.1270752     51.3000000     72.0000000
 Weight     19    100.0263158     22.7739335     50.5000000    150.0000000
 -------------------------------------------------------------------------

(The PROC PRINTTO command could be hidden with the echo=-1 chunk option.)

The log is then shown by a separate code chunk.

```{r}
cat(readLines("saslog.log"), sep="\n")
unlink("saslog.log")
```
NOTE: PROCEDURE PRINTTO used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds
      

3          proc means data=sashelp.class;
4          run;

NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: The PROCEDURE MEANS printed page 1.
NOTE: PROCEDURE MEANS used (Total process time):
      real time           0.03 seconds
      cpu time            0.03 seconds
      

(We could hide all the R code that reads and deletes the log file with the echo=FALSE chunk option.)

We can hide parts of the log file itself, too, by including just selected lines from readLines(). For simple manipulations, like removing several leading lines, the R code isn't too difficult:

```
cat(tail(readLines("saslog.log"), -5), sep="\n")
```

(Display the "tail" of the log from it's "head", minus the first 5 lines.)

DATA Step PUT Files

In additional to echoing code and giving notes and error messages, the SAS log is also used to capture data values from DATA steps. This is useful for debugging and even for very simple reports. Here we are using SAS's PUT and FILE statements (so technically we are sidestepping the SAS log, and there is no PROC PRINTTO required).

data class;
  set sashelp.class;
  file "sasput.txt";
  if sex eq "M" then put name= age= sex=;
run;

And we can see the result as before:

cat(readLines("sasput.txt"), sep="\n")
Name=Alfred Age=14 Sex=M
Name=Henry Age=14 Sex=M
Name=James Age=12 Sex=M
Name=Jeffrey Age=13 Sex=M
Name=John Age=12 Sex=M
Name=Philip Age=16 Sex=M
Name=Robert Age=12 Sex=M
Name=Ronald Age=15 Sex=M
Name=Thomas Age=11 Sex=M
Name=William Age=15 Sex=M

Showing SAS Error Messages in Logs

For showing errors in logs, SAS error messages come in two varieties: DATA step errors, and PROC step errors.

DATA step errors are signaled by an internal _ERROR_ variable, and can be captured and shown to the reader by the PROC PRINTTO method.

data class;
  set sashelp.class(obs=6);
  if sex eq 1 then put name= age= sex=;
run;

And the log looks like this:

NOTE: Character values have been converted to numeric 
      values at the places given by: (Line):(Column).
      5:6   
NOTE: Invalid numeric data, Sex='M' , at line 5 column 6.
Name=Alfred Sex=M Age=14 Height=69 Weight=112.5 _ERROR_=1 _N_=1
NOTE: Invalid numeric data, Sex='F' , at line 5 column 6.
Name=Alice Sex=F Age=13 Height=56.5 Weight=84 _ERROR_=1 _N_=2
NOTE: Invalid numeric data, Sex='F' , at line 5 column 6.
Name=Barbara Sex=F Age=13 Height=65.3 Weight=98 _ERROR_=1 _N_=3
NOTE: Invalid numeric data, Sex='F' , at line 5 column 6.
Name=Carol Sex=F Age=14 Height=62.8 Weight=102.5 _ERROR_=1 _N_=4
NOTE: Invalid numeric data, Sex='M' , at line 5 column 6.
Name=Henry Sex=M Age=14 Height=63.5 Weight=102.5 _ERROR_=1 _N_=5
NOTE: Invalid numeric data, Sex='M' , at line 5 column 6.
Name=James Sex=M Age=12 Height=57.3 Weight=83 _ERROR_=1 _N_=6
NOTE: There were 6 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.CLASS has 6 observations and 5 variables.
NOTE: DATA statement used (Total process time):
      real time           0.01 seconds
      cpu time            0.00 seconds
      

Although SAS notes that there are errors in processing individual observations, the DATA step still runs (a potential problem that any SAS programmer will find familiar).

To log PROC errors, you must use chunk option error=TRUE. Here, SAS exits with an error code (although it still gives you output), and in the R world this is usually the signal to stop everything.

```{r, engine='sas', engine.path=saspath, engine.opts=sasopts, error=TRUE, echo=FALSE}
proc printto log="saslog.log" new;
proc means data=sashelp.class;
  var gender; *ERROR HERE;
run;
```

Which gives us the log:

3          proc means data=sashelp.class;
4            var gender; *ERROR HERE;
ERROR: Variable GENDER not found.
5          run;

NOTE: The SAS System stopped processing this step because of errors.
NOTE: PROCEDURE MEANS used (Total process time):
      real time           0.03 seconds
      cpu time            0.00 seconds