ABAP Application Logging: BAL, CL_BAL_LOG, SLG1

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Das Application Log (BAL) ist das Standard-Framework für persistente Protokollierung in SAP. Logs werden in der Datenbank gespeichert und können über Transaktion SLG1 angezeigt werden.

Grundkonzept

ElementBeschreibung
Log ObjectGruppierung (z.B. Anwendung) - in SLG0 pflegen
Sub-ObjectUnterkategorie des Log Objects
Log HandleEindeutige ID einer Log-Instanz
MessageEinzelne Protokollnachricht
SLG1Transaktion zur Log-Anzeige

Nachrichtentypen

TypBedeutungIcon
AAbbruchRot
EFehlerRot
WWarnungGelb
IInformationBlau
SErfolgGrün

Beispiele

1. Einfaches Log erstellen

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log.
" Log-Header
ls_log-object = 'ZTEST'. " Log-Objekt (aus SLG0)
ls_log-subobject = 'PROCESS'. " Unter-Objekt
ls_log-aldate = sy-datum.
ls_log-altime = sy-uzeit.
ls_log-aluser = sy-uname.
ls_log-alprog = sy-repid.
ls_log-extnumber = 'Meine Verarbeitung 001'.
" Log erstellen
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.
" Nachricht hinzufügen
DATA: ls_msg TYPE bal_s_msg.
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZMYAPP'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = 'Verarbeitung gestartet'.
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.
" Weitere Nachrichten
ls_msg-msgty = 'I'.
ls_msg-msgv1 = '100 Datensätze verarbeitet'.
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 = 'Verarbeitung abgeschlossen'.
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING i_log_handle = lv_log_handle i_s_msg = ls_msg.
" Log speichern
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 gespeichert'.
ENDIF.

2. OO-Ansatz mit 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.
" Freie Nachricht
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.
" Verwendung
DATA(lo_log) = NEW zcl_application_log(
iv_object = 'ZTEST'
iv_subobject = 'BATCH'
iv_extnumber = |Verarbeitung { sy-datum }|
).
lo_log->add_success( 'Verarbeitung gestartet' ).
lo_log->add_info( '500 Datensätze geladen' ).
TRY.
" Verarbeitung...
lo_log->add_success( 'Verarbeitung erfolgreich' ).
CATCH cx_root INTO DATA(lx_error).
lo_log->add_exception( lx_error ).
ENDTRY.
IF lo_log->save( ).
lo_log->display( ).
ENDIF.

3. Log-Objekt in SLG0 erstellen

Transaktion: SLG0
1. "Neue Einträge" klicken
2. Objekt: ZTEST
3. Objekttext: Meine Testanwendung
4. Speichern
5. Unter-Objekt anlegen:
- Unter-Objekt: BATCH
- Text: Batch-Verarbeitung
- Unter-Objekt: ONLINE
- Text: Online-Verarbeitung

4. Log mit Kontext (zusätzliche Daten)

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log,
ls_msg TYPE bal_s_msg.
" Log erstellen
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.
" Nachricht mit Kontext
ls_msg-msgty = 'E'.
ls_msg-msgid = 'ZORD'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = '0000001234'. " Bestellnummer
" Kontext-Struktur (für 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. Logs aus Datenbank lesen

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t,
lt_log_handles TYPE bal_t_logh.
" Filter definieren
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 ) ).
" Logs suchen
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: / 'Gefundene Logs:', lines( lt_log_header ).
" Logs laden
CALL FUNCTION 'BAL_DB_LOAD'
EXPORTING
i_t_log_header = lt_log_header
IMPORTING
e_t_log_handle = lt_log_handles
EXCEPTIONS
OTHERS = 1.
" Anzeigen
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_t_log_handle = lt_log_handles
EXCEPTIONS
OTHERS = 1.
ENDIF.

6. Log löschen

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t.
" Alte Logs finden (älter als 30 Tage)
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.
" Logs löschen
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: / 'Gelöscht:', lines( lt_log_header ), 'Logs'.
ENDIF.
ENDIF.

7. Callback für Detailanzeige

DATA: ls_msg TYPE bal_s_msg,
ls_clbk TYPE bal_s_clbk.
" Callback definieren
ls_clbk-userexitf = 'Z_SHOW_ORDER_DETAILS'. " Funktionsbaustein
ls_clbk-userexitp = sy-repid. " Programm
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.
" Funktionsbaustein für Callback
FUNCTION z_show_order_details.
*" IMPORTING
*" VALUE(I_S_MSG) TYPE BAL_S_MSG
*"----------------------------------------------------------------------
" Bestellung anzeigen
SET PARAMETER ID 'AUN' FIELD i_s_msg-msgv1.
CALL TRANSACTION 'VA03' AND SKIP FIRST SCREEN.
ENDFUNCTION.

8. Hierarchisches Log

