ABAP Application Logging: BAL, CL_BAL_LOG, SLG1

Category
ABAP-Statements
Published
Author
Johannes

The Application Log (BAL) is the standard framework for persistent logging in SAP. Logs are stored in the database and can be displayed via transaction SLG1.

Basic Concept

ElementDescription
Log ObjectGrouping (e.g., application) - maintain in SLG0
Sub-ObjectSubcategory of the Log Object
Log HandleUnique ID of a log instance
MessageIndividual log message
SLG1Transaction for log display

Message Types

TypeMeaningIcon
AAbortRed
EErrorRed
WWarningYellow
IInformationBlue
SSuccessGreen

Examples

1. Create a Simple Log

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log.
" Log header
ls_log-object = 'ZTEST'. " Log object (from SLG0)
ls_log-subobject = 'PROCESS'. " Sub-object
ls_log-aldate = sy-datum.
ls_log-altime = sy-uzeit.
ls_log-aluser = sy-uname.
ls_log-alprog = sy-repid.
ls_log-extnumber = 'My Processing 001'.
" Create log
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING
i_s_log = ls_log
IMPORTING
e_log_handle = lv_log_handle
EXCEPTIONS
log_header_inconsistent = 1
OTHERS = 2.
IF sy-subrc <> 0.
RETURN.
ENDIF.
" Add message
DATA: ls_msg TYPE bal_s_msg.
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZMYAPP'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = 'Processing started'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg
EXCEPTIONS
log_not_found = 1
msg_inconsistent = 2
OTHERS = 3.
" Additional messages
ls_msg-msgty = 'I'.
ls_msg-msgv1 = '100 records processed'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING i_log_handle = lv_log_handle i_s_msg = ls_msg.
ls_msg-msgty = 'S'.
ls_msg-msgv1 = 'Processing completed'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING i_log_handle = lv_log_handle i_s_msg = ls_msg.
" Save log
CALL FUNCTION 'BAL_DB_SAVE'
EXCEPTIONS
log_not_found = 1
save_not_allowed = 2
numbering_error = 3
OTHERS = 4.
IF sy-subrc = 0.
COMMIT WORK.
WRITE: / 'Log saved'.
ENDIF.

2. OO Approach with CL_BAL_LOG

