-
Notifications
You must be signed in to change notification settings - Fork 0
Audit log
Introducing Audit logging support. Audit logging allows to create a singular log entry about a request. Audit logging is flexible.
CAUTION! Given feature can produce alot of data in logs, please be considerate. Consider separating these logs to separate log storage with low retention. Enable this feature for environments that really need it.
If default logging adapter is used, logs are separated into 2 distinct logs entries. First one is an INFO level and will contain all the data except for request payload and response payload. Request payload and response body are included into second log entry, which is DEBUG level. This helps to avoid excessive data flow into the logging stream. In order to enable debug log entry, set logger "AuditLogger" to DEBUG level.
In order to enable the feature, following property should be added to application.properties file.
ee.bitweb.core.audit.auto-configuration=true
Library does not include any transitive dependencies, thus in order to use this feature, following dependencies should be included:
implementation 'org.springframework.boot:spring-boot-starter-web'
| Property | Default value | Description |
|---|---|---|
| ee.bitweb.core.audit.auto-configuration | false | Enables audit log autoconfiguration. Adds automatically filter to filter chain. |
| ee.bitweb.core.audit.maxLoggableResponseSize | 3072 | Maximum amount (in bytes) of response body to log. Can be helpful to reduce the amount of data logged. |
| ee.bitweb.core.audit.maxLoggableRequestSize | 3072 | Maximum amount (in bytes) of request payload to log. |
| ee.bitweb.core.audit.requestHeaders | User-Agent, Origin | List of request headers to include in audit log. |
| ee.bitweb.core.audit.responseHeaders | List of response headers to include in audit log. | |
| ee.bitweb.core.audit.sensitiveHeaders | Authorization | List of headers, whose value is considered confidential. These headers will be obfuscated. |
| ee.bitweb.core.audit.mappers | forwarded, request_headers, method, url, request_body, response_body, response_status, trace_id | List of data mappers to include by default. |
| ee.bitweb.core.audit.blacklist | /actuator/ | List of paths to not generate audit log for. This is useful to exclude some requests to reduce noise. |
| ee.bitweb.core.audit.includeDuration | true | Include duration of the request in the log entry |
Audit log logs specific pieces of information. These are implemented with implementations of ee.bitweb.core.audit.mappers.AuditLogDataMapper interface. There is a list of default mappers available and in use.
| Name | Description | Level |
|---|---|---|
| ee.bitweb.core.audit.mappers.RequestBodyMapper | Logs request body up to ee.bitweb.core.audit.maxLoggableRequestSize | DEBUG |
| ee.bitweb.core.audit.mappers.RequestForwardingDataMapper | Logs request forwarding headers | INFO |
| ee.bitweb.core.audit.mappers.RequestHeadersMapper | Logs headers. Can control with properties: ee.bitweb.core.audit.requestHeaders, ee.bitweb.core.audit.responseHeaders, ee.bitweb.core.audit.sensitiveHeaders | INFO |
| ee.bitweb.core.audit.mappers.RequestMethodMapper | Logs request method | INFO |
| ee.bitweb.core.audit.mappers.RequestUrlDataMapper | Logs request URL with query parameters | INFO |
| ee.bitweb.core.audit.mappers.ResponseBodyMapper | Logs response body up to ee.bitweb.core.audit.maxLoggableRequestSize | DEBUG |
| ee.bitweb.core.audit.mappers.ResponseStatusMapper | Logs response status code | INFO |
| ee.bitweb.core.audit.mappers.TraceIdMapper | Logs the requests Trace ID | INFO, DEBUG |
If you want to add a custom field to the audit log, you can do so by defining own bean which implements interface ee.bitweb.core.audit.mappers.AuditLogDataMapper.
When doing so, be considerate of the placement of audit log filter in filterchain. Other filter’s might affect the access to data.
Here is an example that will extract user’s person code from jwt in the request header and add person code to audit log entry, allowing to map out who is making a request.
@Component
@RequiredArgsConstructor
public class PersonCodeDataMapper implements AuditLogDataMapper {
public static final String KEY = "person_code";
private final JwtProcessor processor;
private final AuthenticationProperties properties;
private final ValidateTokenFeature validateTokenFeature;
@Override
public String getValue(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
String token = httpServletRequest.getHeader(properties.getAccessTokenHeaderName());
if (token != null && validateTokenFeature.isValid(token)) {
return processor.getSubject(token);
}
return null;
}
@Override
public String getKey() {
return KEY;
}
}By default audit log entries are sent to logger, which will consolidate them with other logs. If you wish to implement your own writer to send these logs to database or MQ or some other solution, you can do so by defining a bean of type ee.bitweb.core.audit.writers.AuditLogWriteAdapter. It will override the default solution.
As an example, you can observe the default writer:
public class AuditLogLoggerWriterAdapter implements AuditLogWriteAdapter {
public static final String AUDIT = "audit";
@Override
public void write(Map<String, String> container) {
Map<String, String> currentContext = MDC.getCopyOfContextMap();
container.put(AUDIT, "1");
MDC.setContextMap(container);
log.info(
"{} {} {} {} ms",
get(container, RequestMethodMapper.KEY),
get(container, RequestUrlDataMapper.KEY),
get(container, ResponseStatusMapper.KEY),
get(container, AuditLogFilter.DURATION_KEY));
if (currentContext != null) {
MDC.setContextMap(currentContext);
}
}
private String get(Map<String, String> container, String key) {
if (container.containsKey(key)) {
return container.get(key);
}
return "UNKNOWN";
}
}By default request and response bodies will not be included in log entry. In order to add these as a separate log entry, add following property to configuration:
logging.level.AuditLogger=DEBUG
If previously response body content size exceeded limit, it would not log the content, rather only metadata. Now content will be logged up to the threshold along with metadata.