EDUCAÇÃO E TECNOLOGIA

Business Application Log (BAL) – Message Statistics

Do you have batch jobs or online applications that generate massive amounts of messages in the application log?

Do you wish there was a way to just get an overview of what messages were issued so that you can focus on the most important ones?

Do you want to compare two application logs to see what the differences are?

Let’s see, how we can do this:

Option A:

If you are on an SAP S/4 HANA on premise 2021 release, you can use the report RPMMO_ANALYZE_BAL.

Before we talk about the report, we will take a look at the application log via transaction SLG1:

SLG1 Log Display

You can see that log number 00000000000006208808 has 298 messages in it and the detail list at the bottom is getting big.

Now we go to the analysis report to condense this a little:

Initial Screen of Report RPMMO_ANALYZE_BAL

You can select by the user who created the log, the application log object ID, the creation date or the log number. We will use the log number.

Result%20of%20Log%20Analysis

Result of Log Analysis

We see that 268 out of 298 error messages were due to one particular message. Our code analysis should probably concentrate on that message first (if we want to eliminate or reduce it).

Now we compare two application logs to see the results of different program runs (assuming a batch job):

Initial%20Screen%20with%20Compare%20Option

Initial Screen with Compare Option

Comparison%20Output

Comparison Output

We did something right? The second application run produced fewer messages …

Option B:

You can condense the message display in the application log itself, if you know how to do it.
Unfortunately, a log comparison is not possible.

Change the ALV Layout in the SLG1 Transaction

Select the fields shown above and arrange them in the following order:

Column%20Arrangement%20and%20Summation

Column Arrangement and Summation

Make sure that the column ‘Number’ has summation turned on.

Save the layout.

Layout%20Save%20Dialog

Layout Save Dialog

Now we need to subtotal:

Subtotal
Subtotals

Subtotal%20Dialog

Subtotal Dialog

List%20with%20Subtotals

List with Subtotals

We are almost there. As a last step, we need to manually collapse all the details. This is a little bit cumbersome but I couldn’t find another way to do it. Make sure you do this process with a log that doesn’t have too many entries in it. Fortunately, this has to be done only once.

Collapse%20Selections

Collapse Selections

All%20Sections%20Collapsed

All Sections Collapsed

This looks similar to what the report produces, except that you don’t see the text for the message.
That you will see, when you open up a collapsed section again.

Don’t forget to save the layout in this state. The system will remember that all the sections are collapsed and the next time you apply this layout on a log, it will start out in collapsed form.

If you like the report but are not on the right release yet, here is a scaled down Z version for your use. It probably needs at least a 7.45 basis release, otherwise you might get syntax errors.
The syntax errors should not be too difficult to fix on lower level releases, but no guarantees.

