Skip to Content
Technical Articles
Author's profile photo Serkan Saglam

Standard or Custom Class for Application Logs? – Part 1

Or as an alternative title ‘Why Do ABAP Developers Write Their Own Classes for Application Logs?

This is a blog series about Application Log, consisting of the following chapters:

  • Drag Race Between Three Classes
  • Displaying an Application Log in a Resizable Window
  • Customizing Log Display Using Display Profiles
  • A Custom Class for Application Log


In a very normal world, we create log objects to collect messages (from the SYST structure, exceptions, BAPI, or batch input). We then save the log and optionally display it. This means we basically have 4 steps: create, collect, save, and display. In some scenarios, we may have additional requirements such as deleting or inserting messages.

As a developer, I prefer to use a pure standard class that is present in every system and does not contain any specific logic. Does such a class exist? Let’s find the answer.

It is possible to reach a list of classes that can be related by searching with CL*LOG* and CL*MESSAGE* patterns in transaction SE24. Eventually something like the following list remains:


Let’s include those written in bold text in a drag race¹ and see how functional they are. Others were eliminated because they did not have adequate methods to participate in the race.

⚠️ Note that not all of these classes may be available on your system.

Drag Race Between Three Classes

First, we will compare some methods of the selected classes. Second, we will write sample code for each of them. Let’s see which one crosses the finish line first, 402 meters away. 🏁

Method Comparison

Purpose Method Method Method

ℹ️ There is a factory class for creating a log.


Add text






Add message – ADD_MSG






Add exception


ℹ️ Previous messages in the exception chain are not added.



ℹ️ Previous messages in the exception chain are not added.

Add BAPI messages N/A



Add batch input messages N/A N/A N/A
Insert message N/A N/A – INSERT
Set level of detail



Yes, as an input parameter in related methods – SET_DETAIL_LEVEL
Cumulate messages N/A Yes, as an input parameter in related methods Yes, as an input parameter in related methods
Delete message (from memory) N/A N/A – DELETE_MESSAGE
Delete all messages (from memory) – REFRESH – DELETE_MESSAGES – CLEAR
Delete log (from DB) N/A – DELETE_LOG N/A
Get log number Yes, as a return parameter when the log saved Yes, as a return parameter when the log saved N/A
Get log header N/A – GET_LOG_HEADER – GET_HEADER
Get all messages N/A – GET_MESSAGES





Has messages?

Is empty?




Save log – SAVE – SAVE_LOG – STORE
Display log


✅ Display profiles can be used.


⚠️ Display profiles can not be used.

ℹ️ There is no method to display the log, but function RECA_GUI_MSGLIST_POPUP can be used.

N/A: Non available

Now let’s take a look at some simple code examples of how to use these classes.

↑ Top

Usage example of the CL_BAL_LOGOBJ class

📄 ABAP code

    " Create the log
    DATA(appl_log) = NEW cl_bal_logobj( i_log_object        = 'APPL_LOG'
                                        i_default_subobject = 'OTHERS'
                                        i_extnumber         = 'CL_BAL_LOGOBJ' ).

    " Add a message
    MESSAGE s361(00) INTO DATA(msgtext).
    appl_log->add_msg( i_probclass = if_bal_logger=>c_probclass_none ).

    " Add an exception
        RAISE EXCEPTION TYPE cx_demo_constructor.
      CATCH cx_demo_constructor INTO DATA(exception).
        appl_log->add_exception( exception ).

    " Save the log
    appl_log->save( IMPORTING et_lognumbers = DATA(log_numbers) ).

    " Display the log
    appl_log->display( ).

  CATCH cx_bal_exception.

"TRY .
"    cl_demo_output=>write( |CL_BAL_LOGOBJ log number: { log_numbers[ 1 ]-lognumber }| ).
"    cl_demo_output=>write( |CL_BAL_LOGOBJ log handle: { log_numbers[ 1 ]-log_handle }| ).
"    cl_demo_output=>display( ).
"  CATCH cx_root.

🖥️ Output

💭 Comments

This class is included in SAP standard. (Package: SZAL, Application Component: BC-SRV-BAL). Although it does not contain methods for adding BAPI and BDC messages, it can be used for basic needs. Note that there is no method that returns the collected messages. Therefore, this class can be used to collect messages and to save and display logs.

☢️ Important note

