Security

Security Audit Logging & Compliance: SOC2, GDPR & PCI-DSS for Java Developers in 2026

Audit logging is simultaneously a compliance requirement and a security control. Without it, you fail SOC2 Type II, violate GDPR Article 30, and breach PCI-DSS Requirement 10. With it, you can detect insider threats, trace data breaches to their source, demonstrate regulatory compliance, and answer "who did what, when, and from where" after any incident. This guide gives you a complete Java Spring Boot implementation of tamper-proof, compliance-ready audit logging with centralized SIEM integration.

Md Sanwar Hossain April 6, 2026 18 min read Compliance Logging
Security audit logging compliance SOC2 GDPR PCI-DSS Java Spring Boot structured audit trail

TL;DR — Audit Logs Are Not Application Logs

"Application logs tell you what your system did. Audit logs tell you who did what to which data, when, and from where — with a tamper-proof chain of evidence. They're separate concerns, require separate storage, different retention policies, and stricter access controls. Compliance frameworks don't care about your INFO-level request logs; they require structured, immutable audit events for every security-relevant action."

Table of Contents

  1. Compliance Requirements: SOC2, GDPR & PCI-DSS
  2. What to Log: Security-Relevant Events
  3. Audit Event Schema: Structured JSON for SIEM
  4. Spring Boot AOP Audit Logging Implementation
  5. Spring Data Envers: Database-Level Entity Audit
  6. Tamper-Proof Audit Logs: Hash Chaining & Immutability
  7. Retention Policies & PII in Audit Logs
  8. SIEM Integration: OpenSearch, Splunk & AWS CloudTrail
  9. Security Alerting from Audit Events
  10. Production Audit Logging Checklist
  11. Conclusion

1. Compliance Requirements: SOC2, GDPR & PCI-DSS

Framework Specific Requirement Retention
SOC2 Type II — CC6 Log all logical access, authentication, authorization changes, and privileged operations. Evidence must cover the entire audit period. 12 months minimum
GDPR — Article 30 Record of processing activities: who accessed personal data, when, for what purpose. Data subject rights requests (access, deletion) must be logged with outcomes. Duration of processing + legal obligation period
PCI-DSS v4 — Req 10 Log all access to cardholder data, authentication attempts, changes to accounts, use of root/admin privileges. Logs must be protected from modification (tamper-evident). 12 months (3 months immediately available)
HIPAA — 45 CFR 164.312 Hardware and software activity in systems containing ePHI. Audit controls, login monitoring, and information system activity review. 6 years

2. What to Log: Security-Relevant Events

3. Audit Event Schema: Structured JSON for SIEM

A consistent, structured schema is essential for SIEM ingestion, alerting, and forensic investigation. Every audit event should answer: who, what, when, where, why, and with what result.

{
  "eventId": "550e8400-e29b-41d4-a716-446655440000",  // UUID v4, globally unique
  "eventType": "DATA_ACCESS",                          // Enum: AUTH, AUTHZ, DATA_ACCESS, ADMIN, SYSTEM
  "action": "READ",                                    // CREATE, READ, UPDATE, DELETE, EXPORT
  "outcome": "SUCCESS",                                // SUCCESS, FAILURE, ERROR
  "timestamp": "2026-04-06T09:15:30.123Z",            // ISO 8601 UTC, millisecond precision
  "actor": {
    "userId": "usr_a1b2c3d4",                         // Never log raw email — use internal ID
    "username": "j.doe",                              // For display, not PII like full email
    "roles": ["ROLE_ANALYST"],
    "sessionId": "sess_xyz123",
    "ipAddress": "203.0.113.42",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..."
  },
  "resource": {
    "type": "Customer",
    "id": "cust_98765",
    "path": "/api/customers/98765"
  },
  "request": {
    "method": "GET",
    "correlationId": "req_abc123",                    // For distributed tracing correlation
    "serviceId": "customer-service"
  },
  "metadata": {
    "dataClassification": "PII",                      // PII, PHI, PCI, CONFIDENTIAL, PUBLIC
    "legalBasis": "LEGITIMATE_INTEREST"               // GDPR legal basis if PII involved
  },
  "integrity": {
    "previousHash": "sha256:d4e5f6...",              // For hash-chaining tamper detection
    "hash": "sha256:a1b2c3..."                       // Hash of this event including previousHash
  }
}
Security audit logging pipeline: application events to structured JSON to SIEM to compliance reporting
Security Audit Logging Architecture — from event capture to compliance reporting. Source: mdsanwarhossain.me

4. Spring Boot AOP Audit Logging Implementation

Custom @Auditable Annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
    String eventType();
    String action();
    String resourceType();
    String dataClassification() default "CONFIDENTIAL";
}

