Thursday, August 12, 2010

CheckLog: A Utility for Reviewing the SAS Log

At my previous job, I worked on a SAS program that was intended to be run by someone who did not know SAS. So I needed a utility to tell the user whether the program completed successfully. The final result was a macro called CheckLog that checks a specified log, directory of logs, or the current log for error, warning, invalid, and other messages related to issues in a SAS program.

In order to accomplish this, I first needed a way to export the SAS log to a file so I could read it back in as a SAS data set and search for the terms and phrases associated with issues in the SAS program. Fortunately, there was already a utility available at work that exported the log to a file. The statement that exports the file is a simple DM statement, for example:

dm log "file 'C:\Directory\File.log' replace;" wpgm;

The next step required reading in the log into a data set. Initially, I used the import procedure (i.e., proc import) to import the text file into SAS. However, I needed to specify a delimiter that would not actually delimit the file. I wanted the file to be one large column. So I looked through the SAS font and found a character not common in English, the cedilla ( ¸ ). This character is also not usually found alone, even in languages that use it (like French). At least, I hope it isn't!

The last part is a continuing effort - identify and search for words and phrases associated with issues. The keywords were easy: error, warning, invalid, and uninitialized. The phrases, however, are elusive and open to interpretation. Sometimes phrases like "the data set has 0 observations" may be intended, and therefore should not be searched for. On the other hand, after completing an import, a data set with 0 observations is probably not intended. This type of issue requires the programmer to identify the problem.

Otherwise, the CheckLog macro can identify many common issues of varying levels of importance. Issues like the statement "numeric values have been converted to character values" or the statement "the query requires remerging summary statistics back with the original data" should be identified and resolved to avoid unintended consequences.

The macro includes several options. First, the macro can review a specified log, a directory of logs, or the current SAS log. As long as the file ends with ".log", it can be reviewed with this macro. Second, To enhance reviewing non-SAS logs, I added an argument to add additional keywords, like the word "caution" or "alert". Users could implement user-specified issues (instead of using put statements with the word ERROR or WARNING).

Third, the output can be renamed, with a default of "Log_issues". (When reviewing multiple logs from a directory, this option does not take effect, and the output is named "Logs_w_issues".) Fourth, an option to change the behavior of the pop-up message: always, never, and only when issues are found. Finally, an option to abort the currently executing SAS program if an issue is discovered. This allows the user to run code in production and end it prematurely if issues arise, so the code does not continue to execute with issues.

Readers interested in the macro can join a user group (see below) hosted by Google Groups to download the macro, view documentation, and receive emails when I updated the code. And please, send feedback! I would like to know if the macro works well and whether it checks for all of the issues users require.



CheckLog Site

CheckLog User Group

Description:

This group is a forum for the users of the SAS CheckLog macro to receive updates, ask questions, and post feedback. The CheckLog macro checks the SAS log(s) for issues, including lines with error, warning, invalid, or uninitialized messages. Additionally, other phrases associated with issues are checked, and specific phrases are ignored. The macro can also be used with non-SAS logs, as long as the log uses the same issue keywords.

Subscribe by Email:

Saturday, July 3, 2010

New Transitions

I have been working with an insurance company for almost two years, and I primarily worked with insurance claims to evaluate the effectiveness of care for our members and to discover patterns in care. Last week I accepted a new position with a physician clinical practice organization. The new position will provide experience on the other side of the health care industry, working with data from the doctor's office rather than the insurance company.

My wife, Kyra, is also looking for a new job. She's been having a rough time at her current child care center with the way it's being run. Last weekend she started submitting applications, and she's already had two interviews. She hopes to have a new job very soon, we hope with a daycare center where Luca can enroll and Kyra can seek state teaching certification. One day she would like to teach in an elementary school.

My son, Luca, has been growing rapidly. He's very talkative and he really seems to know what he wants. He enjoys going to school and visiting his friends. On the weekends, he's been taking swimming lessons and he loves the water! A bit too much actually--he really wants to swim on his own, but I'm sure he'd sink like a rock! Soon he will be two, and we hope to have a big birthday party for him.

So we're all undergoing changes. It may be a turbulent summer, but I hope we all end up a little older, a little wiser, and a little happier with these new transitions.

Friday, May 7, 2010

Open Last Table in SAS

I've been trying to find a way to open the last table created in SAS by using a keyboard shortcut. It's taken a while and many online searches to find it, but I finally figured out a good way to do it. I created a macro and set the macro to a keyboard shortcut. Here's the entire macro:

%macro OpenTable(table=&syslast) / des="Open a table";

dm "vt %sysfunc(compress(&table))" vt;

%mend OpenTable;

Set the macro to a keyboard shortcut by typing the following code into the keyboard shortcut editor window (F9):

gsubmit '%opentable;'

By default, the macro opens the last table created, and the argument can be modified for any table.

I'm so glad I found a way to do this! This should save a lot of time!

Update:

I added more to the macro code in case the table doesn't exist. Here's the new code:

%macro OpenTable(table=&syslast) / des="Open a table";

