How to conditionally stop SAS code execution and gracefully terminate SAS session

26

French leave, English style leave, Irish goodbyeIn many SAS applications, there is a need to conditionally stop SAS code execution and gracefully (without generating an ERROR or a WARNING) terminate SAS session when no further processing is required. For example, your program processes large data and flags certain transactions as suspicious. If any suspicious transactions are found, then you continue further processing to gather more information about those transactions. However, if no transactions were flagged, you just want to stop your SAS job and augment the SAS log with a nice NOTE (not ERROR or WARNING), for example:

NOTE: No suspicious transactions are found. Further processing canceled. Exiting program execution.

Graceful termination techniques described in this post primarily apply to batch processing scenarios when SAS programs run unattended without human intervention. However, development of these programs is usually done in interactive SAS sessions, and we need to make sure SAS log is captured before our interactive application is “terminated”. Therefore, before we proceed reviewing and experimenting with SAS termination techniques, let’s make arrangements for capturing the SAS log.

Capturing SAS log

When you run a SAS program in batch you would usually submit it using OS command. For example, in UNIX/Linux you may submit or place in a shell script the following command:

sas /code/proj1/job1.sas -log /code/proj1/job1.log

The log file name is specified in the command itself (-log /code/proj1/job1.log), and it will record our “goodbye NOTE” generated before SAS session is terminated.

However, when you run/debug your program in an interactive application, e.g. SAS Enterprise Guide, the SAS log is captured in the Log Window. When SAS terminates its session, it will not only terminate your program execution, it will also terminate the application itself. That means your Enterprise Guide will close, along with its Log Window leaving you with no Log to see and inspect.

To capture SAS log to a file while debugging a SAS program containing session termination feature in interactive environment/application you can use PROC PRINTTO:

/* Beginning of SAS program */
proc printto log='C:\PROJECT1\program1.log';
run;
resetline;
 
<YOUR SAS PROGRAM>
 
/* End of SAS program */
proc printto; run;

In this code statement RESETLINE makes sure program line numbers in the SAS log always start with 1.

Let’s take a look at the following coding techniques of SAS session termination.

ABORT Statement - not so graceful termination

ABORT Statement is an executable statement that can be used as part of IF-THEN/ELSE conditional processing in a DATA step. Its action, however, extends beyond the DATA step as it not only stops executing the current DATA step, but also stops executing SAS session.

There are several flavors of the ABORT statement in SAS 9.4 – ABORT, ABORT ABEND, ABORT CANCEL, ABORT RETURN, and ABORT <number>. All of them are useful when something goes bad in your program (e.g. database library is not available at the time of run, or a data set is locked preventing you from writing to it, etc.), and you need to kill your program to avoid an even bigger snafu. All such situations are real, and ABORT statement handles them properly – stopping further program execution, killing SAS session, and generating an ERROR message in the SAS log.

But that ERROR message in the SAS log is what effectively disqualifies ABORT statements from graceful termination status. Think about it: “no suspicious transactions” is an occasion to celebrate, not a reason to cry an ERROR. Besides, in many mission-critical production-quality applications having an ERROR or even a WARNING in the SAS log is not an option.

Fortunately, for SAS® Viya users, there is a new ABORT EXIT option available in SAS Viya which ends SAS session without generating an ERROR in the log (it still generates WARNING).

ENDSAS statement - pitfalls

Let’s explore ENDSAS statement which stops SAS program execution and terminates the SAS session.

ENDSAS statement is a global statement, it takes effect as soon as it is encountered in a SAS program. It can be placed anywhere in a SAS program, except where only executable statements are allowed. For example, if you place it as part of an IF-THEN/ELSE statement you get a syntax ERROR because IF-THEN/ELSE statements require executable statements. Try running this code:

data _null_;
   if 1=1 then endsas;
run;

SAS log will show a syntax error:

2    data _null_;
3       if 1=1 then endsas;
                    ------
                    180
ERROR 180-322: Statement is not valid or it is used out of proper order.

However, if you place it in a conditionally executed DO-block it will not generate a syntax ERROR, but it will not produce what we wanted either because of the following.

First, it will execute even when the condition if FALSE. Second, since it executes inside the DO-block, it will end SAS data step and SAS session right there, without even giving DO-block a chance of completing its compilation, thus generating an ERROR. Here is the code illustration:

data _null_;
   if 0 then
   do; 
      put 'Nooo!';
      endsas;
   end;
run;

If you dare to run this code, here is what you will see in the SAS log after your SAS session gets killed:

2    data _null_;
3       if 0 then
4       do;
5          put 'Nooo!';
6          endsas;
 
ERROR 117-185: There was 1 unclosed DO block.

No, it is not what we are after.

ENDSAS statement – data step solution

Using coding technique described in my previous post How to conditionally execute SAS global statements, we can make ENDSAS to be conditionally generated within data step and executed after the data step.

