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:
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:
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.
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):
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.
Select the fields shown above and arrange them in the following order:
Make sure that the column ‘Number’ has summation turned on.
Save the layout.
Now we need to subtotal:
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.
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
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.
The layout we built above is delivered as 1SAP_MSG_COUNT.