ABAP Application Logging : BAL, CL_BAL_LOG, SLG1

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

L’Application Log (BAL) est le framework standard pour la journalisation persistante dans SAP. Les logs sont stockes dans la base de donnees et peuvent etre affiches via la transaction SLG1.

Concept de base

ElementDescription
Log ObjectRegroupement (par ex. application) - a maintenir dans SLG0
Sub-ObjectSous-categorie du Log Object
Log HandleID unique d’une instance de log
MessageMessage de journal individuel
SLG1Transaction pour l’affichage des logs

Types de messages

TypeSignificationIcone
AAbandonRouge
EErreurRouge
WAvertissementJaune
IInformationBleu
SSuccesVert

Exemples

1. Creer un log simple

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log.
" En-tete du log
ls_log-object = 'ZTEST'. " Objet de log (depuis SLG0)
ls_log-subobject = 'PROCESS'. " Sous-objet
ls_log-aldate = sy-datum.
ls_log-altime = sy-uzeit.
ls_log-aluser = sy-uname.
ls_log-alprog = sy-repid.
ls_log-extnumber = 'Mon traitement 001'.
" Creer le 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.
" Ajouter un message
DATA: ls_msg TYPE bal_s_msg.
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZMYAPP'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = 'Traitement demarre'.
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.
" Autres messages
ls_msg-msgty = 'I'.
ls_msg-msgv1 = '100 enregistrements traites'.
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 = 'Traitement termine'.
CALL FUNCTION 'BAL_LOG_MSG_ADD"
EXPORTING i_log_handle = lv_log_handle i_s_msg = ls_msg.
" Sauvegarder le 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 sauvegarde'.
ENDIF.

2. Approche OO avec 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.
" Message libre
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.
" Utilisation
DATA(lo_log) = NEW zcl_application_log(
iv_object = 'ZTEST"
iv_subobject = 'BATCH"
iv_extnumber = |Traitement { sy-datum }|
).
lo_log->add_success( 'Traitement demarre' ).
lo_log->add_info( '500 enregistrements charges' ).
TRY.
" Traitement...
lo_log->add_success( 'Traitement reussi' ).
CATCH cx_root INTO DATA(lx_error).
lo_log->add_exception( lx_error ).
ENDTRY.
IF lo_log->save( ).
lo_log->display( ).
ENDIF.

3. Creer un objet de log dans SLG0

Transaction : SLG0
1. Cliquer sur "Nouvelles entrees"
2. Objet : ZTEST
3. Texte objet : Mon application de test
4. Sauvegarder
5. Creer un sous-objet :
- Sous-objet : BATCH
- Texte : Traitement batch
- Sous-objet : ONLINE
- Texte : Traitement en ligne

4. Log avec contexte (donnees supplementaires)

DATA: lv_log_handle TYPE balloghndl,
ls_log TYPE bal_s_log,
ls_msg TYPE bal_s_msg.
" Creer le 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 avec contexte
ls_msg-msgty = 'E'.
ls_msg-msgid = 'ZORD'.
ls_msg-msgno = '001'.
ls_msg-msgv1 = '0000001234'. " Numero de commande
" Structure de contexte (pour la 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. Lire les logs depuis la base de donnees

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t,
lt_log_handles TYPE bal_t_logh.
" Definir le filtre
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 ) ).
" Rechercher les 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 trouves:', lines( lt_log_header ).
" Charger les 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.
" Afficher
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY"
EXPORTING
i_t_log_handle = lt_log_handles
EXCEPTIONS
OTHERS = 1.
ENDIF.

6. Supprimer un log

DATA: lt_log_filter TYPE bal_s_lfil,
lt_log_header TYPE balhdr_t.
" Trouver les anciens logs (plus de 30 jours)
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.
" Supprimer les 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: / 'Supprimes:', lines( lt_log_header ), 'logs'.
ENDIF.
ENDIF.

7. Callback pour l’affichage des details

DATA: ls_msg TYPE bal_s_msg,
ls_clbk TYPE bal_s_clbk.
" Definir le callback
ls_clbk-userexitf = 'Z_SHOW_ORDER_DETAILS'. " Module fonction
ls_clbk-userexitp = sy-repid. " Programme
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.
" Module fonction pour le callback
FUNCTION z_show_order_details.
*" IMPORTING
*" VALUE(I_S_MSG) TYPE BAL_S_MSG
*"----------------------------------------------------------------------
" Afficher la commande
SET PARAMETER ID 'AUN' FIELD i_s_msg-msgv1.
CALL TRANSACTION 'VA03' AND SKIP FIRST SCREEN.
ENDFUNCTION.

8. Log hierarchique

