Audit Trail Implementation in ABAP Cloud

kategorie
Security
Veröffentlicht
autor
Johannes

Ein Audit Trail dokumentiert alle relevanten Aenderungen an Geschaeftsdaten - wer hat wann was geaendert. In ABAP Cloud gibt es verschiedene Ansaetze, von Custom Audit Logs bis zur Integration mit dem SAP Audit Log Service.

Audit Trail vs. Change Documents

Bevor wir in die Implementierung einsteigen, ist es wichtig, den Unterschied zwischen Audit Trail und Change Documents zu verstehen:

AspektChange DocumentsAudit Trail
FokusTechnische FeldaenderungenGeschaeftsrelevante Aktionen
GranularitaetFeldebene (vorher/nachher)Aktionsebene (was wurde getan)
InhaltAutomatisch alle FelderSelektiv relevante Informationen
ZweckNachvollziehbarkeitCompliance, Forensik
LoeschungArchivierbarOft unveraenderbar
RechtskonformitaetOptionalOft verpflichtend

Wann Audit Trail statt Change Documents?

  • Regulatorische Anforderungen (SOX, GDPR, GxP)
  • Externe Audit-Anforderungen
  • Security-relevante Ereignisse (Logins, Berechtigungsaenderungen)
  • Geschaeftskritische Transaktionen (Zahlungen, Vertraege)

Audit Trail Anforderungen

Ein professioneller Audit Trail muss folgende Anforderungen erfuellen:

Funktionale Anforderungen

  1. Vollstaendigkeit: Alle relevanten Ereignisse erfassen
  2. Unveraenderbarkeit: Keine nachtraegliche Manipulation
  3. Zeitstempel: Praezise Erfassung des Zeitpunkts
  4. Benutzeridentifikation: Wer hat die Aktion ausgefuehrt
  5. Kontextinformationen: Was wurde geaendert (Objekt, Werte)

Technische Anforderungen

  1. Performance: Minimaler Overhead bei Transaktionen
  2. Speichereffizienz: Kompakte Datenstruktur
  3. Abfragbarkeit: Schnelle Suche und Filterung
  4. Archivierbarkeit: Langzeitspeicherung moeglich

Custom Audit Log Tabelle

Fuer maximale Kontrolle implementieren wir einen eigenen Audit Trail.

Tabellenstruktur

@EndUserText.label: 'Audit Trail Log'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
@AbapCatalog.tableCategory: #TRANSPARENT
@AbapCatalog.deliveryClass: #L
@AbapCatalog.dataMaintenance: #RESTRICTED
define table zaudit_log {
key client : abap.clnt not null;
key log_uuid : sysuuid_x16 not null;
created_at : timestampl;
created_by : syuname;
object_type : abap.char(30);
object_key : abap.char(100);
action_type : abap.char(20);
action_details : abap.string(0);
ip_address : abap.char(45);
user_agent : abap.char(255);
session_id : abap.char(40);
}

Datentypen fuer Actions

INTERFACE zif_audit_constants
PUBLIC.
CONSTANTS:
" Action Types
BEGIN OF gc_action,
create TYPE string VALUE 'CREATE',
update TYPE string VALUE 'UPDATE',
delete TYPE string VALUE 'DELETE',
read TYPE string VALUE 'READ',
approve TYPE string VALUE 'APPROVE',
reject TYPE string VALUE 'REJECT',
export TYPE string VALUE 'EXPORT',
login TYPE string VALUE 'LOGIN',
logout TYPE string VALUE 'LOGOUT',
auth_fail TYPE string VALUE 'AUTH_FAIL',
END OF gc_action.
CONSTANTS:
" Object Types
BEGIN OF gc_object,
sales_order TYPE string VALUE 'SALES_ORDER',
purchase_order TYPE string VALUE 'PURCHASE_ORDER',
customer TYPE string VALUE 'CUSTOMER',
vendor TYPE string VALUE 'VENDOR',
user TYPE string VALUE 'USER',
role TYPE string VALUE 'ROLE',
END OF gc_object.
ENDINTERFACE.

Audit Logger Klasse