REPORT zema_analyze_bal. TABLES sscrfields.
TABLES balhdr. CLASS lcl_bal_analyze DEFINITION DEFERRED. DATA go_bal_analyze TYPE REF TO lcl_bal_analyze. SELECTION-SCREEN BEGIN OF BLOCK radio WITH FRAME TITLE TEXT-rad. PARAMETERS p_eval TYPE xfeld RADIOBUTTON GROUP r1 USER-COMMAND dummy. PARAMETERS p_comp TYPE xfeld RADIOBUTTON GROUP r1.
SELECTION-SCREEN END OF BLOCK radio. SELECTION-SCREEN BEGIN OF BLOCK sel WITH FRAME TITLE TEXT-sel. SELECT-OPTIONS so_uname FOR balhdr-aluser MODIF ID sel. SELECT-OPTIONS so_obj FOR balhdr-object MODIF ID sel. SELECT-OPTIONS so_date FOR balhdr-aldate MODIF ID sel. SELECT-OPTIONS so_log FOR balhdr-lognumber MODIF ID sel. SELECT-OPTIONS so_lghdl FOR balhdr-log_handle NO-DISPLAY.
SELECTION-SCREEN END OF BLOCK sel. SELECTION-SCREEN BEGIN OF BLOCK comp WITH FRAME TITLE TEXT-cmp. PARAMETERS p_log_a TYPE balhdr-lognumber MODIF ID cmp. PARAMETERS p_log_b TYPE balhdr-lognumber MODIF ID cmp. PARAMETERS p_diff TYPE xfeld AS CHECKBOX MODIF ID cmp.
SELECTION-SCREEN END OF BLOCK comp. PARAMETERS p_layo TYPE xfeld NO-DISPLAY. CLASS lcl_bal_analyze DEFINITION CREATE PUBLIC. PUBLIC SECTION. METHODS main. PROTECTED SECTION. TYPES BEGIN OF ty_bal_analyze. TYPES msgty TYPE syst_msgty. TYPES msgid TYPE syst_msgid. TYPES msgno TYPE syst_msgno. TYPES msgtxt TYPE natxt. TYPES msg_count TYPE balcntcum. TYPES cell_color TYPE lvc_t_scol. TYPES END OF ty_bal_analyze. TYPES BEGIN OF ty_bal_compare. TYPES msgty TYPE syst_msgty. TYPES msgid TYPE syst_msgid. TYPES msgno TYPE syst_msgno. TYPES msgtxt TYPE natxt. TYPES msg_count TYPE balcntcum. TYPES cell_color TYPE lvc_t_scol. TYPES msg_count_log_a TYPE balcntall. TYPES msg_count_log_b TYPE balcntall. TYPES delta TYPE balcntall. TYPES END OF ty_bal_compare. TYPES ty_log_rg_tab TYPE RANGE OF balognr. TYPES ty_message_tab TYPE STANDARD TABLE OF bal_s_msg WITH EMPTY KEY. TYPES ty_msg_count_tab TYPE SORTED TABLE OF ty_bal_analyze WITH NON-UNIQUE KEY msgty msgid msgno. DATA gt_msg_count_ana TYPE STANDARD TABLE OF ty_bal_analyze. DATA gt_msg_count_cmp TYPE STANDARD TABLE OF ty_bal_compare. DATA gv_deltas_exist TYPE xfeld. METHODS select_bal_header IMPORTING it_log_rg TYPE ty_log_rg_tab RETURNING VALUE(rt_logheader) TYPE bal_t_logh RAISING cx_t100_msg. METHODS compare_messages RAISING cx_t100_msg. METHODS get_messages IMPORTING it_logheader TYPE bal_t_logh RETURNING VALUE(rt_messages) TYPE ty_message_tab. METHODS eval_messages IMPORTING it_messages TYPE ty_message_tab. METHODS authority_check RAISING cx_t100_msg. METHODS get_message_count IMPORTING it_messages TYPE ty_message_tab RETURNING VALUE(rt_msg_count) TYPE ty_msg_count_tab. METHODS get_message_text IMPORTING is_msg_count TYPE ty_bal_analyze RETURNING VALUE(rv_text) TYPE t100-text. METHODS display_alv_count. METHODS display_alv_comp. METHODS build_fcat IMPORTING iv_tabname TYPE tabname RETURNING VALUE(rt_fcat) TYPE lvc_t_fcat. METHODS set_color_code IMPORTING iv_msgty TYPE syst_msgty RETURNING VALUE(rv_color) TYPE char1. METHODS set_cell_color IMPORTING iv_msgty TYPE syst_msgty RETURNING VALUE(rt_cell_color) TYPE lvc_t_scol. METHODS set_color_intensity IMPORTING iv_msgty TYPE syst_msgty RETURNING VALUE(rv_intensity) TYPE char1. METHODS build_sort_cat RETURNING VALUE(rt_sort) TYPE lvc_t_sort. ENDCLASS. CLASS lcl_bal_analyze IMPLEMENTATION. METHOD main. TRY. authority_check( ). IF p_comp = abap_false. DATA(lt_logheader) = select_bal_header( so_log[] ). DATA(lt_msg) = get_messages( lt_logheader ). ELSE. compare_messages( ). RETURN. ENDIF. CATCH cx_t100_msg. RETURN. ENDTRY. eval_messages( lt_msg ). ENDMETHOD. METHOD select_bal_header. FREE MEMORY ID sy-repid. SELECT FROM balhdr FIELDS log_handle WHERE aluser IN @so_uname AND aldate IN @so_date AND object IN @so_obj AND lognumber IN @it_log_rg AND log_handle IN @so_lghdl INTO TABLE @rt_logheader. CHECK sy-subrc IS NOT INITIAL. EXPORT return_code FROM sy-subrc TO MEMORY ID sy-repid. MESSAGE |No messages found that could be analyzed for the statistics count| type 'S' DISPLAY LIKE 'E'. RAISE EXCEPTION NEW cx_t100_msg( ). ENDMETHOD. METHOD get_messages. DATA lt_msg_hdl TYPE bal_t_msgh. DATA ls_msg TYPE bal_s_msg . CALL FUNCTION 'BAL_DB_LOAD' EXPORTING i_t_log_handle = it_logheader IMPORTING e_t_msg_handle = lt_msg_hdl EXCEPTIONS no_logs_specified = 1 log_not_found = 2 log_already_loaded = 3 OTHERS = 4 ##FM_SUBRC_OK. LOOP AT lt_msg_hdl INTO DATA(ls_msg_hdl). CALL FUNCTION 'BAL_LOG_MSG_READ' EXPORTING i_s_msg_handle = ls_msg_hdl IMPORTING e_s_msg = ls_msg EXCEPTIONS log_not_found = 1 msg_not_found = 2 OTHERS = 3 ##FM_SUBRC_OK. CHECK sy-subrc IS INITIAL. APPEND ls_msg TO rt_messages. ENDLOOP. ENDMETHOD. METHOD eval_messages. DATA lt_msg_count TYPE ty_msg_count_tab. lt_msg_count = get_message_count( it_messages ). LOOP AT lt_msg_count INTO DATA(ls_msg_count). ls_msg_count-msgtxt = get_message_text( ls_msg_count ). ls_msg_count-cell_color = set_cell_color( ls_msg_count-msgty ). APPEND ls_msg_count TO gt_msg_count_ana. ENDLOOP. display_alv_count( ). ENDMETHOD. METHOD set_color_intensity. rv_intensity = SWITCH #( iv_msgty WHEN 'S' THEN '0' ELSE '1' ). ENDMETHOD. METHOD set_color_code. rv_color = SWITCH #( iv_msgty WHEN 'A' THEN col_group WHEN 'E' THEN col_negative WHEN 'W' THEN col_total ELSE col_positive ). ENDMETHOD. METHOD get_message_text. SELECT SINGLE FROM t100 FIELDS text WHERE sprsl = @sy-langu AND arbgb = @is_msg_count-msgid AND msgnr = @is_msg_count-msgno INTO @rv_text . ENDMETHOD. METHOD get_message_count. SELECT FROM @it_messages AS msg FIELDS msgty, msgid, msgno, COUNT( * ) AS msg_count GROUP BY msgty, msgid, msgno ORDER BY msgty, msgid, msgno INTO CORRESPONDING FIELDS OF TABLE @rt_msg_count. ENDMETHOD. METHOD authority_check. AUTHORITY-CHECK OBJECT 'S_APPL_LOG' ID 'ALG_OBJECT' FIELD '*' ID 'ALG_SUBOBJ' FIELD '*' ID 'ACTVT' FIELD '03'. IF sy-subrc <> 0. MESSAGE s034(bl). RAISE EXCEPTION NEW cx_t100_msg( ). ENDIF. ENDMETHOD. METHOD compare_messages. CONSTANTS lc_subrc_99 TYPE sy-subrc VALUE '99'. DATA ls_msg_count_cmp LIKE LINE OF gt_msg_count_cmp. CLEAR: so_uname[], so_date[], so_obj[], so_lghdl[]. DATA(lt_log_header_a) = select_bal_header( it_log_rg = VALUE #( ( sign = 'I' option = 'EQ' low = p_log_a ) ) ). DATA(lt_log_header_b) = select_bal_header( it_log_rg = VALUE #( ( sign = 'I' option = 'EQ' low = p_log_b ) ) ). DATA(lt_msg_a) = get_messages( lt_log_header_a ). DATA(lt_msg_b) = get_messages( lt_log_header_b ). DATA(lt_msg_count_a) = get_message_count( lt_msg_a ). DATA(lt_msg_count_b) = get_message_count( lt_msg_b ). FREE: lt_log_header_a, lt_log_header_b, lt_msg_a, lt_msg_b. * Compare messages in log A with log B LOOP AT lt_msg_count_a INTO DATA(ls_msg_count_a). READ TABLE lt_msg_count_b WITH TABLE KEY msgty = ls_msg_count_a-msgty msgid = ls_msg_count_a-msgid msgno = ls_msg_count_a-msgno INTO DATA(ls_msg_count_b). IF sy-subrc IS NOT INITIAL. CLEAR ls_msg_count_b. ENDIF. ls_msg_count_cmp = CORRESPONDING #( ls_msg_count_a ). ls_msg_count_cmp-msg_count_log_a = ls_msg_count_a-msg_count. ls_msg_count_cmp-msg_count_log_b = ls_msg_count_b-msg_count. ls_msg_count_cmp-delta = ls_msg_count_a-msg_count - ls_msg_count_b-msg_count. ls_msg_count_cmp-msgtxt = get_message_text( ls_msg_count_a ). ls_msg_count_cmp-cell_color = set_cell_color( ls_msg_count_a-msgty ). IF p_diff = abap_true. CHECK ls_msg_count_cmp-delta IS NOT INITIAL. ENDIF. IF ls_msg_count_cmp-delta IS NOT INITIAL. gv_deltas_exist = abap_true. ENDIF. APPEND ls_msg_count_cmp TO gt_msg_count_cmp. ENDLOOP. * Compare messages in log B with log A LOOP AT lt_msg_count_b INTO ls_msg_count_b. READ TABLE lt_msg_count_a WITH TABLE KEY msgty = ls_msg_count_b-msgty msgid = ls_msg_count_b-msgid msgno = ls_msg_count_b-msgno TRANSPORTING NO FIELDS. CHECK sy-subrc IS NOT INITIAL. "Entry only in log B but not in log A ls_msg_count_cmp = CORRESPONDING #( ls_msg_count_b ). ls_msg_count_cmp-msg_count_log_b = ls_msg_count_b-msg_count. ls_msg_count_cmp-delta = ls_msg_count_b-msg_count * -1. ls_msg_count_cmp-msgtxt = get_message_text( ls_msg_count_b ). ls_msg_count_cmp-cell_color = set_cell_color( ls_msg_count_b-msgty ). APPEND ls_msg_count_cmp TO gt_msg_count_cmp. ENDLOOP. FREE: lt_msg_count_a, lt_msg_count_b. IF gt_msg_count_cmp IS INITIAL AND p_diff = abap_true. EXPORT return_code FROM lc_subrc_99 TO MEMORY ID sy-repid. MESSAGE |No messages with differences found| TYPE 'S' DISPLAY LIKE 'E'. RAISE EXCEPTION NEW cx_t100_msg( ). ENDIF. display_alv_comp( ). ENDMETHOD. METHOD display_alv_comp. DATA(lt_fcat) = build_fcat( 'GT_MSG_COUNT_CMP' ). DATA(lt_sort) = build_sort_cat( ). CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING it_sort_lvc = lt_sort is_layout_lvc = VALUE lvc_s_layo( ctab_fname = 'CELL_COLOR' ) it_fieldcat_lvc = lt_fcat i_save = 'A' is_variant = VALUE disvariant( report = sy-repid handle = 'COMP' ) TABLES t_outtab = gt_msg_count_cmp EXCEPTIONS program_error = 1 OTHERS = 2 ##FM_SUBRC_OK. ENDMETHOD. METHOD build_fcat. CASE iv_tabname. WHEN 'GT_MSG_COUNT_ANA'. rt_fcat = VALUE #( ( fieldname = 'MSGTY' datatype = 'CHAR' outputlen = 3 reptext = 'Message Type' key = abap_true ) ( fieldname = 'MSGID' datatype = 'CHAR' outputlen = 20 reptext = 'Message ID' key = abap_true ) ( fieldname = 'MSGNO' datatype = 'NUMC' outputlen = 3 reptext = 'Message Number' key = abap_true ) ( fieldname = 'MSGTXT' datatype = 'CHAR' outputlen = 60 reptext = 'Message Text' ) ( fieldname = 'MSG_COUNT' datatype = 'INT4' outputlen = 10 reptext = 'Number' do_sum = abap_true ) ). WHEN OTHERS. rt_fcat = VALUE #( ( fieldname = 'MSGTY' datatype = 'CHAR' outputlen = 3 reptext = 'Message Type' key = abap_true ) ( fieldname = 'MSGID' datatype = 'CHAR' outputlen = 20 reptext = 'Message ID' key = abap_true ) ( fieldname = 'MSGNO' datatype = 'NUMC' outputlen = 3 reptext = 'Message Number' key = abap_true ) ( fieldname = 'MSGTXT' datatype = 'CHAR' outputlen = 60 reptext = 'Message Text' ) ( fieldname = 'MSG_COUNT' datatype = 'INT4' outputlen = 10 reptext = 'Number' do_sum = abap_true no_out = abap_true ) ( fieldname = 'MSG_COUNT_LOG_A' datatype = 'INT4' outputlen = 10 reptext = 'Msg Cnt Log A' do_sum = abap_true ) ( fieldname = 'MSG_COUNT_LOG_B' datatype = 'INT4' outputlen = 10 reptext = 'Msg Cnt Log B' do_sum = abap_true ) ( fieldname = 'DELTA' datatype = 'INT4' outputlen = 10 reptext = 'Delta A - B' do_sum = abap_true ) ). ENDCASE. ENDMETHOD. METHOD display_alv_count. DATA(lt_fcat) = build_fcat( 'GT_MSG_COUNT_ANA' ). DATA(lt_sort) = build_sort_cat( ). CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING i_callback_program = sy-repid it_sort_lvc = lt_sort is_layout_lvc = VALUE lvc_s_layo( ctab_fname = 'CELL_COLOR' ) it_fieldcat_lvc = lt_fcat i_save = 'A' is_variant = VALUE disvariant( report = sy-repid handle = 'CNT' ) TABLES t_outtab = gt_msg_count_ana EXCEPTIONS program_error = 1 OTHERS = 2 ##FM_SUBRC_OK. ENDMETHOD. METHOD build_sort_cat. rt_sort = VALUE lvc_t_sort( ( spos = '1' fieldname = 'MSGTY' up = abap_true subtot = abap_true ) ( spos = '2' fieldname = 'MSGID' up = abap_true ) ( spos = '3' fieldname = 'MSGNO' up = abap_true ) ). ENDMETHOD. METHOD set_cell_color. DATA(lv_color) = set_color_code( iv_msgty ). DATA(lv_int) = set_color_intensity( iv_msgty ). rt_cell_color = VALUE #( color-col = lv_color color-int = lv_int ( fname = 'MSGID' ) ( fname = 'MSGTY' ) ( fname = 'MSGNO' ) ). ENDMETHOD. ENDCLASS. AT SELECTION-SCREEN OUTPUT. LOOP AT SCREEN. IF p_eval = abap_true OR ( p_eval IS INITIAL AND p_comp IS INITIAL ). CHECK screen-group1 = 'CMP'. ELSE. CHECK screen-group1 = 'SEL'. ENDIF. screen-active = '0'. MODIFY SCREEN. ENDLOOP. START-OF-SELECTION. go_bal_analyze = NEW lcl_bal_analyze( ). go_bal_analyze->main( ).

And here are the Text Elements (Selection Texts):

P_COMP Compare 2 Application Logs
P_DIFF Only Messages with Differences
P_EVAL Evaluate Application Logs
P_LOG_A Log number A
P_LOG_B Log number B
SO_DATE Date
SO_LOG Log Number
SO_OBJ Object
SO_UNAME User

Text%20Elements%20-%20Selection%20Texts

Text Elements – Selection Texts

I hope you like this functionality and that it will help you in your journey to resolve bugs quickly and efficiently.

In the Project Manufacturing Management and Optimization Application this functionality is integrated into the delivered application logs (transactions PMMO_DISLOG, PMMO_MIGLOG and PMMO_PEGLOG). Unfortunately it was not possible to deliver it as a standard for all application logs.

Application%20Log%20in%20PMMO

Application Log in PMMO

The layout we built above is delivered as 1SAP_MSG_COUNT.