Suppose we have a data set SUSPICIOUS_CASES which may have either zero or some positive number of observations. We want to stop further processing and terminate SAS session in case it has 0 observations.

Here is how we can achieve this:

data _null_;
   if 0 then set SUSPICIOUS_CASES nobs=n;
   if n=0 then call execute('endsas;');
   stop;
run;

The first if 0 condition is FALSE so data set SUSPICIOUS_CASES will never be read; it is needed just to capture its number of observations n.
In the second if n=0 we conditionally invoke CALL EXECUTE routine which un-quotes its character argument and pushes it outside the data step boundaries, thus generating the following code after RUN statement (as shown in the SAS log):

NOTE: CALL EXECUTE generated line.
1   + endsas;

Thus, we conditionally generated global ENDSAS statement and placed it after the data step. This global statement will terminate SAS session without a fuss; no ERROR, no WARNING, and even no NOTE.

If you want it to be not so “silent goodbye”, you can add some informative NOTES using PUT statement executed under the same condition as CALL EXECUTE (we combine them in a DO-block):

data _null_;
   if 0 then set SUSPICIOUS_CASES nobs=n;
   if n=0 then
   do;
      put 'NOTE: No suspicious cases were found. Further processing is terminated.';
      call execute('endsas;');
   end;
   stop;
run;

This code will conditionally output NOTE to the SAS log during data step. Obviously, you can generate any number of NOTE lines making your exit more verbose. We can place PUT statement either before or after CALL EXECUTE within the DO-block, because it will be executed within the data step, while generated ENDSAS statement will be executed after the data step.

ENDSAS statement – SAS macro solution

Another way of conditionally generating ENDSAS statement is by using SAS Macro Language.

The following macro language code will conditionally generate ENDSAS statement which then will gracefully terminate SAS session. For demonstration purposes, I explicitly specified TRUE condition as %if 1:

%if 1 %then
%do;
   %put NOTE: Ending SAS session gracefully.;
   endsas;
%end;

SAS log will show:

2    %if 1 %then
3    %do;
4       %put NOTE: Ending SAS session gracefully.;
NOTE: Ending SAS session gracefully.
5       endsas;
 
NOTE: SAS Institute Inc., SAS Campus Drive, Cary, NC USA 27513-2414

Additional resources

Thoughts? Comments?

Do you find this blog post useful? How do you handle graceful termination of your SAS programs? Please share with us below.

WANT MORE GREAT INSIGHTS MONTHLY? | SUBSCRIBE TO THE SAS TECH REPORT
Share

About Author

Leonid Batkhan

Leonid Batkhan, Ph.D. in Computer Science and Automatic Control Systems, has been a SAS user for more than 25 years. He came to work for SAS in 1995 and is currently a Senior Consultant with the SAS Federal Data Management and Business Intelligence Practice. During his career, Leonid has successfully implemented dozens of SAS applications and projects in various industries. All posts by Leonid Batkhan >>>

