[ABAP] Business Application Log (BAL) verwenden

* Paket: SZAL
* Demo: SBAL_DEMO_*
* Vorteil: vollständige Parametrierung über bal_s_log (Externe Identifikation, Verfallsdatum...)
* Link: https://wiki.scn.sap.com/wiki/display/Snippets/Using+Application+Log
CLASS lcl_applog DEFINITION.
  PUBLIC SECTION.

    TYPES:
      BEGIN OF ty_s_msgtext,
        msgv1 TYPE symsgv,
        msgv2 TYPE symsgv,
        msgv3 TYPE symsgv,
        msgv4 TYPE symsgv,
      END OF ty_s_msgtext .

    CONSTANTS: co_msg_type_error TYPE symsgty VALUE 'E'.
    CONSTANTS: co_msg_type_warning TYPE symsgty VALUE 'W'.
    CONSTANTS: co_msg_type_info TYPE symsgty VALUE 'I'.
    CONSTANTS: co_problem_class_high TYPE balprobcl VALUE '1'.
    CONSTANTS: co_problem_class_medium TYPE balprobcl VALUE '2'.
    CONSTANTS: co_problem_class_low TYPE balprobcl VALUE '3'.
    CONSTANTS: co_problem_class_info TYPE balprobcl VALUE '4'.

    CLASS-METHODS:
      create
        IMPORTING
                  i_bal_s_log  TYPE bal_s_log
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      add_message
        IMPORTING
                  i_msgtype    TYPE symsgty DEFAULT co_msg_type_error
                  i_msgid      TYPE symsgid DEFAULT '01'  " Verwendung der generische Nachrichtenklasse mit & & & &
                  i_msgno      TYPE symsgno DEFAULT '319' " Verwendung der generische Nachrichtenklasse mit & & & &
                  i_msgv1      TYPE symsgv OPTIONAL
                  i_msgv2      TYPE symsgv OPTIONAL
                  i_msgv3      TYPE symsgv OPTIONAL
                  i_msgv4      TYPE symsgv OPTIONAL
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      add_string
        IMPORTING
                  i_msgtype    TYPE symsgty DEFAULT co_msg_type_error
                  i_msgid      TYPE symsgid DEFAULT '01'  " Verwendung der generische Nachrichtenklasse mit & & & &
                  i_msgno      TYPE symsgno DEFAULT '319' " Verwendung der generische Nachrichtenklasse mit & & & &
                  i_msg_text   TYPE string
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      add_exception
        IMPORTING
                  i_probclass  TYPE balprobcl DEFAULT co_problem_class_info
                  i_msgtype    TYPE symsgty DEFAULT co_msg_type_error
                  i_exception  TYPE REF TO cx_root
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      save
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      delete
        RETURNING VALUE(rv_ok) TYPE abap_bool.

    CLASS-METHODS:
      display_current
        IMPORTING
          i_title     TYPE baltitle OPTIONAL
          i_it_balnri TYPE ehsky_balnri.

    CLASS-METHODS:
      display
        IMPORTING
          iv_object                    TYPE balhdr-object DEFAULT space
          iv_subobject                 TYPE balhdr-subobject DEFAULT space
          iv_external_number           TYPE balhdr-extnumber DEFAULT space
          iv_object_attribute          TYPE i DEFAULT 0
          iv_subobject_attribute       TYPE i DEFAULT 0
          iv_external_number_attribute TYPE i DEFAULT 0
          iv_date_from                 TYPE balhdr-aldate DEFAULT sy-datum
          iv_time_from                 TYPE balhdr-altime DEFAULT '000000'
          iv_date_to                   TYPE balhdr-aldate DEFAULT sy-datum
          iv_time_to                   TYPE balhdr-altime DEFAULT sy-uzeit
          iv_suppress_selection_dialog TYPE abap_bool DEFAULT abap_false
          iv_title_selection_screen    TYPE char255 DEFAULT space
          iv_title_list_screen         TYPE char255 DEFAULT space.

    CLASS-METHODS:
      has_data
        RETURNING VALUE(rv_has) TYPE abap_bool.

  PRIVATE SECTION.
    CLASS-DATA:
      gv_log_handle TYPE balloghndl.

