A month back, I wrote a short blog about the application log, and how great it would be to have yet another object oriented wrapper around the SBAL function group and friends.  Well, I wasn’t kidding.  I just put up a working version on Github, and added some documentation to explain some of the features I thought would make a logger tool great.  It’s been done fifty times before, but I haven’t seen any of them with the features I describe.  It’s all working, so feel free to kick the tires, fork the project, and please, please, let me know what you think!

epeterson320/ABAP-Logger · GitHub

Here is my original post: What Would a Developer-Friendly Application Log Look Like?

Below is most of the readme from Github:

ZCL_LOGGER

SAP Logging as painless as any other language

One of these things is not like the others

One of the most effective ways of debugging and inspecting a program’s operations is by writing to a log.  Logging is so powerful, just about every language has some way of doing it.  For example, if you want to write to a log in javascript, you just need to write the following line of code:

    console.log(“Leapin’ lizards, something went wrong!”);

Or how about in Ruby? Ruby has the added benefit of different levels of messages.

    require ‘logger’

    log = Logger.new(‘logfile.log’)

    log.warn(“You’re on thin ice, bud.”)

    log.info(“Things are normal again.”)

Writing an Android app? You’re in luck – you can log and optionally tag messages, all in one line of Java.

    Log.e(‘MAPS_INTERFACE’, ‘The system is down!!!’);

So, how does ABAP logging stack up to the other languages? What code is required to log a string to be viewed later?

    DATA: header TYPE bal_s_log,

          handle TYPE balloghndl,

          handles_to_save TYPE bal_t_logh.

 

    header-object = ‘ZINTERFACES’.

    header-subobject = ‘ACCOUNTING’.

    header-extnumber = ‘Stuff imported from legacy systems’.

 

    CALL FUNCTION ‘BAL_LOG_CREATE’

      EXPORTING

        i_s_log      = header

      IMPORTING

        e_log_handle = handle.

 

    CALL FUNCTION ‘BAL_LOG_MSG_ADD_FREE_TEXT’

      EXPORTING

        i_log_handle = handle

        i_msgty = ‘E’

        i_text = ‘You see, what had happened was…’.

    CALL FUNCTION ‘BAL_DB_SAVE’

      EXPORTING

        i_t_log_handle = handles_to_save.

If you’re not asleep after writing all that, then you’ve at least forgot what you were programming before you had to write to a log.  If anything, logging should be QUICK so you can get on with the real programming!

Get out of my way

A better log would barely interrupt my code, so I can output messages in one line, and you don’t lose the big picture as you are reading it. What do you wish the ABAP example above looked like?  How about this:

    DATA: log TYPE REF TO zcl_logger.

    log = zcl_logger=>new( object = ‘ZINTERFACES’

                           subobject = ‘ACCOUNTING’

                           desc = ‘Stuff imported from legacy systems’ ).

    log->e( ‘You see, what had happened was…’ ).

All the information, none of the boilerplate. This is the goal of ZCL_LOGGER.

Log anything

Making use of SAP’s run-time type services, we can pass almost anything we might want to log to an instance of ZCL_LOGGER, and it will do the heavy lifting.

Log a string!

    log->s( ‘Document 4800095710 created successfully’ ).

Log a bapi return message!

    DATA: rtn TYPE bapiret2.

    log->add( rtn ).

Log a…gasp…TABLE of bapi return messages!

    DATA: msgs TYPE TABLE OF bapiret2.

    log->add( msgs ).

Log an exception? Yep!

    TRY.

        rubber_band_powered_spaceship=>fly_to( the_moon ).

      CATCH zcx_not_enough_power INTO err.

        log->e( err ).

    ENDTRY.

Log the current system message.

    MESSAGE e001(oo) WITH foo bar baz INTO dummy.

    log->add( ). “you don’t even need to pass anything in, bro.

Log the return of a BDC call.

    CALL TRANSACTION ‘CO07’ USING bdc_tab MESSAGES INTO bdc_messages.

    log->add( bdc_messages ).

And that’s every scenario I’ve been able to think of, so far.

