ABAP log

November 22, 2007

Your fault! (Using SAP logging in your ABAP.)

Filed under: ABAP, SAP — abaplog @ 8:50 pm

The most common problem explanation we hear from users is that there should be a program error that triggers when users do everything right. While we never get rid of mistakes we do, we have to admit this special class of elusive errors that magically disappear as soon as we can trace every critical step in the program flow. The key to solution is logging. Sure, you cannot log everything, but only the most critical points. What I have to do sometimes is to add a temporary logging capability to investigate problem popping up in production system. So, let’s see what we can do to give us more chances to smack users with a log and say “Your fault!” 🙂

There are numerous simple ways to show and store messages from your program. What’s important is that they have to be stored for some time to allow analysis after they happen. For example, if it’s a report or a program that is run in background most of the time, you have obvious choices of printing messages into the spool with normal WRITEs or using MESSAGE to have it stored in the job log. Both things can be found then in SM37.

If you want logging capabilities for an online report or a dialog program, you have to use other techniques. It is the same also for user exits that are called from SAP standard transactions or things that run in background by definition, like interfaces (e.g. APO CIF).

The simplest trick I am using in development to check program logic that cannot be easily debugged is my own table, a local object, with a long text field to store my messages and probably couple of other fields like date/time to distinguish the records. The table content is written by simple MODIFY. I usually have a program that zaps the content of my logging table, to clean up the mess.

When you are going to use this kind of logging in a “real” system, some things have to be taken care of:

  1. Uniqueness of records / keys, especially in case when the messages can be written by different program instances running at the same time
  2. More intelligent way of log reorganization (schedule).

When in development system, I can simply use the current time as the table key because I know that “I’m alone”. The official way of generating unique keys in SAP, however, is using number ranges. Briefly, you have number ranges for, say, production orders, and number ranges for materials etc. For each number range object, SAP can generate you the “next number” when you call a special function. This functionality is managed centrally and numbers will be unique even if you call the function on different application servers. The disadvantage of number ranges is that they have to be customized and monitored for “exhaustion”.

To save us the effort of creating a new number range object, customizing its range and monitoring, we can use another technique of generating unique numbers (or strings) that can then be used as part of table key: Global Unique Identifiers (GUIDs). Normally this is a long string, generated to be “almost” unique for a given system. The chance of having duplicate GUIDs is so small that I have never seen any checks whether to ID exists already. SAP uses GUIDs everywhere in APO, providing tables that map readable names / codes and internal identifiers – GUIDs. Those are normally 22 or 32 characters long. You can have a GUID generated for you by calling the function GUID_CREATE. When you add date/time as key fields into your logging table, you get a pretty simple logging system without any customizing. An additional reorganization program can be run periodically to have the entries older than, say, one month, deleted. Alternatively, if some errors have to be analysed and fixed (like problems with missing master data etc), you can provide table maintenance dialog (SE11/SE54) so that responsible user can delete checked log records after analysing them.

And finally let’s have a look at the real logging, as blessed by SAP. SAP Application Log, transaction SLG1, is the most common way to store messages. Log entries are created and stored using a set of functions. Logs are grouped by log objects and subobjects. For example, you have log group for object PPORDER (production order) and you can further classify logs with production order subobjects HEADER and OPERATION. Those are selections that you use on the initial screen of SLG1, along with others like: free text name / ID, date/time, user name and transaction code.

When creating a log entry, you can tell the system how long should it be stored before it’s deleted automatically. Unlike in the above case with your own log table, you don’t need a reorganization program. I normally request the log messages to be stored for 30 days, it can be less if you feel like your system will not be happy to carry the weight.

Enough with discussion, let’s see the code now:

* this include contains log system constants and has to be used
* in your program (or top include if it's a function group)
  include sbal_constants.

* the form writes a single log entry to application log,
* building log entry identifier from parameters pf_par1 and pf_par2,
* and writing the content of other parameters into the message
  form write_log
    using pf_par1
          pf_par2
          pf_str1  type  char50
          pf_str2  type  char50
          pf_str3  type  char50
          pf_str4  type  char50.

    data:
       ls_log        type bal_s_log,
       lt_handle     type bal_t_logh,
       lf_handle     type balloghndl,
       ls_msg        type bal_s_msg.

* we use production order / operation log objects
    ls_log-object     = 'PPORDER'.
    ls_log-subobject  = 'OPERATION'.
    concatenate 'Some_Name_' pf_par1 '_' pf_par2
      into ls_log-extnumber.
    ls_log-aluser     = sy-uname.
    ls_log-alprog     = sy-repid.
    ls_log-altcode    = 'YOUR_TCODE'.
    ls_log-aldate_del = sy-datum + 30.  "keep for one month
    ls_log-del_before = 'X'.

*   create a log
    call function 'BAL_LOG_CREATE'
         exporting
              i_s_log      = ls_log
         importing
              e_log_handle = lf_handle
         exceptions
              others       = 1.

*   define data of message for Application Log
*   Use generic message template with & & & &
    ls_msg-msgty     = 'S'.
    ls_msg-msgid     = '01'.
    ls_msg-msgno     = '319'.
    ls_msg-msgv1     = pf_str1.
    ls_msg-msgv2     = pf_str2.
    ls_msg-msgv3     = pf_str3.
    ls_msg-msgv4     = pf_str4.
    ls_msg-probclass = probclass_none.

*   add this message to log
*   this function can be called several times to have one log entry
*   store several different messages
    call function 'BAL_LOG_MSG_ADD'
         exporting
              i_log_handle = lf_handle
              i_s_msg      = ls_msg
         exceptions
              others       = 1.

*   save the log
    append lf_handle to lt_handle.
    call function 'BAL_DB_SAVE'
         exporting
              i_save_all     = 'X'
              i_t_log_handle = lt_handle
         exceptions
              others         = 1.

  endform. 

Note that here we are using a generic message (01/319). This way you are free to write anything you want but then you don’t get “long text” of the message when clicking it in the SLG1. Alternatively, we could use application specific message class, assigning it and message variables to appropriate fields of the structure bal_s_msg.

Advertisements

November 12, 2007

Copying SAP ALV layouts between clients.

Filed under: ABAP, SAP — abaplog @ 8:44 pm

When my colleague wanted to check some standard SAP reports, he got the following problem: the system had two different clients (mandants), one of them had the test data and in another one the ALV layouts were saved. You can create a transport request from ALV maintenance screen when in the list output, but for that, you had to have some data (this particular report stopped with an error message at the selection screen). It was known to him that ALV layouts can be transported with a separate program, RKKBALVI, but it seems that the program name indicated in the status line was a different one. After doing the usual trick with debugger (going to layout transport screen and entering /h to get into debugger), we could figure out that the report RKKBALVI actually calls the function module LT_VARIANTS_IMPORT_SELECTION, which can also be used separately. But best of all, we could notice in the debugger that the program name used for RKKBALVI was completely wrong! With the right program name, both ways (function module and SAP-supplied program) worked perfectly.

Blog at WordPress.com.