DATA: lv_log_handle TYPE balloghndl,
lv_msg_handle TYPE balmsghndl,
ls_msg TYPE bal_s_msg.
" Haupt-Log erstellen
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING i_s_log = ls_log
IMPORTING e_log_handle = lv_log_handle.
" Parent-Nachricht
ls_msg-msgty = 'I'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = 'Verarbeitung Kunde 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 für Parent
" Kind-Nachrichten
ls_msg-msgv1 = ' Adresse aktualisiert'.
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 = ' Kontakte synchronisiert'.
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 mit Priorität und Detailstufe

DATA: ls_msg TYPE bal_s_msg.
" Detailstufe
ls_msg-detlevel = '1'. " 1-9, höher = mehr Detail
ls_msg-probclass = '1'. " Problemklasse 1 = wichtig, 4 = unwichtig
" Bei Anzeige filtern
DATA: ls_profile TYPE bal_s_prof.
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
IMPORTING e_s_display_profile = ls_profile.
" Nur wichtige Nachrichten (Problemklasse 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. Log automatisch bei 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( |Verarbeite { lines( it_data ) } Einträge| ).
LOOP AT it_data INTO DATA(ls_data).
TRY.
" Verarbeitung
process_single( ls_data ).
mo_log->add_success( |Eintrag { ls_data-id } verarbeitet| ).
CATCH cx_root INTO DATA(lx_error).
mo_log->add_error( |Eintrag { ls_data-id }: { lx_error->get_text( ) }| ).
" Bei kritischem Fehler abbrechen
IF lx_error IS INSTANCE OF zcx_critical_error.
mo_log->add_error( 'Kritischer Fehler - Abbruch' ).
mo_log->save( ).
RAISE EXCEPTION TYPE zcx_processing_error
EXPORTING previous = lx_error.
ENDIF.
ENDTRY.
ENDLOOP.
mo_log->add_success( 'Verarbeitung abgeschlossen' ).
ENDMETHOD.
ENDCLASS.

11. Massenprotokollierung (Performance)

DATA: lt_messages TYPE bal_t_msg,
ls_msg TYPE bal_s_msg.
" Nachrichten sammeln statt einzeln hinzufügen
LOOP AT lt_data INTO DATA(ls_data).
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = |Datensatz { ls_data-id } verarbeitet|.
APPEND ls_msg TO lt_messages.
ENDLOOP.
" Alle auf einmal hinzufügen
CALL FUNCTION 'BAL_LOG_MSGS_ADD'
EXPORTING
i_log_handle = lv_log_handle
i_t_msg = lt_messages
EXCEPTIONS
OTHERS = 1.
" Einmal speichern
CALL FUNCTION 'BAL_DB_SAVE'
EXPORTING
i_save_all = abap_true.

12. Log in Job verwenden

REPORT zlog_in_job.
DATA: go_log TYPE REF TO zcl_application_log.
START-OF-SELECTION.
" Log initialisieren
go_log = NEW zcl_application_log(
iv_object = 'ZJOB'
iv_subobject = 'DAILY'
iv_extnumber = |Job { sy-datum } { sy-uzeit }|
).
go_log->add_info( 'Job gestartet' ).
TRY.
" Verarbeitung
perform_processing( ).
go_log->add_success( 'Job erfolgreich beendet' ).
CATCH cx_root INTO DATA(lx_error).
go_log->add_exception( lx_error ).
go_log->add_error( 'Job mit Fehler beendet' ).
ENDTRY.
" Immer speichern
go_log->save( ).
END-OF-SELECTION.

13. SLG1 programmatisch aufrufen

" Log-Anzeige wie SLG1
DATA: ls_display_profile TYPE bal_s_prof.
" Standard-Profil
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
IMPORTING e_s_display_profile = ls_display_profile.
" Einstellungen anpassen
ls_display_profile-title = 'Meine Log-Anzeige'.
ls_display_profile-tree_log = abap_true. " Baumdarstellung
" Anzeigen
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_s_display_profile = ls_display_profile
EXCEPTIONS
OTHERS = 1.

Wichtige Funktionsbausteine

FunktionsbausteinBeschreibung
BAL_LOG_CREATELog erstellen
BAL_LOG_MSG_ADDNachricht hinzufügen
BAL_DB_SAVEIn DB speichern
BAL_DB_SEARCHLogs suchen
BAL_DB_LOADLogs laden
BAL_DB_DELETELogs löschen
BAL_DSP_LOG_DISPLAYLogs anzeigen

Wichtige Hinweise / Best Practice

  • Log-Objekte in SLG0 pflegen (nicht hardcoden).
  • Externe Nummer für einfache Suche in SLG1.
  • COMMIT WORK nach BAL_DB_SAVE nicht vergessen.
  • Hierarchie für übersichtliche Strukturierung nutzen.
  • Kontext für Navigation zu Belegen.
  • Callbacks für Detail-Anzeige implementieren.
  • Massenprotokollierung für Performance bei vielen Nachrichten.
  • Alte Logs regelmäßig bereinigen (Performance).
  • Problemklasse und Detailstufe für Filterung nutzen.
  • Kombinieren Sie mit Background Jobs für Batch-Protokollierung.