This class also has a method called ADD_DEBUG_MESSAGE which can be used to add a message with the call stack. If this method is used with the parameter I_DETLEVEL and the log is displayed with the display profile “hierarchy by message detail level”, a runtime error CONVT_NO_NUMBER occurs. This is because the following line in CL_BAL_LOG_BASE->IF_BAL_LOGGER~ADD_DEBUG_MESSAGE has an incorrect assignment:

l_s_msg-detlevel = if_bal_logger~mv_loghandle.

The runtime error occurs in the DETLEVEL_FILL macro in LSBAL_DISPLAY_BASEF05. (Line 344). Therefore, the ADD_DEBUG_MSG method should not be used when messages are to be displayed by detail level.

An example code to reproduce the error:

DATA display_profile TYPE bal_s_prof.

    e_s_display_profile = display_profile.

    DATA(log) = NEW cl_bal_logobj( ).
    log->add_debug_msg( i_detlevel = '1' ).
    log->display( i_disp_profile = display_profile ).
  CATCH cx_bal_exception .

↑ Top

Usage example of the CL_PTU_MESSAGE class

📄 ABAP code

" Create the log
DATA appl_log TYPE REF TO cl_ptu_message.

DATA(log_header) = VALUE bal_s_log( object    = 'APPL_LOG'
                                    subobject = 'OTHERS'
                                    extnumber = 'CL_PTU_MESSAGE' ).
    is_log = log_header
    OTHERS = 3.

CHECK appl_log IS BOUND.