// Usage on service methods
@Auditable(eventType = "DATA_ACCESS", action = "READ",
           resourceType = "Customer", dataClassification = "PII")
public CustomerDto getCustomer(String customerId) {
    return customerRepository.findById(customerId)
        .map(customerMapper::toDto)
        .orElseThrow(() -> new ResourceNotFoundException("Customer not found: " + customerId));
}

AOP Aspect for Automatic Audit Event Capture

@Aspect
@Component
@RequiredArgsConstructor
public class AuditLoggingAspect {

    private final AuditEventPublisher auditPublisher;
    private final SecurityContextHolder securityContextHolder;

    @Around("@annotation(auditable)")
    public Object auditMethod(ProceedingJoinPoint joinPoint, Auditable auditable)
            throws Throwable {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        HttpServletRequest request = ((ServletRequestAttributes)
            RequestContextHolder.getRequestAttributes()).getRequest();

        AuditEvent.Builder eventBuilder = AuditEvent.builder()
            .eventId(UUID.randomUUID().toString())
            .eventType(auditable.eventType())
            .action(auditable.action())
            .timestamp(Instant.now())
            .actor(AuditActor.fromAuthentication(auth, request))
            .resource(AuditResource.builder()
                .type(auditable.resourceType())
                .path(request.getRequestURI())
                .build())
            .dataClassification(auditable.dataClassification());

        try {
            Object result = joinPoint.proceed();
            eventBuilder.outcome("SUCCESS");
            auditPublisher.publishAsync(eventBuilder.build());
            return result;
        } catch (Exception ex) {
            eventBuilder.outcome("FAILURE")
                        .errorMessage(ex.getMessage());
            auditPublisher.publishAsync(eventBuilder.build());
            throw ex;
        }
    }
}

Async Publisher to Dedicated Audit Log Table

@Service
@RequiredArgsConstructor
public class AuditEventPublisher {

    private final AuditLogRepository auditLogRepository;
    private final ObjectMapper objectMapper;

    @Async("auditExecutor")  // Separate thread pool — never block request thread
    public void publishAsync(AuditEvent event) {
        try {
            String json = objectMapper.writeValueAsString(event);
            AuditLogEntry entry = AuditLogEntry.builder()
                .eventId(event.getEventId())
                .eventJson(json)
                .eventType(event.getEventType())
                .timestamp(event.getTimestamp())
                .outcome(event.getOutcome())
                .build();
            auditLogRepository.save(entry);
        } catch (Exception ex) {
            // CRITICAL: Never swallow audit log failures silently
            log.error("AUDIT LOG FAILURE: failed to persist audit event {}", event.getEventId(), ex);
            // Alert via PagerDuty/Slack — audit log failure is a compliance incident
            alertingService.sendCriticalAlert("Audit log persistence failed", ex);
        }
    }
}

5. Spring Data Envers: Database-Level Entity Audit

Spring Data Envers (Hibernate Envers integration) provides automatic, database-level change tracking for JPA entities. Every INSERT, UPDATE, DELETE creates a revision entry in an audit table, giving you a complete history of every entity's state changes.

// Enable auditing on entity
@Entity
@Audited
@EntityListeners(AuditingEntityListener.class)
public class CustomerEntity {
    @Id
    private String id;

    @NotAudited  // Exclude volatile computed fields
    private LocalDateTime lastLoginAt;

    private String email;
    private String status;

    @CreatedBy private String createdBy;
    @LastModifiedBy private String modifiedBy;
    @CreatedDate private Instant createdAt;
    @LastModifiedDate private Instant modifiedAt;
}

// Query audit history
@Repository
public interface CustomerRevisionRepository
    extends RevisionRepository<CustomerEntity, String, Long> {}

// Usage: get all revisions for a customer
List<Revision<Long, CustomerEntity>> revisions =
    customerRevisionRepository.findRevisions("cust_98765").getContent();

6. Tamper-Proof Audit Logs: Hash Chaining & Immutability

PCI-DSS Requirement 10.3.3 requires that audit logs be protected against modification. Hash chaining (similar to blockchain) ensures that any modification to historical audit entries is detectable.

@Service
public class TamperProofAuditService {

    public AuditLogEntry createEntry(AuditEvent event, String previousHash) {
        String eventJson = serialize(event);
        // Hash = SHA-256(previousHash + eventJson)
        String currentHash = sha256(previousHash + eventJson);

        return AuditLogEntry.builder()
            .eventJson(eventJson)
            .previousHash(previousHash)
            .hash(currentHash)
            .build();
    }