Don’t Ignore SAP’s Strengths

As frustrating as it can sometimes be, the SAP environment has a lot of power, and it would be good not to ignore it.  Transaction code SLG1 views and filters logs with ease, and it allows for added context variables and parameters. A new logger class should not reinvent the wheel, the wheelbarrow, or the mechanisms for saving and displaying logs.

If you have an instance of a log object, you can add context variables and a problem class, two of a few things that SLG1 handles well.

    log->w( obj_to_log = ‘Document created with errors’ “<– Which document? Needs context.

            context = new_document ). “<– Here’s the context

Since this log is designed to be simple, it has compromised a lot of the more exotic function modules in the SBAL family. If your log needs to use one of these function modules, the log header, handle and database id are all read-only members of the class, so you can pass them right along to the function module.

    log->i( ‘Results of system analysis’ ).

    CALL FUNCTION ‘BAL_LOG_MSG_CUMULATE’

      EXPORTING

        i_log_handle = log->handle

        i_s_msg = l_msg

        i_compare_attributes = abap_true.

Chainable

It’s 2014, so yes, you can chain method calls.

    zcl_logger=>new( object = ‘foo’ )->e( ‘Bad things happened: See details’ )->e( error ).

To report this post you need to login first.

27 Comments

You must be Logged on to comment or reply to a post.

  1. Lucas Tétreault

    Hey Eric,

    This looks promising! I have a couple of things I’m going to add to it that I’ve used for my own logging purposes and then I’ll send it back your way.

    I tried to import this in to a vanilla Netweaver 7.4 ABAP system and it didn’t like a few of the data types you were using in your tests. Once I figure out what it’s doing I’ll see if I can update it to some more generic types.

    Also, I imported in to one of our Dev systems (NW 7.0) and spent a few minutes fixing all the syntax that doesn’t work in the older versions. Then when I finally got everything to activate I tried running the unit tests and I get the following message:

    Error.png

    (0) 
    1. Lucas Tétreault

      Got the unit tests to run…

      Had to add the exceptions to the FM call to the following method in the test class.

         METHOD get_first_message.

           DATA: msg_handle TYPE balmsghndl.

           msg_handlelog_handle = log_handle.

           msg_handlemsgnumber = ‘000001’.

           CALL FUNCTION ‘BAL_LOG_MSG_READ’

             EXPORTING

               i_s_msg_handle = msg_handle

             IMPORTING

               e_txt_msg      = msg

             EXCEPTIONS

               log_not_found  = 1

               msg_not_found  = 2.

         ENDMETHOD.                    “get_first_message


      Without the exceptions there grabbing the result it was actually giving me the message and not finishing the unit tests.

      Capture.PNG

      (0) 
  2. Peter Inotai

    Hi Eric,

    Thanks for sharing this.

    Is there some release restriction for it? What is the lowest release where it should work?

    BTW do you know standard SAP class CL_CUX_APPL_LOG? It’s also quite similar.

    Cheers,

    Peter

    (0) 
    1. Eric Peterson Post author

      Hi Peter,

      I believe it will work on 7.2, but I only know it works on my system.  I’ll try to make it work on others as I get feedback from people with access to other systems.

      CL_CUX_APPL_LOG is missing a few features I wanted, such as fatory constructors, logging multiple data types with one method, method chaining, and automatic saving.

      (0) 
      1. Suhas Saha

        First of all, the pleasantries 😉 Nice, clean approach & good job!

        Eric Peterson wrote:

        CL_CUX_APPL_LOG is missing a few features I wanted, such as fatory constructors, logging multiple data types with one method, method chaining, and automatic saving.

        Now that the pleasantries are out of the way, what about the RECA message logging framework? 😉

        (0) 
          1. Suhas Saha

            Yep.

            I understand that the RE-CA package may not be available in all SAP installations and therefore i wish that this message logging framework was part of the SAP_BASIS component 🙂

            (0) 
            1. Eric Peterson Post author

              Suhas, if you prefer the RECA class, a class with no constructor, no documentation, and thirty methods, then I’m going to have to try to change your mind!

              Also, if the RECA class has any features you use that my class doesn’t have, submit a feature issue on Github and I’ll see if I can’t figure out a way to add it in.

              (0) 
              1. Shai Sinai

                Actually, RECA does have a “constructor” (technically, a factory): CF_RECA_MESSAGE_LIST. It even have some interesting implementations.

                However, I do agree that isn’t suitable for daily use and is missing many necessary features.

                (0) 
                1. Suhas Saha

                  Hi Eric, Shai answers the question in part. I just wanted to know what you think about the RECA messaging classes. I use the RECA class regularly and i never thought that it lacks some functionality. May be i’m too short-sighted 🙁

                  Lastly, i hope that you are taking my comments in the positive sense 🙂

                  However, I do agree that isn’t suitable for daily use and is missing many necessary features.

                  Shai Sinai – Can you please highlight a few for me please? Until now, i have heard only good things about the RECA logging classes 😐

                  (0) 
                  1. Shai Sinai

                    Well, there is one significant drawback, which comes right to my mind:

                    It doesn’t support context:

                    Context structure RECAMSGCONTEXT is hard-coded and many of the methods don’t have context data parameter.

                    I use context all the time.

                    (0) 
                  2. Michal Unger

                    Lacking support of CONTEXT as mentioned by Shai Sinai is really significant. However, the most important decision factor when thinking of using IF_RECA_MESSAGE_LIST as the key logging component is that it’s ECC systems specific (being part of RE_CA package – Real Estates, Cross Application), so that if you work in different system types (BI, SCM, …) it does not solve your problem. Building the Eric’s logger on top of very standard FMs for application logs is therefore very nice. Will test it soon!

                    (0) 
  3. Thomas Porcham

    Hi Eric,

    thanks for the implementation of this nice logging class!

    I imported the class into our sandbox System which is on version 7.4 SP level 03.

    Compiling worked fine. The ABAP unit tests gave a few errors.

    The unit tests had some i18n issues. Our system language is german. When logging a message the following text was logged: “Nachricht: Testing logger that saves.” which is obviously not the same as “Message: Testing logger that saves.”

    The same problem occurred when logging an exception “Fehler: ” is not the same as “Error”.

    I adapted the unit tests to read the expected strings based on the current system language. How can I send my version of the unit testing class to you? I cannot attach a file to this comment. I created a pull request on github. Hope that worked as I am a github newbie 🙂

    (0) 
    1. Shai Sinai

      Hi,

      You may see the context data by clicking on the Technical Message Information button.

      If you wish to display the context data in separate ALV columns, you have two options:

      1. Use the new transaction SLGD (Note 1746757 – Missing functionality in log display and printing).
      2. Write your own report based on FM APPL_LOG_DISPLAY (Check report SBAL_DEMO_04 for an example or report SBAL_DOCUMENTATION for further info).

      Edit:

      After reading your comment again, I wasn’t sure anymore that you were referring to application log (data) context.

      Since it might be useful to others, I decided to leave my comment anyway 🙂

      (0) 
      1. Rainer Hübenthal

        Hi thanks for your explanation. As I still cant see it I must made something wrong or ZLOGGER is doing something wrong 😆

        So I need to do some debugging now 🙁

        (0) 
  4. Alexander Petrenz

    Hi Eric,

    sorry for bugging, but did you already see the issue I created on git hub for your logging class project? If not, may I ask you to have a look at it?

    Thanks in advance

    Alex

    (0) 
  5. Jörg Krause

    Hi Eric,

    beautiful approach! Especially the auto-save option is brilliant. There is one problem you’d like to fix: in methods OPEN and POPUP, you are calling the display module like this:

      CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
         EXPORTING
           i_s_display_profile    = profile
           i_t_log_handle         = me->handle.
    

    This leads to a dump, because I_t_log_handle is meant to be a table of handles. So I had to change the call to get it working:

      data(lt_handles) = value bal_t_logh( ( me->handle ) ).
      CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
        EXPORTING
          i_s_display_profile    = profile
          i_t_log_handle         = lt_handles.
    
    (0) 

Leave a Reply