ENDCLASS.

CLASS lcl_applog IMPLEMENTATION.

* Log erzeugen und Log-Handle ermitteln
  METHOD create.

    rv_ok = abap_false.

    CALL FUNCTION 'BAL_LOG_CREATE'
      EXPORTING
        i_s_log                 = i_bal_s_log
      IMPORTING
        e_log_handle            = gv_log_handle
      EXCEPTIONS
        log_header_inconsistent = 1
        OTHERS                  = 2.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.

  ENDMETHOD.

* Log-Message hinzufügen
* Achtung: Die Stringlänge für die Parameter i_msgv1, i_msgv2, i_msgv3 und i_msgv4 ist jeweils max. 50 Zeichen!
  METHOD add_message.

    rv_ok = abap_false.

    DATA(lv_msg) = VALUE bal_s_msg( msgty = i_msgtype
                                    msgid = i_msgid
                                    msgno = i_msgno
                                    msgv1 = i_msgv1
                                    msgv2 = i_msgv2
                                    msgv3 = i_msgv3
                                    msgv4 = i_msgv4 ).

    CALL FUNCTION 'BAL_LOG_MSG_ADD'
      EXPORTING
        i_log_handle     = gv_log_handle
        i_s_msg          = lv_msg
      EXCEPTIONS
        log_not_found    = 1
        msg_inconsistent = 2
        log_is_full      = 3
        OTHERS           = 4.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.

  ENDMETHOD.

  METHOD add_string.
    rv_ok = abap_false.

* String (max. 200 Zeichen) -> TY_S_MSGTEXT (msgv1 ... msgv4) (max. 4 x 50 Zeichen) wandeln
    DATA(lv_msg_text) = CONV ty_s_msgtext( i_msg_text ).

    DATA(lv_msg) = VALUE bal_s_msg( msgty = i_msgtype
                                    msgid = i_msgid
                                    msgno = i_msgno
                                    msgv1 = lv_msg_text-msgv1
                                    msgv2 = lv_msg_text-msgv2
                                    msgv3 = lv_msg_text-msgv3
                                    msgv4 = lv_msg_text-msgv4 ).

* altenativ: Verwendung von FuBa BAL_LOG_MSG_ADD_FREE_TEXT
    CALL FUNCTION 'BAL_LOG_MSG_ADD'
      EXPORTING
        i_log_handle     = gv_log_handle
        i_s_msg          = lv_msg
      EXCEPTIONS
        log_not_found    = 1
        msg_inconsistent = 2
        log_is_full      = 3
        OTHERS           = 4.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.
  ENDMETHOD.

* Log-Message anhand einer Exception hinzufügen
  METHOD add_exception.

    rv_ok = abap_false.

    DATA(lv_ex) = VALUE bal_s_exc( probclass  = i_probclass
                                   msgty      = i_msgtype
                                   exception  = i_exception ).

    CALL FUNCTION 'BAL_LOG_EXCEPTION_ADD'
      EXPORTING
        i_log_handle     = gv_log_handle
        i_s_exc          = lv_ex
      EXCEPTIONS
        log_not_found    = 1
        msg_inconsistent = 2
        log_is_full      = 3
        OTHERS           = 4.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.

  ENDMETHOD.

* akt. Log auf der Datenbank speichern
  METHOD save.

    rv_ok = abap_false.

    DATA: it_log_handle TYPE bal_t_logh.
    APPEND gv_log_handle TO it_log_handle.

* alternativ: Verwendung von FuBa APPL_LOG_WRITE_DB
    CALL FUNCTION 'BAL_DB_SAVE'
      EXPORTING
        i_t_log_handle   = it_log_handle
      EXCEPTIONS
        log_not_found    = 1
        save_not_allowed = 2
        numbering_error  = 3
        OTHERS           = 4.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.

  ENDMETHOD.