    public boolean verifyChainIntegrity(List<AuditLogEntry> entries) {
        for (int i = 1; i < entries.size(); i++) {
            AuditLogEntry entry = entries.get(i);
            String expectedHash = sha256(entries.get(i-1).getHash() + entry.getEventJson());
            if (!expectedHash.equals(entry.getHash())) {
                log.error("AUDIT CHAIN VIOLATION at entry {}: tamper detected!", entry.getEventId());
                return false;
            }
        }
        return true;
    }
}

// Database immutability: revoke UPDATE and DELETE on audit table
REVOKE UPDATE, DELETE ON TABLE audit_log FROM application_user;
GRANT INSERT, SELECT ON TABLE audit_log TO application_user;

7. Retention Policies & PII in Audit Logs

Audit logs create a tension between two requirements: security needs logs retained for as long as possible; privacy regulations (GDPR) require deletion of personal data when no longer necessary. Resolution:

SIEM audit log pipeline: structured events to OpenSearch or Splunk, alerting and compliance dashboards
SIEM Audit Log Pipeline — from Spring Boot events to OpenSearch/Splunk dashboards. Source: mdsanwarhossain.me

8. SIEM Integration: OpenSearch, Splunk & AWS CloudTrail

Logback JSON appender for structured audit output

<!-- logback-spring.xml — separate appender for audit events -->
<appender name="AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>/var/log/app/audit.log</file>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <includeContext>false</includeContext>
        <fieldNames>
            <timestamp>@timestamp</timestamp>
            <message>message</message>
        </fieldNames>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>/var/log/app/audit.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
        <maxHistory>90</maxHistory> <!-- 90 days hot; ship older to S3 -->
    </rollingPolicy>
</appender>

<logger name="AUDIT_LOGGER" level="INFO" additivity="false">
    <appender-ref ref="AUDIT"/>
</logger>

OpenSearch Index Template for Audit Logs

PUT _index_template/audit-logs
{
  "index_patterns": ["audit-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "index.lifecycle.name": "audit-ilm-policy"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "eventType":  { "type": "keyword" },
        "action":     { "type": "keyword" },
        "outcome":    { "type": "keyword" },
        "actor.userId": { "type": "keyword" },
        "actor.ipAddress": { "type": "ip" },
        "resource.type": { "type": "keyword" },
        "dataClassification": { "type": "keyword" }
      }
    }
  }
}

9. Security Alerting from Audit Events

10. Production Audit Logging Checklist

  • All security-relevant events logged — authentication, authorization, data access, admin operations, DSR events
  • Structured JSON schema — eventId, eventType, action, outcome, actor (with IP), resource, timestamp UTC, dataClassification
  • AOP-based automatic capture — @Auditable annotation on all service methods touching sensitive data
  • Separate audit log storage — dedicated table/index/topic separate from application logs
  • Tamper-proof: INSERT-only on DB table — UPDATE and DELETE revoked from application_user
  • Hash chaining integrity verification — daily automated integrity check, alert on any chain break
  • No raw PII in actor fields — log internal user IDs; pseudonymize on GDPR erasure
  • Retention policy documented — SOC2: 12 months, PCI-DSS: 12 months, HIPAA: 6 years
  • SIEM integration working — audit events visible in OpenSearch/Splunk within 60 seconds
  • Security alerts configured — failed login threshold, privilege escalation, volume anomaly, geographic anomaly
  • Audit log failure alerting — audit persistence failure triggers PagerDuty (compliance incident)
  • Access to audit logs restricted — only security and compliance teams; all access to audit logs is itself audited

11. Conclusion

Security audit logging sits at the intersection of security, compliance, and forensics. Done correctly, it satisfies SOC2 Type II CC6 controls, GDPR Article 30 processing records, PCI-DSS Requirement 10 logging controls, and gives your incident response team the evidence trail they need to reconstruct any security event.

The implementation path is straightforward: create a structured AuditEvent schema, build an @Auditable AOP aspect to capture events automatically, persist to an INSERT-only table with hash chaining, ship to OpenSearch or Splunk via Filebeat, and configure alerting rules for the high-signal events that indicate active threats. The Java ecosystem (Spring Boot AOP, Spring Data Envers, Logstash Logback Encoder) makes this achievable in a single sprint.

Remember: audit log failure is itself a compliance incident. Monitor your audit log pipeline with the same rigor you apply to your application health checks — because an audit log that failed silently is worse than no audit log at all.

security audit logging SOC2 audit trail GDPR Article 30 PCI-DSS Requirement 10 Spring Boot AOP audit tamper-proof audit log structured logging compliance OpenSearch SIEM Spring Data Envers immutable audit trail

Leave a Comment

Related Posts

Md Sanwar Hossain - Software Engineer
Md Sanwar Hossain

Software Engineer · Java · Spring Boot · Microservices · AI/LLM Systems

All Posts
Last updated: April 6, 2026