CLASS zcl_audit_logger DEFINITION
PUBLIC
FINAL
CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
get_instance
RETURNING VALUE(ro_instance) TYPE REF TO zcl_audit_logger,
log_action
IMPORTING
iv_object_type TYPE string
iv_object_key TYPE string
iv_action_type TYPE string
iv_details TYPE string OPTIONAL.
PRIVATE SECTION.
CLASS-DATA:
go_instance TYPE REF TO zcl_audit_logger.
METHODS:
constructor,
get_session_context
EXPORTING
ev_ip_address TYPE string
ev_user_agent TYPE string
ev_session_id TYPE string.
ENDCLASS.
CLASS zcl_audit_logger IMPLEMENTATION.
METHOD get_instance.
IF go_instance IS NOT BOUND.
go_instance = NEW #( ).
ENDIF.
ro_instance = go_instance.
ENDMETHOD.
METHOD constructor.
" Initialisierung
ENDMETHOD.
METHOD log_action.
DATA: ls_audit TYPE zaudit_log.
" UUID generieren
TRY.
ls_audit-log_uuid = cl_system_uuid=>create_uuid_x16_static( ).
CATCH cx_uuid_error.
" Fallback
GET TIME STAMP FIELD DATA(lv_ts).
ls_audit-log_uuid = |{ lv_ts }{ sy-uname }|.
ENDTRY.
" Zeitstempel
GET TIME STAMP FIELD ls_audit-created_at.
" Benutzer
ls_audit-created_by = cl_abap_context_info=>get_user_technical_name( ).
" Audit Daten
ls_audit-object_type = iv_object_type.
ls_audit-object_key = iv_object_key.
ls_audit-action_type = iv_action_type.
ls_audit-action_details = iv_details.
" Session Kontext
get_instance( )->get_session_context(
IMPORTING
ev_ip_address = ls_audit-ip_address
ev_user_agent = ls_audit-user_agent
ev_session_id = ls_audit-session_id
).
" In Datenbank schreiben
INSERT zaudit_log FROM @ls_audit.
" Bei Fehler: Alternative Protokollierung
IF sy-subrc <> 0.
" Fallback zu Application Log
" Siehe application-logging.md
ENDIF.
ENDMETHOD.
METHOD get_session_context.
" IP-Adresse aus HTTP Request
TRY.
DATA(lo_request) = cl_http_server=>if_http_server~get_request( ).
ev_ip_address = lo_request->get_header_field( 'x-forwarded-for' ).
IF ev_ip_address IS INITIAL.
ev_ip_address = lo_request->get_header_field( 'remote_addr' ).
ENDIF.
ev_user_agent = lo_request->get_header_field( 'user-agent' ).
CATCH cx_root.
ev_ip_address = 'N/A'.
ev_user_agent = 'N/A'.
ENDTRY.
" Session ID
ev_session_id = cl_abap_context_info=>get_session_id( ).
ENDMETHOD.
ENDCLASS.

RAP Determination fuer Logging

In RAP nutzen wir Determinations, um automatisch Audit Logs zu erstellen. Siehe auch RAP Determinations und Validations.

Behavior Definition

managed implementation in class zbp_i_salesorder unique;
strict ( 2 );
define behavior for ZI_SalesOrder alias SalesOrder
persistent table zsalesorder
lock master
authorization master ( instance )
{
create;
update;
delete;
" Determination fuer Audit Trail
determination auditCreate on save { create; }
determination auditUpdate on save { update; }
determination auditDelete on save { delete; }
" Alle Felder
field ( readonly ) OrderId;
field ( readonly ) CreatedAt, CreatedBy, LastChangedAt, LastChangedBy;
}

Determination Implementation

CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS:
auditCreate FOR DETERMINE ON SAVE
IMPORTING keys FOR SalesOrder~auditCreate,
auditUpdate FOR DETERMINE ON SAVE
IMPORTING keys FOR SalesOrder~auditUpdate,
auditDelete FOR DETERMINE ON SAVE
IMPORTING keys FOR SalesOrder~auditDelete.
ENDCLASS.
CLASS lhc_salesorder IMPLEMENTATION.
METHOD auditCreate.
" Lese die erstellten Daten
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order).
" Audit Log erstellen
zcl_audit_logger=>log_action(
iv_object_type = zif_audit_constants=>gc_object-sales_order
iv_object_key = |{ ls_order-OrderId }|
iv_action_type = zif_audit_constants=>gc_action-create
iv_details = |Customer: { ls_order-CustomerId }, | &&
|Amount: { ls_order-NetAmount } { ls_order-Currency }|
).
ENDLOOP.
ENDMETHOD.
METHOD auditUpdate.
" Lese die aktualisierten Daten
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
" Optional: Vorherige Werte fuer Vergleich
" Dies erfordert zusaetzliche Logik oder Change Documents
LOOP AT lt_orders INTO DATA(ls_order).
zcl_audit_logger=>log_action(
iv_object_type = zif_audit_constants=>gc_object-sales_order
iv_object_key = |{ ls_order-OrderId }|
iv_action_type = zif_audit_constants=>gc_action-update
iv_details = |Status: { ls_order-Status }|
).
ENDLOOP.
ENDMETHOD.
METHOD auditDelete.
" Bei Delete haben wir nur noch die Keys
LOOP AT keys INTO DATA(ls_key).
zcl_audit_logger=>log_action(
iv_object_type = zif_audit_constants=>gc_object-sales_order
iv_object_key = |{ ls_key-OrderId }|
iv_action_type = zif_audit_constants=>gc_action-delete
iv_details = ''
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Audit bei RAP Actions

Fuer besonders wichtige Aktionen wie Freigaben:

" In der Behavior Definition
action approve result [1] $self;
action reject result [1] $self;
" In der Implementation
METHOD approve.
MODIFY ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( Status ApprovedAt ApprovedBy )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
Status = 'APPROVED'
ApprovedAt = cl_abap_context_info=>get_system_time( )
ApprovedBy = cl_abap_context_info=>get_user_technical_name( )
) )
FAILED DATA(lt_failed)
REPORTED DATA(lt_reported).
" Audit Log fuer Freigabe
LOOP AT keys INTO DATA(ls_key).
zcl_audit_logger=>log_action(
iv_object_type = zif_audit_constants=>gc_object-sales_order
iv_object_key = |{ ls_key-OrderId }|
iv_action_type = zif_audit_constants=>gc_action-approve
iv_details = ''
).
ENDLOOP.
" Rueckgabe
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
result = VALUE #( FOR order IN lt_orders (
%tky = order-%tky
%param = order
) ).
ENDMETHOD.

Audit Log Auswertung

CDS View fuer Audit Log Analyse

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Audit Log Analysis'
define view entity ZI_AuditLogAnalysis
as select from zaudit_log
{
key log_uuid as LogUuid,
created_at as CreatedAt,
created_by as CreatedBy,
object_type as ObjectType,
object_key as ObjectKey,
action_type as ActionType,
action_details as ActionDetails,
ip_address as IpAddress,
session_id as SessionId,
" Berechnete Felder
cast( created_at as abap.dats ) as CreatedDate,
substring( cast( created_at as abap.char( 26 ) ), 12, 8 ) as CreatedTime,
" Aggregationen
@Aggregation.default: #COUNT
1 as ActionCount
}