CLASS zcl_application_log DEFINITION.
PUBLIC SECTION.
METHODS: constructor
IMPORTING iv_object TYPE balobj_d
iv_subobject TYPE balsubobj
iv_extnumber TYPE balnrext OPTIONAL.
METHODS: add_success
IMPORTING iv_message TYPE string.
METHODS: add_error
IMPORTING iv_message TYPE string.
METHODS: add_warning
IMPORTING iv_message TYPE string.
METHODS: add_info
IMPORTING iv_message TYPE string.
METHODS: add_exception
IMPORTING ix_error TYPE REF TO cx_root.
METHODS: save
RETURNING VALUE(rv_success) TYPE abap_bool.
METHODS: display.
METHODS: get_messages
RETURNING VALUE(rt_messages) TYPE bapiret2_t.
PRIVATE SECTION.
DATA: mv_log_handle TYPE balloghndl.
METHODS: add_message
IMPORTING iv_type TYPE sy-msgty
iv_message TYPE string.
ENDCLASS.
CLASS zcl_application_log IMPLEMENTATION.
METHOD constructor.
DATA: ls_log TYPE bal_s_log.
ls_log-object = iv_object.
ls_log-subobject = iv_subobject.
ls_log-aldate = sy-datum.
ls_log-altime = sy-uzeit.
ls_log-aluser = sy-uname.
ls_log-alprog = sy-repid.
ls_log-extnumber = iv_extnumber.
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING
i_s_log = ls_log
IMPORTING
e_log_handle = mv_log_handle
EXCEPTIONS
OTHERS = 1.
ENDMETHOD.
METHOD add_message.
DATA: ls_msg TYPE bal_s_msg.
" Free message
ls_msg-msgty = iv_type.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = iv_message(50).
IF strlen( iv_message ) > 50.
ls_msg-msgv2 = iv_message+50(50).
ENDIF.
IF strlen( iv_message ) > 100.
ls_msg-msgv3 = iv_message+100(50).
ENDIF.
IF strlen( iv_message ) > 150.
ls_msg-msgv4 = iv_message+150(50).
ENDIF.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = mv_log_handle
i_s_msg = ls_msg
EXCEPTIONS
OTHERS = 1.
ENDMETHOD.
METHOD add_success.
add_message( iv_type = 'S' iv_message = iv_message ).
ENDMETHOD.
METHOD add_error.
add_message( iv_type = 'E' iv_message = iv_message ).
ENDMETHOD.
METHOD add_warning.
add_message( iv_type = 'W' iv_message = iv_message ).
ENDMETHOD.
METHOD add_info.
add_message( iv_type = 'I' iv_message = iv_message ).
ENDMETHOD.
METHOD add_exception.
DATA: ls_msg TYPE bal_s_msg.
ls_msg-msgty = 'E'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = ix_error->get_text( )(50).
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = mv_log_handle
i_s_msg = ls_msg
EXCEPTIONS
OTHERS = 1.
ENDMETHOD.
METHOD save.
DATA: lt_log_handles TYPE bal_t_logh.
APPEND mv_log_handle TO lt_log_handles.
CALL FUNCTION 'BAL_DB_SAVE'
EXPORTING
i_t_log_handle = lt_log_handles
EXCEPTIONS
log_not_found = 1
save_not_allowed = 2
numbering_error = 3
OTHERS = 4.
rv_success = xsdbool( sy-subrc = 0 ).
IF rv_success = abap_true.
COMMIT WORK.
ENDIF.
ENDMETHOD.
METHOD display.
DATA: lt_log_handles TYPE bal_t_logh,
ls_profile TYPE bal_s_prof.
APPEND mv_log_handle TO lt_log_handles.
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
IMPORTING
e_s_display_profile = ls_profile.
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_t_log_handle = lt_log_handles
i_s_display_profile = ls_profile
EXCEPTIONS
OTHERS = 1.
ENDMETHOD.
METHOD get_messages.
DATA: lt_msg TYPE bal_t_msg,
ls_msg TYPE bal_s_msg.
CALL FUNCTION 'BAL_LOG_MSG_READ'
EXPORTING
i_log_handle = mv_log_handle
IMPORTING
e_t_msg = lt_msg
EXCEPTIONS
OTHERS = 1.
LOOP AT lt_msg INTO ls_msg.
APPEND VALUE bapiret2(
type = ls_msg-msgty
id = ls_msg-msgid
number = ls_msg-msgno
message_v1 = ls_msg-msgv1
message_v2 = ls_msg-msgv2
message_v3 = ls_msg-msgv3
message_v4 = ls_msg-msgv4
) TO rt_messages.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
" Usage
DATA(lo_log) = NEW zcl_application_log(
iv_object = 'ZTEST'
iv_subobject = 'BATCH'
iv_extnumber = |Processing { sy-datum }|
).
lo_log->add_success( 'Processing started' ).
lo_log->add_info( '500 records loaded' ).
TRY.
" Processing...
lo_log->add_success( 'Processing successful' ).
CATCH cx_root INTO DATA(lx_error).
lo_log->add_exception( lx_error ).
ENDTRY.
IF lo_log->save( ).
lo_log->display( ).
ENDIF.

3. Create Log Object in SLG0

Transaction: SLG0
1. Click "New Entries"
2. Object: ZTEST
3. Object text: My Test Application
4. Save
5. Create Sub-Object:
- Sub-Object: BATCH
- Text: Batch Processing
- Sub-Object: ONLINE
- Text: Online Processing

4. Log with Context (Additional Data)

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log,
ls_msg TYPE bal_s_msg.
" Create log
ls_log-object = 'ZORDERS'.
ls_log-subobject = 'PROCESS'.
ls_log-extnumber = 'Order Processing'.
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING i_s_log = ls_log
IMPORTING e_log_handle = lv_log_handle.
" Message with context
ls_msg-msgty = 'E'.
ls_msg-msgid = 'ZORD'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = '0000001234'. " Order number
" Context structure (for navigation)
DATA: ls_context TYPE bal_s_cont.
ls_context-tabname = 'VBAK'.
ls_context-value = '0000001234'.
ls_msg-context-tabname = 'VBAK'.
ls_msg-context-value = ls_context-value.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg.