DATA: lv_log_handle TYPE balloghndl,
lv_msg_handle TYPE balmsghndl,
ls_msg TYPE bal_s_msg.
" Creer le log principal
CALL FUNCTION 'BAL_LOG_CREATE"
EXPORTING i_s_log = ls_log
IMPORTING e_log_handle = lv_log_handle.
" Message parent
ls_msg-msgty = 'I'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = 'Traitement Client 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 pour le parent
" Messages enfants
ls_msg-msgv1 = ' Adresse mise a jour'.
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. " Handle parent
ls_msg-msgv1 = ' Contacts synchronises'.
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 avec priorite et niveau de detail

DATA: ls_msg TYPE bal_s_msg.
" Niveau de detail
ls_msg-detlevel = '1'. " 1-9, plus eleve = plus de detail
ls_msg-probclass = '1'. " Classe de probleme 1 = important, 4 = sans importance
" Filtrer a l'affichage
DATA: ls_profile TYPE bal_s_prof.
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET"
IMPORTING e_s_display_profile = ls_profile.
" Seulement les messages importants (classe de probleme 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 automatique pour les 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( |Traitement de { lines( it_data ) } entrees| ).
LOOP AT it_data INTO DATA(ls_data).
TRY.
" Traitement
process_single( ls_data ).
mo_log->add_success( |Entree { ls_data-id } traitee| ).
CATCH cx_root INTO DATA(lx_error).
mo_log->add_error( |Entree { ls_data-id }: { lx_error->get_text( ) }| ).
" Abandonner en cas d'erreur critique
IF lx_error IS INSTANCE OF zcx_critical_error.
mo_log->add_error( 'Erreur critique - Abandon' ).
mo_log->save( ).
RAISE EXCEPTION TYPE zcx_processing_error
EXPORTING previous = lx_error.
ENDIF.
ENDTRY.
ENDLOOP.
mo_log->add_success( 'Traitement termine' ).
ENDMETHOD.
ENDCLASS.

11. Journalisation en masse (Performance)

DATA: lt_messages TYPE bal_t_msg,
ls_msg TYPE bal_s_msg.
" Collecter les messages au lieu de les ajouter un par un
LOOP AT lt_data INTO DATA(ls_data).
ls_msg-msgty = 'S'.
ls_msg-msgid = 'ZLOG'.
ls_msg-msgno = '000'.
ls_msg-msgv1 = |Enregistrement { ls_data-id } traite|.
APPEND ls_msg TO lt_messages.
ENDLOOP.
" Ajouter tous en une fois
CALL FUNCTION 'BAL_LOG_MSGS_ADD"
EXPORTING
i_log_handle = lv_log_handle
i_t_msg = lt_messages
EXCEPTIONS
OTHERS = 1.
" Sauvegarder une seule fois
CALL FUNCTION 'BAL_DB_SAVE"
EXPORTING
i_save_all = abap_true.

12. Utiliser le log dans un job

REPORT zlog_in_job.
DATA: go_log TYPE REF TO zcl_application_log.
START-OF-SELECTION.
" Initialiser le 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 demarre' ).
TRY.
" Traitement
perform_processing( ).
go_log->add_success( 'Job termine avec succes' ).
CATCH cx_root INTO DATA(lx_error).
go_log->add_exception( lx_error ).
go_log->add_error( 'Job termine avec erreur' ).
ENDTRY.
" Toujours sauvegarder
go_log->save( ).
END-OF-SELECTION.

13. Appeler SLG1 par programmation

" Affichage de log comme SLG1
DATA: ls_display_profile TYPE bal_s_prof.
" Profil standard
CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET"
IMPORTING e_s_display_profile = ls_display_profile.
" Personnaliser les parametres
ls_display_profile-title = 'Mon affichage de log'.
ls_display_profile-tree_log = abap_true. " Representation en arbre
" Afficher
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY"
EXPORTING
i_s_display_profile = ls_display_profile
EXCEPTIONS
OTHERS = 1.

Modules fonction importants

Module fonctionDescription
BAL_LOG_CREATECreer un log
BAL_LOG_MSG_ADDAjouter un message
BAL_DB_SAVESauvegarder dans la BD
BAL_DB_SEARCHRechercher des logs
BAL_DB_LOADCharger des logs
BAL_DB_DELETESupprimer des logs
BAL_DSP_LOG_DISPLAYAfficher des logs

Remarques importantes / Bonnes pratiques

  • Objets de log a maintenir dans SLG0 (ne pas coder en dur).
  • Numero externe pour une recherche facile dans SLG1.
  • COMMIT WORK ne pas oublier apres BAL_DB_SAVE.
  • Hierarchie a utiliser pour une structuration claire.
  • Contexte pour la navigation vers les documents.
  • Callbacks a implementer pour l’affichage des details.
  • Journalisation en masse pour la performance avec beaucoup de messages.
  • Nettoyer regulierement les anciens logs (performance).
  • Classe de probleme et niveau de detail a utiliser pour le filtrage.
  • Combiner avec Background Jobs pour la journalisation batch.