" Add a message
MESSAGE s361(00) INTO DATA(msgtext).
appl_log->add_message_simple( EXPORTING iv_level = CONV #( if_bal_logger=>c_probclass_none ) ).

" Add BAPI messages
DATA bapiret2_tab TYPE bapiret2_tab.

    airlineid    = VALUE bapisflkey-airlineid( )
    connectionid = VALUE bapisflkey-connectid( )
    flightdate   = VALUE bapisflkey-flightdate( )
    return       = bapiret2_tab.

appl_log->add_bapiret2_tab( EXPORTING it_bapiret2 = bapiret2_tab ).

" Save the log
appl_log->save_log( IMPORTING  es_new_lognumber = DATA(log_number)
                    EXCEPTIONS OTHERS = 2 ).

" Display the log
appl_log->display_log( EXPORTING  iv_as_popup = abap_true
                                  iv_use_grid = abap_true
                       EXCEPTIONS OTHERS = 2 ).

"cl_demo_output=>write( |CL_PTU_MESSAGE log number: { log_number-lognumber }| ).
"cl_demo_output=>write( |CL_PTU_MESSAGE log handle: { log_number-log_handle }| ).
"cl_demo_output=>display( ).

🖥️ Output

💭 Comments

Although it is possible to collect BAPI return messages with this class, it does not include a method for collecting BDC messages. There is also no method for collecting exception messages. The inability to use display profiles is another disadvantage.

↑ Top

Usage example of the CL_RECA_MESSAGE_LIST class

📄 ABAP code

" Create the log
DATA(msg_list) = cf_reca_message_list=>create( id_object    = 'APPL_LOG'
                                               id_subobject = 'OTHERS'
                                               id_extnumber = 'CL_RECA_MESSAGE_LIST' ).
CHECK msg_list IS BOUND.

DATA(log_header) = msg_list->get_header( ).
log_header-params-altext = 'Appl. log: Standard text'.
msg_list->change_header( EXPORTING  is_msg_header = log_header
                         EXCEPTIONS OTHERS        = 2 ).

" Add a message
MESSAGE s361(00) INTO DATA(msgtext).
msg_list->add_symsg( EXPORTING id_probclass = if_bal_logger=>c_probclass_none ).

" Add BAPI messages
DATA bapiret2_tab TYPE bapiret2_tab.

    airlineid    = VALUE bapisflkey-airlineid( )
    connectionid = VALUE bapisflkey-connectid( )
    flightdate   = VALUE bapisflkey-flightdate( )
    return       = bapiret2_tab.

msg_list->add_from_bapi( EXPORTING it_bapiret = bapiret2_tab ).

" Add an exception
    RAISE EXCEPTION TYPE cx_demo_constructor.
  CATCH cx_demo_constructor INTO DATA(exception).
    msg_list->add_from_exception( io_exception = exception ).

" Save the log
msg_list->store( EXPORTING  if_in_update_task = abap_false
                 EXCEPTIONS OTHERS = 2 ).

" Display the log
    io_msglist = msg_list.

"cl_demo_output=>write( |CL_RECA_MESSAGE_LIST log number: Who knows? | ).
"cl_demo_output=>write( |CL_RECA_MESSAGE_LIST log handle: { msg_list->get_handle( ) }| ).
"cl_demo_output=>display( ).

🖥️ Output

💭 Comments

This is the richest class in terms of method in the race. Strangely, it has no method for adding free text. Like other classes, it has no method for collecting BDC messages. The good thing is that there is a method for inserting messages. An original feature is the possibility to display the log in a resizable window. (The next chapter is about it)

⚠️This class may not be available in every system.

↑ Top


As you can see, each class has different methods. One has a method for collecting BAPI return messages, while the other has a method for collecting exception messages. One does not have a method to add custom text, while the other includes a method to add a message with a date/time stamp. However, not all three classes have a method for collecting batch input messages.

So who is the winner? Although none of the classes in the race really ticked all the boxes, the winner seems to be CL_RECA_MESSAGE_LIST.

Before we take a custom class to the next race, let’s make a pit stop here and take a look at a nice feature of the winner: Displaying an Application Log in a Resizable Window.

See you in the next chapter.

↑ Top

More Resources

  • For more resources on ABAP development, you can visit the ABAP Development topic page here, ask a question, or follow the ABAP Development blog here.
  • Please follow my profile for future posts and feel free to share your feedback or thoughts in a comment.

  • ¹ Drag racing is a type of motorsport that involves vehicles competing to see who can accelerate faster over a short distance, typically a quarter-mile (402 meters) or an eighth-mile (201 meters) straight track.


  • SAP®, ABAP® are the trademarks or registered trademarks of SAP SE or its affiliates in Germany and in other countries.


  • All source code available in this blog post is provided “as is” without warranty of any kind, and any use of such source code is at your own risk.


Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Thales Batista
      Thales Batista

      Even SAP Developers write they own classes instead of joining efforts to create single and powerful class-API to everyone use... We just do it again justifying with the same two lame excuses: "we need this feature set but no class provide them all", and "if I use class X and need something later that it doesn't provide, I'll face the moral dillema of either mix Class-API with FM-API or revoke my pride of fitting to standard and code the class I told myself I wouldn't create". It's the vicious cycle of missing documentation for (OO) API feeded with the closed, self-contained nature of ABAP development. dotapbap help us to at least not reinvent the wheel every time factory provided a car without them (and licensing complies with company policy).

      I have a lighter to spark a discussion, altough it is mostly philosophical and probably should be something to coffee corner: what classifies a winner API to you?

      IMO would be one that uses an interface modelling log operations, because if for some reason I need to store log data somewhere else than SLG log (like a file, remote system), I wouldn't spend too much time adapting application code. CL_SBAL_LOGGER would be the winner because it implements an interface with a very tempting name "IF_LOGGER". Of course you can wrap the other ones into a adapter class. CL_RECA_MESSAGE_LIST also implements a "log-like" interface from what I recall, but there was something inside that class that drove me away from it, I don't have a system with it available, can't fact check again.

      But then we have another thing that would be valuable: a API that can keep track from message origin. SLG log allows that, but I rarely see some development actually using it (even standard ones), so when the issue arises and Developer was not kind enough to "MESSAGE i234(msgid)" either to dummy variable or inside a "IF 1 = 0", our only real chance to find its origin quickly is through debugging by message ID/Number. For a better chance to have message tracked by static checks (where-usage), the winner would be CL_LOG_PPF since message data is transferred by SY fields (still prone to fail, since someone may consider that manually filling SY fields is quicker than "MESSAGE...INTO" statement).

      For a true OO API, CL_BAL_LOGGER will never have a chance, but its replacement CL_BAL_LOGOBJ is a strong candidate (they are in same Package, but I forgot to check its Package Interface for objects declared obsolete).

      (Let the game begin)

      Author's profile photo Serkan Saglam
      Serkan Saglam
      Blog Post Author

      Thank you very much for your valuable comments.

      Some developers do not use the OOP paradigm. Or they use classes instead of functions because it's convenient. Also, there may be a lack of guidance from senior consultants to juniors in projects, which can lead to juniors (well, not just juniors) creating duplicate objects for the same purpose. Especially if there is no self or team control over the development. At the end of the day, we realize that there are a few custom classes (babies) that require maintenance (milk).

      In fact, this blog is for beginners. In the last chapter, I will try to provide an example of a custom class that has been kept as simple as possible in terms of methods and code.

      If you have more ideas about the ideal API, please share your comments.

      Author's profile photo Thales Batista
      Thales Batista

      ABAP is a outlier of its own on programming world, the language does not evolve itself (obsolete stuff is never deleted), the average associate/seniors doesn't see any reason to learn new stuff, which also reflect on seeing to this day training materials teaching internal table with header line. If they don't bother with syntax, they are also doesn't bother to drop procedural programming and embrace OO.

      The only real driving force I saw for them learn new syntax was when recruiters started to ask 7.40+ for job application (but still, requiring it on application doesn't mean the actual job enforces it). This discussion thread is also a good topic for coffee corner (or other shady group).


      Regarding the ideal API, for me is the one that:

      • Have only a single productive API for that product: fragmentation creates redundancy and confusion, like we have for those standard classes, each one with pros/cons; it would be better if SAP provided a released-for-customer Class API for it (well, they did for BTP, probably downportable but I don't think they will ever do).
      • Provides a way to stub/mock it or switch off for testing purposes.
      Author's profile photo Alexander Jäger
      Alexander Jäger

      Maybe when talking about custom implementations, you might consider watching this really powerful implementation: SchwarzIT/sap-usi-logging-api: An easy-to-use, object-oriented encapsulation around the SAP application log (Transaction SLG1) (

      I am pretty sure it contains all the functions someone would need - and even more. Would be nice if you could cover that one as well.

      Author's profile photo Suhas Saha
      Suhas Saha

      I prefer CL_BAL_LOGGING because with it i can work with the interface IF_BAL_LOGGER (CL_BAL_LOGGING=>CREATE_INSTANCE). That way it is easier for me to mock the behavior of the logger in unit-tests.  

      Of course, there are some missing functions but it gets my job done.

      Thanks for highlighting the problem with ADD_DEBUG_MESSAGE. Did you consider raising a ticket with SAP?

      Author's profile photo Shai Sinai
      Shai Sinai

      A nice and detailed comparison.


      Two comments:

      1. IMO, the greatest strength of the application log is the context, which isn't used widely enough though.
      2. In our system we have "exported" the maintenance of display profiles into a maintenance view, what converts it into a customizing task instead of development task (i.e. no coding is required).



      I couldn't find any bug in the method CL_BAL_LOG_BASE->IF_BAL_LOGGER~ADD_DEBUG_MESSAG in our system.

      It might be related to note 2247878 - "SM21: More detailed error logging during syslog collection".

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Thank you for sharing!

      I don't write my own classes for this because it's already been done: see ABAP Logger. (There are few more logging projects that can be found here by searching for "log" but I think ABAP Logger is best.)

      It's been available for many years and I've used it in many previous jobs/projects. The mission statement of that project shows exactly all the things that are wrong with SAP standard on this.

      • What's the deal with the names? Especially CL_BAL_LOG / LOGGER / LOGGING. What is wrong with you, SAP developers? This is seriously sick stuff. And I have no idea what PTU or RECA even is.
      • These classes can be in some odd packages and it's not clear whether you're even supposed to use them or not. The minute you use them in custom code SAP will come up with the note saying it's not supported or something.
      • They can still require too much boilerplate code. Again - look at ABAP Logger. You pretty much instantiate it and then just use a method to log anything. This is the dream come true.
      • As correctly noted, there are always things that class just doesn't do or does it in some ugly way. I honestly have not found such things in ABAP Logger. It's perfect.

      I could go on forever here. It's bizarre that there still isn't ONE actually good, properly named standard class, released in ABAP package. Especially since ABAP Logger has been available for a long, long time. SAP could've just adopted it as standard (after some sprucing up). But that doesn't help to sell any products, so who cares, I guess, and we will continue seeing more stupid CL_ ... and more desperate ZCL... + GitHub projects.

      Thales Batista this also to add to your comment.

      Author's profile photo Thales Batista
      Thales Batista

      Upvote on "still isn't ONE actually good, properly named standard class, released in ABAP package" in loop for so many stuff.

      Well, they released for BTP. with boilerplate and all verbosity we expect (but don't want) to have: CL_BALI_LOG. A new class with a new prefix to remember for Ctrl-Space oriented programming. Will be ever downported to on-premise? Do we really want it? I'm good using 3rd party libs, but a standard solution would still be desired as a alternative when project forbids 3rd party.

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Amen to that! As a consultant, I find some clients are not very receptive to "hey, let's just install this report from GitHub". I find their fears sometimes over-exaggerated but it is their right and we have to work with it.

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Some additional reading on standard vs custom classes: Hello, SAP, Where Are My Global Classes?

      Just realized I've actually used a log class as an example of poor code organization and exposure. Nothing changed so far, from what I've seen.