Overview

ReadAudit is a feature where read access is logged for auditing purposes. You can annotation entity beans with @ReadAudit and then read events on these beans (queries and hits in L2 cache) are logged.

An implementation of the ReadAuditPrepare interface is typically required. The readAuditPrepare.prepare() method is expected to populate the ReadEvent with user context information (user id, user ip address etc).

Limitations

SqlQuery queries are currently not logged to the read audit log (RawSql queries are included in read auditing).

Getting started

Step 1: Add @ReadAudit

Add @ReadAudit annotation to all the entity beans that should have read auditing.

@ReadAudit
@Entity
@Table(name = "customer")
public class Customer {
...

Step 2: Implement ReadAuditPrepare

If you skip this step and don't supply a ReadAuditPrepare implementation a 'no op' implementation is used and the user context information (user id, user ip address etc) is left unpopulated.

class MyReadAuditPrepare implements ReadAuditPrepare {

  @Override
  public void prepare(ReadEvent event) {

    // get user context information typically from a
    // ThreadLocal or similar mechanism

    String currentUserId = ...;
    event.setUserId(currentUserId);

    String userIpAddress = ...;
    event.setUserIpAddress(userIpAddress);

    event.setSource("myApplicationName");

    // add arbitrary user context information to the
    // userContext map
    event.getUserContext().put("some", "thing");
  }
}

Step 3: Register ReadAuditPrepare implementation

The implementation of ReadAuditPrepare can be automatically detected if classpath scanning is on (just like entity beans are found etc). That is, if scanning is on you don't need to explicitly register the ReadAuditPrepare implementation and instead it will be found and instantiated.

If scanning is not used or the ReadAuditPrepare implementation has dependencies and its instantiation should be performed externally to Ebean then you can register it explicitly with the DatabaseConfig.

// example code explicitly registering the ReadAuditPrepare implementation

MyReadAuditPrepare readAuditPrepare = ...;

DatabaseConfig config = new DatabaseConfig();
...

// register explicitly here
config.setReadAuditPrepare(readAuditPrepare);


...
Database database = DatabaseFactory.create(config);

Step 4: Configure logging

The default implementation of ReadAuditLogger logs query plan entries to io.ebean.ReadAuditQuery and read events to io.ebean.ReadAudit. The query plans contain the full SQL and having these logged separately means that the read events don't need to include the full SQL executed and instead the bean type and query key can be used to reference/lookup the associated SQL. This reduces the bulk/size of the read event logs.

Below in logback xml configuration is 2 appenders. READAUDIT_QUERY_LOG for logging the query plans and READAUDIT_LOG for logging the read bean events.

<!-- LOGBACK configuration: separate loggers for the read auditing -->

<appender name="READAUDIT_QUERY_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <File>log/readAuditQuery.log</File>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <FileNamePattern>log/readAuditQuery.log.%d{yyyy-MM-dd}</FileNamePattern>
    <MaxHistory>90</MaxHistory>
  </rollingPolicy>
  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%d{HH:mm:ss.SSS} %msg%n</pattern>
  </encoder>
</appender>

<appender name="READAUDIT_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <File>log/readAudit.log</File>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <FileNamePattern>log/readAudit.log.%d{yyyy-MM-dd}</FileNamePattern>
    <MaxHistory>90</MaxHistory>
  </rollingPolicy>
  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%d{HH:mm:ss.SSS} %msg%n</pattern>
  </encoder>
</appender>


<logger name="io.ebean.ReadAuditQuery" level="TRACE" additivity="false">
  <appender-ref ref="READAUDIT_QUERY_LOG"/>
</logger>

<logger name="io.ebean.ReadAudit" level="TRACE" additivity="false">
  <appender-ref ref="READAUDIT_LOG"/>
</logger>

Optional: ReadAuditLogger implementation

If the default logging does not suit you can implement ReadAuditLogger to control how the events are logged. Log to a message queue, direct to a data store etc.

Query.setDisableReadAuditing()

For a specific query you can explicitly exclude it from the read auditing. The typical use case for this is where the query is used internally in the application to populate a cache or process bulk data and where you don't want that to go into the read audit log.