This is a test message to test the length of the message box.
Login
ABAP Cloud Message Logging
Created by Software-Heroes

ABAP Cloud - Message Logging

724

How do you currently log messages across larger blocks of ABAP code, and how do you handle the different formats? Another new open source project.



In this article, we'll look at a new open source project from us in the ABAP Cloud area. We'll discuss the use and structure of the framework and how you can use it effectively.

 

Introduction

Just another ABAP Logger ... for ABAP Cloud. BAPIs usually generate messages via their RETURN parameters; newer implementations throw exceptions; messages are located in system fields. The amount of information to be logged can be diverse. Then there may also be the issue of translation, if messages are saved in the application log and later checked in another language. In such cases, more than just simple texts and strings should be saved. This is where the ABAP Message Logger, or AML for short, comes into play, and can take over these tasks for you.

 

Architecture

Let's take a look at the component's architecture and the actual idea behind it. Typically, many developments focus on storing messages and errors in the application log, but in most cases, this isn't actually needed and is more of a function of the logger. During implementation, we therefore paid attention to the following points:

  • Testable and mockable - Usable in ABAP unit tests and for all types of developments where you want to remain testable (Factory and Injector).
  • Configuration - You only have to configure the object once when creating it, and then you don't have to worry about it again during processing.
  • Creation - For creation, we offer a factory that generates new instances or a singleton with a buffer for creating and providing various objects.
  • ABAP Cloud - The new application log, application jobs, and saving via a second database connection are supported for ABAP Cloud developers.
  • RAP - Collect and bundle messages to the RAP framework Returning is also no problem.
  • Exceptions - Errors and messages are handled silently and with little effort by the framework, keeping your source code light and clean.
  • Context - Save additional data in the context? Implementation is currently still in the planning stages.

 

Usage

In this chapter, we will look at the application and effective use of the framework.

 

Creation

For creation, you have two methods of the ZCL_AML_LOG_FACTORY factory available that you can use:

  • CREATE - adopts the settings and creates a new log object that you receive directly for use.
  • GET_INSTANCE - This method returns the desired instance of a log object. The identification is used to find the instance. The settings only need to be passed for creation.

 

Let's look at a very simple example of how you can minimally create a log object. We don't pass any information and return an empty object.

DATA(new_log) = zcl_aml_log_factory=>create( ).

 

In a second example, we create an instance and pass some settings. We specify a default message class, set the default types to S (Success), and disable stacked exceptions. You can find more information about the various settings in the Git repository.

log_setting = zcl_aml_log_factory=>create( VALUE #( default_message_class = 'Z_AML'
                                                    default_message_type  = 'S'
                                                    no_stacked_exception  = abap_true ) ).

 

As a third option, we create a log using the GET_INSTANCE method. This not only returns an object, but also an entry in the buffer. Therefore, we don't store the log as an attribute in the executable class. You can basically pass the settings here as well.

log_specific = zcl_aml_log_factory=>get_instance( identification = 'SPECIFIC_INSTANCE' ).

 

Logging

Now let's get to the real point: logging. How do we get the various messages into the object, and how are they actually saved? If you want to add messages, we have numerous ADD_MESSAGE* methods at our disposal. The common methods and types should be represented so that conversion outside the class isn't necessary. Here's an example for adopting a simple message.

log_default->add_message( class  = 'Z_AML'
                          type   = 'S'
                          number = '001'
                          v1     = 'Placeholder' ).

 

In the next example, we create an exception, catch it, and pass it to the logging object. This is a simple exception. Exceptions created via PREVIOUS are generally unpacked afterwards, and all messages are transferred. 

TRY.
    RAISE EXCEPTION NEW cx_sy_itab_line_not_found( ).
    
  CATCH cx_root INTO DATA(error).
    log_setting->add_message_exception( error ).
ENDTRY.

 

In this example, we create a message using a standard MESSAGE statement and use it to populate the system fields (SY). To pass the message, we simply call the method, and the system fields are read via the XCO class.

MESSAGE e002(z_aml) WITH 'Dummy' INTO DATA(dummy_message).
log_specific->add_message_system( ).

 

Hint: In our design, we basically adhered to the two Clean ABAP methods: "Keep methods small" and "Do one thing, do it well, do it only." This means there is more than one method for each correct type.

 

Helpful Methods

In the next step, we want to check the actual log. So that we don't have to look at every message, there are various helper methods that we can use to obtain further information in the object. For example, if you want to know whether there are any messages in the log, you can use Method to get the number.

log_specific->get_number_of_messages( )

 

If we want to check for errors in the log, we can do so using the HAS_ERROR method. With the similar HAS_WARNING method, we can check whether at least warnings are included, even if these are already errors for you.

IF log_default->has_error( ).
  out->write( `There are some errors` ).
ELSE.
  out->write( `No errors found` ).
ENDIF.

 

If we have multiple logs, we can merge them with little effort. Using the MERGE_LOG method, we transfer all messages from the transferred log. However, settings are not transferred, only the buffered messages. This method is always useful when you receive logs from other processing.

log_setting->merge_log( log_default ).

 

Finally, we can also search for messages in the log. To do this, you can search for various elements such as the message class or the message number. The result will be a structure with the status and the first message found, in case you want to access the placeholders, for example.