Fiori App fuer Audit Log

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Audit Log'
@Metadata.allowExtensions: true
@Search.searchable: true
define view entity ZC_AuditLog
as projection on ZI_AuditLogAnalysis
{
@UI.facet: [
{ id: 'General', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Details', position: 10 },
{ id: 'Context', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'Kontext', position: 20, targetQualifier: 'Context' }
]
@UI: { lineItem: [{ position: 10 }], identification: [{ position: 10 }] }
key LogUuid,
@UI: { lineItem: [{ position: 20, importance: #HIGH }],
identification: [{ position: 20 }],
selectionField: [{ position: 10 }] }
@Search.defaultSearchElement: true
CreatedAt,
@UI: { lineItem: [{ position: 30 }],
identification: [{ position: 30 }],
selectionField: [{ position: 20 }] }
@Search.defaultSearchElement: true
CreatedBy,
@UI: { lineItem: [{ position: 40, importance: #HIGH }],
identification: [{ position: 40 }],
selectionField: [{ position: 30 }] }
@Search.defaultSearchElement: true
ObjectType,
@UI: { lineItem: [{ position: 50 }],
identification: [{ position: 50 }] }
ObjectKey,
@UI: { lineItem: [{ position: 60, importance: #HIGH }],
identification: [{ position: 60 }],
selectionField: [{ position: 40 }] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_ActionTypeVH', element: 'ActionType' } }]
ActionType,
@UI: { identification: [{ position: 70 }] }
ActionDetails,
@UI: { fieldGroup: [{ qualifier: 'Context', position: 10 }] }
IpAddress,
@UI: { fieldGroup: [{ qualifier: 'Context', position: 20 }] }
SessionId,
CreatedDate,
CreatedTime,
ActionCount
}

Abfrage-Service fuer Auswertungen

CLASS zcl_audit_query_service DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_audit_summary,
object_type TYPE string,
action_type TYPE string,
action_count TYPE i,
END OF ty_audit_summary,
tt_audit_summary TYPE STANDARD TABLE OF ty_audit_summary WITH EMPTY KEY.
METHODS:
get_user_activity
IMPORTING
iv_user TYPE syuname
iv_from_date TYPE timestampl OPTIONAL
iv_to_date TYPE timestampl OPTIONAL
RETURNING
VALUE(rt_log) TYPE STANDARD TABLE OF zaudit_log,
get_object_history
IMPORTING
iv_object_type TYPE string
iv_object_key TYPE string
RETURNING
VALUE(rt_log) TYPE STANDARD TABLE OF zaudit_log,
get_suspicious_activity
RETURNING
VALUE(rt_log) TYPE STANDARD TABLE OF zaudit_log,
get_action_summary
IMPORTING
iv_from_date TYPE timestampl OPTIONAL
RETURNING
VALUE(rt_summary) TYPE tt_audit_summary.
ENDCLASS.
CLASS zcl_audit_query_service IMPLEMENTATION.
METHOD get_user_activity.
DATA(lv_from) = COND #( WHEN iv_from_date IS INITIAL
THEN CONV timestampl( '00000000000000' )
ELSE iv_from_date ).
DATA(lv_to) = COND #( WHEN iv_to_date IS INITIAL
THEN CONV timestampl( '99991231235959' )
ELSE iv_to_date ).
SELECT * FROM zaudit_log
WHERE created_by = @iv_user
AND created_at BETWEEN @lv_from AND @lv_to
ORDER BY created_at DESCENDING
INTO TABLE @rt_log.
ENDMETHOD.
METHOD get_object_history.
SELECT * FROM zaudit_log
WHERE object_type = @iv_object_type
AND object_key = @iv_object_key
ORDER BY created_at DESCENDING
INTO TABLE @rt_log.
ENDMETHOD.
METHOD get_suspicious_activity.
" Beispiel: Viele fehlgeschlagene Logins
SELECT * FROM zaudit_log
WHERE action_type = @zif_audit_constants=>gc_action-auth_fail
AND created_at >= @( cl_abap_context_info=>get_system_date( ) - 1 )
ORDER BY created_at DESCENDING
INTO TABLE @rt_log.
" Weitere Kriterien:
" - Ungewoehnliche Uhrzeiten
" - Viele Loeschungen
" - Zugriffe auf sensitive Objekte
ENDMETHOD.
METHOD get_action_summary.
DATA(lv_from) = COND #( WHEN iv_from_date IS INITIAL
THEN CONV timestampl( '00000000000000' )
ELSE iv_from_date ).
SELECT object_type, action_type, COUNT(*) AS action_count
FROM zaudit_log
WHERE created_at >= @lv_from
GROUP BY object_type, action_type
ORDER BY action_count DESCENDING
INTO TABLE @rt_summary.
ENDMETHOD.
ENDCLASS.

Integration mit SAP Audit Log Service

Der SAP Audit Log Service auf BTP bietet eine zentrale, manipulationssichere Audit-Loesung.

Service Binding einrichten

Im BTP Cockpit:

  1. Services > Service Marketplace oeffnen
  2. Audit Log Service suchen
  3. Create Instance mit Plan “standard”
  4. Service Key erstellen fuer ABAP-Zugriff

Communication Arrangement

Communication Scenario: SAP_COM_0193 (Audit Log Integration)
1. Fiori Launchpad > Communication Arrangements
2. "New" waehlen
3. Scenario: SAP_COM_0193
4. Communication System mit BTP-Destination verknuepfen
5. Inbound Service aktivieren

Audit Log Service aufrufen

CLASS zcl_audit_log_service DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
write_audit_log
IMPORTING
iv_object_type TYPE string
iv_object_id TYPE string
iv_action TYPE string
iv_attributes TYPE string_table OPTIONAL
RAISING
cx_static_check.
ENDCLASS.
CLASS zcl_audit_log_service IMPLEMENTATION.
METHOD write_audit_log.
" HTTP Client erstellen
DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'AUDIT_LOG_SERVICE'
).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
" Request bauen
DATA(lo_request) = lo_client->get_http_request( ).
" Audit Message als JSON
DATA(lv_timestamp) = |{ sy-datum }T{ sy-uzeit }Z|.
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
DATA(lv_json) = |\{| &&
|"uuid":"{ cl_system_uuid=>create_uuid_c36_static( ) }",| &&
|"time":"{ lv_timestamp }",| &&
|"user":"{ lv_user }",| &&
|"object":\{| &&
|"type":"{ iv_object_type }",| &&
|"id":\{"key":"{ iv_object_id }"\}| &&
|\},| &&
|"action":"{ iv_action }"| &&
|\}|.
lo_request->set_text( lv_json ).
lo_request->set_header_field(
i_name = 'Content-Type'
i_value = 'application/json'
).
" Request senden
DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
IF lo_response->get_status( )-code <> 201.
RAISE EXCEPTION TYPE cx_abap_not_a_number
MESSAGE e001(zaudit) WITH lo_response->get_status( )-reason.
ENDIF.
lo_client->close( ).
ENDMETHOD.
ENDCLASS.

Hybrid-Ansatz: Lokal + Cloud

Fuer beste Ergebnisse kombinieren wir lokale und Cloud-Logs:

METHOD log_action_hybrid.
" 1. Lokales Audit Log (schnell, immer verfuegbar)
zcl_audit_logger=>log_action(
iv_object_type = iv_object_type
iv_object_key = iv_object_key
iv_action_type = iv_action_type
iv_details = iv_details
).
" 2. Cloud Audit Log (asynchron, manipulationssicher)
TRY.
NEW zcl_audit_log_service( )->write_audit_log(
iv_object_type = iv_object_type
iv_object_id = iv_object_key
iv_action = iv_action_type
).
CATCH cx_static_check INTO DATA(lx_error).
" Bei Cloud-Fehler: Lokales Log reicht
" Optional: Application Log fuer Monitoring
ENDTRY.
ENDMETHOD.

Aufbewahrungsfristen und Archivierung

Gesetzliche Anforderungen

RegulierungAufbewahrungsfristAnwendungsbereich
HGB (DE)10 JahreGeschaeftsrelevante Dokumente
GoBD (DE)10 JahreBuchhaltungsrelevante Daten
SOX (US)7 JahreBoersennotierte Unternehmen
GDPR/DSGVOMinimierungPersonenbezogene Daten
GxP15+ JahrePharma/Medizin

Archivierungs-Job

CLASS zcl_audit_archiver DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
archive_old_entries
IMPORTING
iv_retention_days TYPE i DEFAULT 365.
ENDCLASS.
CLASS zcl_audit_archiver IMPLEMENTATION.
METHOD archive_old_entries.
" Berechne Stichtag
DATA(lv_cutoff_date) = CONV timestampl(
|{ cl_abap_context_info=>get_system_date( ) - iv_retention_days }000000|
).
" 1. Exportiere alte Eintraege (z.B. als JSON)
SELECT * FROM zaudit_log
WHERE created_at < @lv_cutoff_date
INTO TABLE @DATA(lt_archive).
IF lt_archive IS NOT INITIAL.
" JSON-Export erstellen
DATA(lo_json) = /ui2/cl_json=>serialize( lt_archive ).
" In Archiv-Storage speichern
" (z.B. Object Store, Document Management Service)
" Siehe: sap-document-management-service.md
" 2. Loesche archivierte Eintraege
DELETE FROM zaudit_log WHERE created_at < @lv_cutoff_date.
" 3. Protokolliere Archivierung
zcl_audit_logger=>log_action(
iv_object_type = 'AUDIT_LOG'
iv_object_key = |ARCHIVE_{ sy-datum }|
iv_action_type = 'ARCHIVE'
iv_details = |{ lines( lt_archive ) } Eintraege archiviert|
).
ENDIF.
ENDMETHOD.
ENDCLASS.

Background Job einrichten

" Job fuer regelmaessige Archivierung
CLASS zcl_audit_archive_job DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES: if_apj_dt_exec_object,
if_apj_rt_exec_object.
ENDCLASS.
CLASS zcl_audit_archive_job IMPLEMENTATION.
METHOD if_apj_dt_exec_object~get_parameters.
" Job-Parameter definieren
et_parameter_def = VALUE #(
( selname = 'P_DAYS'
kind = if_apj_dt_exec_object=>parameter
datatype = 'INT4'
mandatory = abap_false
changeable_ind = abap_true )
).
et_parameter_val = VALUE #(
( selname = 'P_DAYS' sign = 'I' option = 'EQ' low = '365' )
).
ENDMETHOD.
METHOD if_apj_rt_exec_object~execute.
" Parameter lesen
DATA(lv_days) = CONV i( it_parameters[ selname = 'P_DAYS' ]-low ).
" Archivierung ausfuehren
NEW zcl_audit_archiver( )->archive_old_entries( lv_days ).
ENDMETHOD.
ENDCLASS.

Best Practices

1. Performance-Optimierung

" Schlecht: Einzelne INSERTs
LOOP AT lt_changes INTO DATA(ls_change).
zcl_audit_logger=>log_action( ... ). " Jeder Aufruf = 1 DB-Insert
ENDLOOP.
" Besser: Bulk-Insert
CLASS zcl_audit_logger DEFINITION.
CLASS-METHODS:
log_actions_bulk
IMPORTING
it_audit_entries TYPE tt_audit_entries.
ENDCLASS.
METHOD log_actions_bulk.
INSERT zaudit_log FROM TABLE @it_audit_entries.
ENDMETHOD.

2. Asynchrones Logging

Fuer zeitkritische Transaktionen:

" BGPF fuer asynchrones Audit Logging
CLASS zcl_audit_async DEFINITION.
INTERFACES: if_bgmc_op_single.
DATA: ms_audit TYPE zaudit_log.
ENDCLASS.
METHOD if_bgmc_op_single~execute.
INSERT zaudit_log FROM @ms_audit.
ENDMETHOD.
" Verwendung
DATA(lo_async) = NEW zcl_audit_async( ).
lo_async->ms_audit = ls_audit_entry.
cl_bgmc_process_factory=>get_default( )->create(
)->set_operation_single( lo_async
)->save_for_execution( ).

3. Selektives Logging

Nicht alles loggen - nur relevante Ereignisse:

METHOD should_log_action.
" Konfigurierbare Whitelist
SELECT SINGLE @abap_true
FROM zaudit_config
WHERE object_type = @iv_object_type
AND action_type = @iv_action_type
AND is_active = @abap_true
INTO @rv_should_log.
ENDMETHOD.

4. Sensitive Daten maskieren

METHOD mask_sensitive_data.
" Kreditkartennummer maskieren
rv_masked = replace(
val = iv_data
regex = '\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?(\d{4})'
with = '****-****-****-$1'
).
" Passwort entfernen
rv_masked = replace(
val = rv_masked
regex = '"password"\s*:\s*"[^"]*"'
with = '"password":"***"'
).
ENDMETHOD.

Zusammenfassung

Ein professioneller Audit Trail in ABAP Cloud erfordert:

KomponenteImplementierung
SpeicherungCustom Tabelle + optional SAP Audit Log Service
ErfassungRAP Determinations fuer automatisches Logging
KontextUser, Timestamp, IP, Session
AuswertungCDS Views + Fiori App
ArchivierungBackground Job mit Retention Policy
PerformanceBulk-Insert, asynchrones Logging

Weiterführende Themen