45 · Audit Logging

Enable @PersonalData.IsPotentiallyPersonal and @cap-js/audit-logging for automatic CRUD audit trails.

Episode 45 — Audit Logging with SAP Audit Log Service

What you'll learn

Why this matters

GDPR and SOX mandate immutable audit records for access to personal data. SAP Audit Log Service provides a tamper-proof, cloud-hosted log. CAP's audit-log plugin can generate these automatically from @PersonalData annotations — no custom logging code required for standard CRUD.

SAP Audit Log Service overview

Step 1 — Annotate personal data in CDS

// db/schema.cds
using { managed, cuid } from '@sap/cds/common';

entity Customers : cuid, managed {
  @PersonalData.FieldSemantics: 'DataSubjectID'
  customerNumber : String;          // ← unique identifier for the data subject

  @PersonalData.IsPotentiallyPersonal: true
  firstName : String;

  @PersonalData.IsPotentiallyPersonal: true
  lastName  : String;

  @PersonalData.IsPotentiallySensitive: true
  dateOfBirth : Date;               // sensitive — stricter logging

  email     : String;
  @PersonalData.IsPotentiallyPersonal: true
  phoneNumber : String;
}

annotate Customers with @PersonalData: {
  EntitySemantics: 'DataSubject',
  DataSubjectRole: 'Customer'
};

Step 2 — Enable the audit-log plugin

npm install @cap-js/audit-logging
// .cdsrc.json
{
  "requires": {
    "audit-log": {
      "[production]": {
        "kind": "audit-log-service",
        "vcap": { "label": "auditlog" }
      },
      "[development]": {
        "kind": "@cap-js/audit-logging",
        "impl": "@cap-js/audit-logging/lib/sqlite"  // logs to console in dev
      }
    }
  }
}

Step 3 — Bind Audit Log Service in MTA

# mta.yaml
resources:
  - name: auditlog
    type: org.cloudfoundry.managed-service
    parameters:
      service:      auditlog
      service-plan: premium
modules:
  - name: bookshop-srv
    requires:
      - name: auditlog

What gets logged automatically

Operation Auto-logged
READ of @PersonalData entity Data access log entry
CREATE with personal fields Data modification log
UPDATE of personal fields Before/after values
DELETE of @PersonalData entity Data erasure log

Each log entry includes: timestamp, user ID, tenant, operation, entity name, data subject ID, and field values (before/after for updates).

Manual audit log entries

const audit = await cds.connect.to('audit-log')

this.on('exportCustomerData', async (req) => {
  const { customerId } = req.data
  const data = await SELECT.from('Customers').where({ ID: customerId })

  // Manually log a data access event
  await audit.log('SensitiveDataExport', {
    object:  { type: 'Customers', id: { ID: customerId } },
    data_subject: { type: 'Customer', id: customerId },
    attributes: [{ name: 'export', new_value: 'GDPR export triggered' }],
    tenant:  req.tenant
  })

  return data
})

Viewing audit logs in BTP Cockpit

  1. BTP Cockpit → Subaccount → Services → Instances → auditlog → Open SAP Audit Log Viewer
  2. Filter by time range, user, entity type
  3. Export for compliance reporting (CSV or JSON)

Development audit log (console)

# In dev, audit entries appear in console:
[audit] READ Customers { user: 'alice', subject: { ID: 'cust-001' }, fields: ['firstName','email'] }
[audit] UPDATE Customers { user: 'alice', field: 'email', old: 'a@b.com', new: 'c@d.com' }

Checkpoint ✓

Sign in for progress tracking & Q&A →