* Log-Messages löschen
  METHOD delete.

    rv_ok = abap_false.

    CALL FUNCTION 'BAL_LOG_MSG_DELETE_ALL'
      EXPORTING
        i_log_handle  = gv_log_handle
      EXCEPTIONS
        log_not_found = 1
        OTHERS        = 2.

    IF sy-subrc = 0.
      rv_ok = abap_true.
    ENDIF.

  ENDMETHOD.

* akt. Log anzeigen
  METHOD display_current.

    IF sy-batch IS INITIAL.

      DATA: lv_bal_s_prof TYPE bal_s_prof.

      CALL FUNCTION 'BAL_DSP_PROFILE_STANDARD_GET'
        IMPORTING
          e_s_display_profile = lv_bal_s_prof
        EXCEPTIONS
          OTHERS              = 1.

      lv_bal_s_prof-show_all   = abap_true.
      lv_bal_s_prof-cwidth_opt = abap_true.

      DATA(it_lognumbers) = VALUE szal_lognumbers( FOR <l> IN i_it_balnri
                                                   (
                                                      item = <l>-lognumber
                                                   )
                                                 ).

      CALL FUNCTION 'APPL_LOG_DISPLAY_WITH_LOGNO'
        EXPORTING
          title_list_screen   = i_title
          i_s_display_profile = lv_bal_s_prof
        TABLES
          lognumbers          = it_lognumbers
        EXCEPTIONS
          no_authority        = 1
          OTHERS              = 2.
    ENDIF.

  ENDMETHOD.

* SLG1 aufrufen: gesamtes Appl. Log anhand von Suchkriterien anzeigen
  METHOD display.

    CALL FUNCTION 'APPL_LOG_DISPLAY'
      EXPORTING
        object                    = iv_object
        subobject                 = iv_subobject
        external_number           = iv_external_number
        object_attribute          = iv_object_attribute
        subobject_attribute       = iv_subobject_attribute
        external_number_attribute = iv_external_number_attribute
        date_from                 = iv_date_from
        time_from                 = iv_time_from
        date_to                   = iv_date_to
        time_to                   = iv_time_to
        title_selection_screen    = iv_title_selection_screen
        title_list_screen         = iv_title_list_screen
*       COLUMN_SELECTION          = '11112221122   '
        suppress_selection_dialog = iv_suppress_selection_dialog
*       COLUMN_SELECTION_MSG_JUMP = '1'
*       EXTERNAL_NUMBER_DISPLAY_LENGTH       = 20
*       I_S_DISPLAY_PROFILE       =
*       I_VARIANT_REPORT          = ' '
*       I_SRT_BY_TIMSTMP          = ' '
*     IMPORTING
*       NUMBER_OF_PROTOCOLS       =
      EXCEPTIONS
        no_authority              = 1
        OTHERS                    = 2.

  ENDMETHOD.

* sind für das akt. Log Daten verfügbar
  METHOD has_data.

    rv_has = abap_false.

    DATA: lv_statistics TYPE bal_s_scnt.

    CALL FUNCTION 'BAL_LOG_HDR_READ'
      EXPORTING
        i_log_handle  = gv_log_handle
      IMPORTING
        e_statistics  = lv_statistics
      EXCEPTIONS
        log_not_found = 1
        OTHERS        = 2.

    IF sy-subrc = 0.

      CLEAR: lv_statistics-msg_max_pc.

      IF NOT lv_statistics IS INITIAL.
        rv_has = abap_true.
      ENDIF.
    ENDIF.

  ENDMETHOD.

ENDCLASS.
**********************************************************************
*
* PARAMETERS
*
**********************************************************************
PARAMETERS: p_all AS CHECKBOX DEFAULT abap_false.
**********************************************************************
*
* START-OF-SELECTION
*
**********************************************************************
START-OF-SELECTION.
  IF abap_false = p_all.