26 Comments

  1. Hi Leonid, thanks for this posting and your many other useful posts. I happened to have a need for this functionality and you saved me some digging and explained the different approaches in your usual crystal-clear style.

    • Leonid,

      Adding Informative Notes is something I can use for debugging and to capture data results at certain points of the program stream execution.

      Also like the Endsas.

      Reading discussions and links also Informative.

      Best,
      Deb

  2. Hi Leonid,
    Thanks for the post. This was really helpful.

    One observation on the usage of the ENDSAS; sample

    Does not work as expected when used exactly as shown in the sample.

    27         data _null_;
    28           set SUSPICIOUS_CASES nobs=n;
    29         
    30           if n=0 then
    31             do;
    32               put 'NOTE: No suspicious cases were found. Further processing is terminated.';
    33               call execute('endsas;');
    34             end;
    35         
    36           stop;
    37         run;
    
    NOTE: There were 0 observations read from the data set WORK.SUSPICIOUS_CASES.
    

    Works as expected with a small modification :

    39         data _null_;
    40           if n=0 then
    41             do;
    42               put 'NOTE: No suspicious cases were found. Further processing is terminated.';
    43               call execute('endsas;');
    44             end;
    45         
    46           set SUSPICIOUS_CASES nobs=n;
    47           stop;
    48         run;
    
    NOTE: No suspicious cases were found. Further processing is terminated.
    NOTE: There were 0 observations read from the data set WORK.SUSPICIOUS_CASES.
    NOTE: CALL EXECUTE generated line.
    1         + endsas;
    

    • Leonid Batkhan

      Thank you, Raja, for your comment and for catching this glitch. Indeed, the original code I was testing had if 0 then set SUSPICIOUS_CASES nobs=n; and somehow that if 0 then got lost. I just corrected it so it should work fine as well as your provided version. I also augmented code description to explain the if 0 then purpose.

  3. Could agree with you more Peter. SAS has dropped the ball and makes managing production jobs needlessly painful. See here(1) for a sasware ballot entry, and a method I use to terminate programs (but not sessions) cleanly.

    Note also that when programs fail, the log may not be updated. Here(2)'s a ballot entry for that too.

    The need for a proper session management has never been looked at imho. SAS has never bothered to provide simple tools for managing production jobs(3).

    (1) https://communities.sas.com/t5/SASware-Ballot-Ideas/Create-new-statement-STOPROGRAMRIGHTHERE-or-similar/idi-p/332062

    (2) https://communities.sas.com/t5/SASware-Ballot-Ideas/Reinstate-the-unbuflog-option/idi-p/289101

    (3) This is true also of rogue warning messages. See ballot entry https://communities.sas.com/t5/SASware-Ballot-Ideas/Add-more-options-to-customise-how-messages-are-displayed-in-the/idi-p/325172 and https://communities.sas.com/t5/SASware-Ballot-Ideas/Enable-disabling-warnings-in-log-when-a-Hadoop-string-is-long/idi-p/341800 for example

  4. Hi Leonid,
    Thank you for reminding the world of the behavior of ENDSAS; I used the CALL EXECUTE and the MACRO methods in the past. There is still an historical paper floating on the internet about it: https://support.sas.com/resources/papers/proceedings-archive/SUGI95/Sugi-95-73%20Tilanus.pdf

    It works as long as the step where you want to test whether you want to stop is actually entering the execution phase. If it is stuck before that because some input file is not available you have to apply a different approach. I presented a paper about that as well: http://www2.sas.com/proceedings/sugi31/029-31.pdf

  5. Thanks Leonid! As a SAS App developer this issue is a very pertinent one. If a SAS backend service fails, it's necessary to reset the SYSCC and return a graceful response to the user. On certain installs of SAS (I forget whether it was 9.3 or 9.4m3) the `endsas` statement can also cause problems, specifically with load balancing. Basically, those sessions that are ended are not recycled, which means that the multibridge sessions are all used up and the Stored Process server stops responding.

    Not a problem with current versions of SAS, but for an app developer working with customers in multiple SAS configurations, we need to ensure all bases are covered.

    For that reason we adopted another approach - one that lets you handle the error, and prevent downstream code being executed.

    The approach was simple - handle the error, then open a macro, but don't close it! We documented it in a SASjs/core macro, here: https://core.sasjs.io/mp__abort_8sas_source.html

    • Leonid Batkhan

      Thank you, Alan, for your constructive feedback and sharing your implementation. I just wanted to underscore the distinction between handling errors/irregularities and handling logically plausible events as I describe in the example for graceful exit. While ABORT statement is appropriate for the first case, it is not fit for the latter case where ENDSAS provides greater flexibility.

  6. Peter Lancashire on

    Thanks for these insights. In my experience SAS error handling and flow control is one of the most challenging aspects of building robust batch processes. I have never found a way cleanly to trap errors and deal with them effectively. Hence my code includes a lot of prevention and conditional %includes of code to try to modularize it. Any structural improvements in SAS here would be welcome. Your bizarre trick for ENDSAS shows the need for improvement. How about a try-catch-finally construct?

    • Leonid Batkhan

      Thank you, Peter, for your feedback. I hope our R&D will look into your suggestions. Regarding the “bizarre trick for ENDSAS”, I do not find it bizarre, but rather quite natural and beautiful. Perhaps you were judging it while thinking in some other language (Java?) than SAS. Wouldn’t you agree that perspective here is important? Here is an example. English-speaking people can say “I love you” where the order of words is of the essence. However, in Russian these three words can be said in any order thus forming 6 possible sentences, each valid, having its own flavor. For an English speaker it may feel bizarre to hear “I you love”, but that is the most common words order in Russian equivalent (“Я тебя люблю”). On the other hand, for a Russian speaker the words order restriction may also feel bizarre 🙂

      • Is this due to Russian words having suffixes that determine their grammatical function Leonid? Like Latin does?

        • Leonid Batkhan

          No, this is due to less structured sentences in Russian when words order is not as predetermined as in English. Although word endings variety could be another reason for looking "weird". 🙂

  7. Hi Leonid,
    I like your post.
    And I agree with Nicole about the RESETLINE statement being a nice little tidbit. When you are trying to teach SAS and want to capture or show the log, and don't want your log output to have a line number of 100 plus, you can use this. (I am sure there are many other uses). SAS Studio automatically by default clears the log completely unless you change the settings (which has both pros and cons).
    Thanks!

    • Leonid Batkhan

      Thank you, Lisa! That is exactly why I put it in - to capture consistent log line numbers for this blog post. I agree, it is not always beneficial to reset the log line numbering, that is why it's nice to have full control over it.

Leave A Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back to Top