IF log_setting->search_message( VALUE #( msgid = 'Z_AML' msgno = '001' ) )-found = abap_true.
  out->write( `Found the specific message in the log.` ).
ENDIF.

 

Processing

Now that we've created the log, filled it with messages, and validated some information, we now come to the actual processing. What can we do with the messages:

  • Status - We only care about the processing status, so we can easily do that using the helper methods.
  • Messages - We're interested in the messages and their contents, so there are numerous GET_MESSAGES* methods that we can use to consume the contents in the appropriate formats.
  • Save - If you want to save all messages in the application log, you can do so. Via settings in the application job or via a second database connection.

 

Let's take a look at a few example methods. For example, if we want to receive the messages back as a BAPI, you can call the appropriate method.

DATA(log_specific) = zcl_aml_log_factory=>get_instance( identification = instance_identification ).
out->write( log_specific->get_messages_bapi( ) ).

 

If you want to save the application log, you can use the SAVE method. The settings must be set initially using the Settings parameter. The SAVE method returns a result. There you can find out whether the messages were saved, the log handle, and any error messages if there were any problems.

DATA(result) = log_default->save( ).
out->write( result ).

 

For the ABAP RESTful Application Programming Model (RAP), there is a specific method that returns a table of RAP exceptions with the interface IF_ABAP_BEHV_MESSAGE. This allows you to pass exceptions directly to the UI via Others, for example. Here's an excerpt from an action in RAP; there you'll usually find the REPORTED parameter with the field %OTHER. If you fill this with messages, they will be returned to the UI and displayed as a message box if you return more than one message.

reported-%other = log_default->get_messages_rap( ).

 

Complete example

You can find the project in our GitHub repository. If you want to get involved or have suggestions for improvement, please open an issue. If you have any further questions about the project, please feel free to ask them here via the website. The project is completely open source and can be used freely.

Here you can find the test class if you would like to recreate the various methods.

CLASS zcl_bs_demo_aml_logging DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    CONSTANTS instance_identification TYPE zcl_aml_log_factory=>identification VALUE 'SPECIFIC_INSTANCE'.

    DATA log_default TYPE REF TO zif_aml_log.
    DATA log_setting TYPE REF TO zif_aml_log.

    METHODS create_log_object.
    METHODS create_messages.

    METHODS check_content
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS work_with_content
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.


CLASS zcl_bs_demo_aml_logging IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    create_log_object( ).
    create_messages( ).
    check_content( out ).
    work_with_content( out ).
  ENDMETHOD.


  METHOD create_log_object.
    log_default = zcl_aml_log_factory=>create( ).

    log_setting = zcl_aml_log_factory=>create( VALUE #( default_message_class = 'Z_AML'
                                                        default_message_type  = 'S'
                                                        no_stacked_exception  = abap_true ) ).

    DATA(log_specific) = zcl_aml_log_factory=>get_instance( identification = instance_identification ).
    log_specific->add_message_text( |This is a specific log: { instance_identification }| ).
  ENDMETHOD.


  METHOD create_messages.
    log_default->add_message( class  = 'Z_AML'
                              type   = 'S'
                              number = '001'
                              v1     = 'Placeholder' ).

    TRY.
        RAISE EXCEPTION NEW cx_sy_itab_line_not_found( ).

      CATCH cx_root INTO DATA(error).
        log_setting->add_message_exception( error ).
    ENDTRY.

    DATA(log_specific) = zcl_aml_log_factory=>get_instance( identification = instance_identification ).

    MESSAGE e002(z_aml) WITH 'Dummy' INTO DATA(dummy_message) ##NEEDED.
    log_specific->add_message_system( ).
  ENDMETHOD.


  METHOD check_content.
    DATA(log_specific) = zcl_aml_log_factory=>get_instance( identification = instance_identification ).
    out->write( log_specific->get_number_of_messages( ) ).

    IF log_default->has_error( ).
      out->write( `There are some errors` ).
    ELSE.
      out->write( `No errors found` ).
    ENDIF.

    log_setting->merge_log( log_default ).

    IF log_setting->search_message( VALUE #( msgid = 'Z_AML'
                                             msgno = '001' ) )-found = abap_true.
      out->write( `Found the specific message in the log.` ).
    ENDIF.
  ENDMETHOD.


  METHOD work_with_content.
    out->write( log_setting->get_messages_flat( ) ).

    DATA(log_specific) = zcl_aml_log_factory=>get_instance( identification = instance_identification ).
    out->write( log_specific->get_messages_bapi( ) ).

    DATA(result) = log_default->save( ).
    out->write( result ).
  ENDMETHOD.
ENDCLASS.

 

Conclusion

Handling messages and errors in ABAP code is always a personal matter, and many developers have their own logic here. However, the worst decision is still to have a separate logic for each development.


Included topics:
ABAP CloudABAPLoggingOpen Source
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Tuesday and Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP Cloud - Prepare date

Category - ABAP

How can you actually get a date in ABAP Cloud into the appropriate output format? Let's look at an example in the system.

08/29/2025

ABAP Cloud - Clean Core Level Concept

Category - ABAP

The new Clean Core Level Concept for ABAP Cloud is here, replacing the 3-TIER model. In this article, we take a detailed look at the changes and adjustments.

08/15/2025

ABAP Cloud - Test Data Container

Category - ABAP

Test data containers aren't released for ABAP Cloud, so how can generic test data be accessed? Here's a suggested simple framework.

07/08/2025

ABAP Cloud ... without BTP?

Category - ABAP

In this article, we explore the question of whether ABAP Cloud is possible without BTP and what you should consider.

06/24/2025

ABAP Cloud - Skills for the Start

Category - ABAP

When can you actually start with modern ABAP development, and what skills do you need at a minimum? Let's clarify this question together.

06/17/2025