5. Read Logs from Database

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t,
lt_log_handles TYPE bal_t_logh.
" Define filter
lt_log_filter-object = VALUE #( ( sign = 'I' option = 'EQ' low = 'ZTEST' ) ).
lt_log_filter-subobject = VALUE #( ( sign = 'I' option = 'EQ' low = 'BATCH' ) ).
lt_log_filter-aldate = VALUE #( ( sign = 'I' option = 'GE' low = sy-datum - 7 ) ).
" Search logs
CALL FUNCTION 'BAL_DB_SEARCH'
EXPORTING
i_s_log_filter = lt_log_filter
IMPORTING
e_t_log_header = lt_log_header
EXCEPTIONS
log_not_found = 1
no_filter_criteria = 2
OTHERS = 3.
IF sy-subrc = 0.
WRITE: / 'Logs found:', lines( lt_log_header ).
" Load logs
CALL FUNCTION 'BAL_DB_LOAD'
EXPORTING
i_t_log_header = lt_log_header
IMPORTING
e_t_log_handle = lt_log_handles
EXCEPTIONS
OTHERS = 1.
" Display
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_t_log_handle = lt_log_handles
EXCEPTIONS
OTHERS = 1.
ENDIF.

6. Delete Log

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t.
" Find old logs (older than 30 days)
lt_log_filter-object = VALUE #( ( sign = 'I' option = 'EQ' low = 'ZTEST' ) ).
lt_log_filter-aldate = VALUE #( ( sign = 'I' option = 'LE' low = sy-datum - 30 ) ).
CALL FUNCTION 'BAL_DB_SEARCH'
EXPORTING
i_s_log_filter = lt_log_filter
IMPORTING
e_t_log_header = lt_log_header
EXCEPTIONS
OTHERS = 1.
" Delete logs
IF lt_log_header IS NOT INITIAL.
CALL FUNCTION 'BAL_DB_DELETE'
EXPORTING
i_t_logs_to_delete = lt_log_header
EXCEPTIONS
OTHERS = 1.
IF sy-subrc = 0.
COMMIT WORK.
WRITE: / 'Deleted:', lines( lt_log_header ), 'logs'.
ENDIF.
ENDIF.

7. Callback for Detail Display

DATA: ls_msg TYPE bal_s_msg,
ls_clbk TYPE bal_s_clbk.
" Define callback
ls_clbk-userexitf = 'Z_SHOW_ORDER_DETAILS'. " Function module
ls_clbk-userexitp = sy-repid. " Program
ls_clbk-userexitt = ' '.
ls_msg-msgty = 'E'.
ls_msg-msgid = 'ZORD'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = lv_vbeln.
ls_msg-params-callback = ls_clbk.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg.
" Function module for callback
FUNCTION z_show_order_details.
*" IMPORTING
*" VALUE(I_S_MSG) TYPE BAL_S_MSG
*"----------------------------------------------------------------------
" Display order
SET PARAMETER ID 'AUN' FIELD i_s_msg-msgv1.
CALL TRANSACTION 'VA03' AND SKIP FIRST SCREEN.
ENDFUNCTION.

8. Hierarchical Log

DATA: lv_log_handle TYPE balloghndl,
lv_msg_handle TYPE balmsghndl,
ls_msg TYPE bal_s_msg.
" Create main log
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING i_s_log = ls_log
IMPORTING e_log_handle = lv_log_handle.
" Parent message
ls_msg-msgty = 'I'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = 'Processing Customer 1000'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg
IMPORTING
e_msg_handle = lv_msg_handle. " Handle for parent
" Child messages
ls_msg-msgv1 = ' Address updated'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg
i_msg_handle_par = lv_msg_handle. " Parent handle
ls_msg-msgv1 = ' Contacts synchronized'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_s_msg = ls_msg
i_msg_handle_par = lv_msg_handle.

9. Log with Priority and Detail Level

DATA: ls_msg TYPE bal_s_msg.
" Detail level
ls_msg-detlevel = '1'. " 1-9, higher = more detail
ls_msg-probclass = '1'. " Problem class 1 = important, 4 = unimportant
" Filter on display
DATA: ls_profile TYPE bal_s_prof.
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
IMPORTING e_s_display_profile = ls_profile.
" Only important messages (problem class 1-2)
ls_profile-mess_filter-probclass = VALUE #(
( sign = 'I' option = 'BT' low = '1' high = '2' )
).
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_t_log_handle = lt_log_handles
i_s_display_profile = ls_profile.