/* Check if table exists */
%if %sysfunc(exist(&table))=1 %then %do;

dm "vt %sysfunc(compress(&table))" vt;

%end;

/* Otherwise pop-up a message that it does not exist. */
%else %do;

dm 'postmessage "The specified table does not exist."';

%end;

%mend OpenTable;

Update Update:

I've developed a rather complex macro now that does the same thing:

OpenTable.sas

Sunday, April 25, 2010

Electronic Task Management

I have a lot of things to do, and sometimes I have trouble remembering it all. At home, I use my computer to do remember and remind me of many tasks. At work, I use a combination of paper and the computer. Since documents from my PC or the network are not always available, it is still useful to retain a copy of certain tasks on paper. Nonetheless, I still use a few computer tools, at home and work, to help remember all of these tasks and make task management easier.

My wife and I planned our wedding using a number of Google's online office applications, including Docs, Calendar, and Notebook. We also used RememberTheMilk (RTM), an online task management application. Managing our tasks online provided the opportunity to travel and retain access to our tasks, as well as keep an eye on tasks while at work. Gmail and Microsoft Outlook also provide some useful tools for managing tasks.

For our wedding, my wife and I kept a few documents for the wedding in Google Docs, including the guest list and a service provider comparison spreadsheet. In Google Notebook, we had song lists, service providers and costs, a long to do list, and all sorts of ideas in a shared notebook. (Unfortunately, Notebook is not longer supported, so I transferred our notes to Google Docs and have been using that application since.) For specific projects at work (for example, HEDIS), I keep a task list in an electronic document, and I usually archive the tasks as I go forward, keeping newer items at the top.

My wife and I also used RememberTheMilk for managing tasks. I used this quite a bit and shared to-do items with my wife. We went back and forth between our to-do list in Notebook, which was a long-term list, and RTM, which was a short-term list. I still use RememberTheMilk for short- and moderate-term tasks, and Google Docs for long-term tasks. RTM, Google Calendar, and Outlook's task feature are useful for scheduling a task with a reminder and a specific due date. Calendar was also useful for sharing events with my wife as well as managing our to-do list and making appointments.

I associate many tasks with emails. Using Gmail at home and Microsoft Outlook at work, I flag specific emails with varying levels of priority. Gmail has a "lab" feature that enables various flags or "stars" that users can associate with anything. I have flags for immediate tasks, "important" tasks (due soon), "assigned" tasks (due later), items to read or review, and items to monitor. Gmail also has a Task list feature. I have not used this feature extensively, since I have been using RememberTheMilk for some time, and it appears that RTM has more features (e.g., scheduling tasks, associating a task with a URL).

I hope these tips and strategies are useful to someone out there. If you have any novel and efficient ways of managing tasks, feel free to share them in a comment!

Tuesday, February 9, 2010

Checking for Issues in SAS

I've been working on a macro program to check the SAS log for specific "issue" keywords, including "error", "warning", "invalid", and "uninitialized". Typically these are associated with issues in the program. I'm considering adding the following statements as well:
  • "The query requires remerging summary statistics back with the original data"
  • "Numeric values have been converted to character values"
  • "Character values have been converted to numeric values"
The above statements may not always be associated with issues; however, there are better ways to write the program then to allow these statements to occur. For example, if the programmer wants to set a character variable to the text value of a numeric variable, it is better to use a put statement then to allow SAS to do it, as it may convert it incorrectly. Here's an example:

data _null_;
    today=today();
    call symput('today',today);
run;
%put NOTE: Today is &today;

The example sets today's date as a macro variable called today. However, the output is just a number. Depending on how the macro variable is used, this may be okay. Here's a better way to write it:

data _null_;
    today=today();
    call symput('today',compress(put(today,8.)));
run;
%put NOTE: The date value of today is &today;

Or, even simpler:

data _null_;
    today=today();
    call symputx('today',today);
run;
%put NOTE: The date value of today is &today;

Adding the "x" to the call symput statement will automatically set the numeric variable to character and remove spaces.

If the programmer actually intends to write out the date in a date format, then the following code will work. (Note that the date format can be whatever format desired. I like the YYMMDDN8 format since it sorts better.)

data _null_;
    today=today();
    call symput('today',put(today,yymmddn8.));
run;
%put NOTE: Today is &today;

The macro program I've written can identify these types of issues so they can be corrected. I've also recently discovered that other SAS users have created such log checking utilities, and they include more phrases that would indicate issues within various procedures that I do not use frequently. Once I have time to review these statements, I will include them in my program as well.

In addition to identifying these issues, I'm filtering out common issues that occur with the above keywords that are probably not associated with issues in the program. For example, a libname error from an autoexec program may not be an error for the program if it is not used. If it is used, there will be subsequent errors that would identify the issue.

This type of non-issue occurs at work frequently. We have a generic autoexec that runs when SAS is started, and it includes libnames that are located in directories I do not have the security rights to view. Therefore, every time I start SAS there are errors in the log. When I check the log for issues, I do not want these generic issues to appear.

How do you check for issues in a SAS program? Do you have a utility for doing so? What do you check for?