* Log-Objekte und Subobjekte definieren -> Transaktion SLG0 --> es werden Transportaufträge generiert
* extnumber: Freitext für spätere Suche in der SLG1, i.d.R. Belegnummern
* aldate_del: Verfallsdatum der Lognachricht (i.d.R. 30 Tage)
    DATA(lv_bal_log) = VALUE bal_s_log( object     = 'Z_TEST_LOG'
                                        subobject  = 'Z_TEST_LOG_U1'
                                        extnumber  = 'Identifier_01234'
                                        aldate_del = sy-datum + 30
                                      ).
* Legt eine Protokoll-Instanz an
* Log-Objekte werden in der Transaktion SLG0 definiert -> es werden Transportaufträge generiert
* Anlegen der Objekte erfolgt in den Tabellen
*   BALOBJ (Anwendungs-Log: Objekte)
*   BALSUB (Anwendungs-Log: Unterobjekte)
    IF abap_true = lcl_applog=>create( i_bal_s_log = lv_bal_log ).
* Protokolleinträge hinzufügen -> Nachrichten werden in der Transaktion SE91 gefplegt
* Beispiel: Verwendung der vordefinierten Nachrichtenklasse 'S_AUT', Nr. '128'
      lcl_applog=>add_message( i_msgtype = lcl_applog=>co_msg_type_info
                               i_msgid   = 'S_AUT'
                               i_msgno   = '128' ).

* Beispiel: Verwendung der generische Nachrichtenklasse mit & & & & für die Parameter msgv1 - msgv4
      lcl_applog=>add_message( i_msgtype = lcl_applog=>co_msg_type_warning
                               i_msgv1   = 'Test1'
                               i_msgv2   = 'Test2'
                               i_msgv3   = 'Test3'
                               i_msgv4   = 'Test4' ).

* Beispiel: Verwendung der generische Nachrichtenklasse mit & & & &
      lcl_applog=>add_string( i_msgtype = lcl_applog=>co_msg_type_error
                              i_msg_text = 'Testeintrag' ).

      TRY.
* Ausnahme auslösen
          DATA(result) = 1 / 0.

        CATCH cx_root INTO DATA(e_txt).
* Fügt eine Ausnahme hinzu, wobei
* i_probclass: 1 - sehr wichtig
*              2 - wichtig
*              3 - mittel
*              4 - Zusatzinformationen
          lcl_applog=>add_exception( i_probclass = lcl_applog=>co_problem_class_info
                                     i_msgtype   = lcl_applog=>co_msg_type_error
                                     i_exception = e_txt ).
      ENDTRY.

* Schließt und persistiert (speichert) das Protokoll
* Protokoll kann auch in der Transaktion SLG1 angeschaut werden
* Speicherung der Log-Daten erfolgt in den Tabellen
*   BALHDR (Anwendungs-Log: Protokollkopf)
*   BALDAT (Anwendungs-Log: Daten eines Protokolls)
      DATA: it_balnri TYPE ehsky_balnri.

      IF abap_true = lcl_applog=>save( ).

* Existieren Meldungen?
        IF abap_true = lcl_applog=>has_data( ).
* Protokoll anzeigen
          lcl_applog=>display_current( i_title     = 'Application-Log'
                                       i_it_balnri = it_balnri ).
        ELSE.
          WRITE: / 'Keine Statistik verfügbar.'.
        ENDIF.
      ELSE.
        WRITE: / 'Fehler beim Speichern des Logs.'.
      ENDIF.
    ELSE.
      WRITE: / 'Fehler beim Erzeugen des Logs.'.
    ENDIF.
  ELSE.
* SLG1 aufrufen
    lcl_applog=>display( iv_object    = 'Z_TEST_LOG'
                         iv_subobject = 'Z_TEST_LOG_U1' ).
  ENDIF.