10. Automatic Log on Exceptions

CLASS zcl_logged_processor DEFINITION.
PUBLIC SECTION.
METHODS: constructor
IMPORTING io_log TYPE REF TO zcl_application_log.
METHODS: process
IMPORTING it_data TYPE ty_data_tab
RAISING zcx_processing_error.
PRIVATE SECTION.
DATA: mo_log TYPE REF TO zcl_application_log.
ENDCLASS.
CLASS zcl_logged_processor IMPLEMENTATION.
METHOD constructor.
mo_log = io_log.
ENDMETHOD.
METHOD process.
mo_log->add_info( |Processing { lines( it_data ) } entries| ).
LOOP AT it_data INTO DATA(ls_data).
TRY.
" Processing
process_single( ls_data ).
mo_log->add_success( |Entry { ls_data-id } processed| ).
CATCH cx_root INTO DATA(lx_error).
mo_log->add_error( |Entry { ls_data-id }: { lx_error->get_text( ) }| ).
" Abort on critical error
IF lx_error IS INSTANCE OF zcx_critical_error.
mo_log->add_error( 'Critical error - aborting' ).
mo_log->save( ).
RAISE EXCEPTION TYPE zcx_processing_error
EXPORTING previous = lx_error.
ENDIF.
ENDTRY.
ENDLOOP.
mo_log->add_success( 'Processing completed' ).
ENDMETHOD.
ENDCLASS.

11. Mass Logging (Performance)

DATA: lt_messages TYPE bal_t_msg,
ls_msg TYPE bal_s_msg.
" Collect messages instead of adding individually
LOOP AT lt_data INTO DATA(ls_data).
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = |Record { ls_data-id } processed|.
APPEND ls_msg TO lt_messages.
ENDLOOP.
" Add all at once
CALL FUNCTION 'BAL_LOG_MSGS_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_t_msg = lt_messages
EXCEPTIONS
OTHERS = 1.
" Save once
CALL FUNCTION 'BAL_DB_SAVE'
EXPORTING
i_save_all = abap_true.

12. Use Log in Job

REPORT zlog_in_job.
DATA: go_log TYPE REF TO zcl_application_log.
START-OF-SELECTION.
" Initialize log
go_log = NEW zcl_application_log(
iv_object = 'ZJOB'
iv_subobject = 'DAILY'
iv_extnumber = |Job { sy-datum } { sy-uzeit }|
).
go_log->add_info( 'Job started' ).
TRY.
" Processing
perform_processing( ).
go_log->add_success( 'Job completed successfully' ).
CATCH cx_root INTO DATA(lx_error).
go_log->add_exception( lx_error ).
go_log->add_error( 'Job ended with error' ).
ENDTRY.
" Always save
go_log->save( ).
END-OF-SELECTION.

13. Call SLG1 Programmatically

" Log display like SLG1
DATA: ls_display_profile TYPE bal_s_prof.
" Standard profile
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
IMPORTING e_s_display_profile = ls_display_profile.
" Customize settings
ls_display_profile-title = 'My Log Display'.
ls_display_profile-tree_log = abap_true. " Tree display
" Display
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_s_display_profile = ls_display_profile
EXCEPTIONS
OTHERS = 1.

Important Function Modules

Function ModuleDescription
BAL_LOG_CREATECreate log
BAL_LOG_MSG_ADDAdd message
BAL_DB_SAVESave to DB
BAL_DB_SEARCHSearch logs
BAL_DB_LOADLoad logs
BAL_DB_DELETEDelete logs
BAL_DSP_LOG_DISPLAYDisplay logs

Important Notes / Best Practice

  • Maintain log objects in SLG0 (don’t hardcode).
  • Use external number for easy search in SLG1.
  • Don’t forget COMMIT WORK after BAL_DB_SAVE.
  • Use hierarchy for clear structuring.
  • Use context for navigation to documents.
  • Implement callbacks for detail display.
  • Use mass logging for performance with many messages.
  • Regularly clean up old logs (performance).
  • Use problem class and detail level for filtering.
  • Combine with Background Jobs for batch logging.