⏰ Reading Time: 5 minutes
Ensuring data privacy in logs is crucial for compliance, security, and user trust. This guide explains how to mask and sanitise data in Java logs using Logback, Log4j, and Java Util Logging (JUL). We’ll include both configuration-based and custom code implementations, providing examples of masked log outputs and discussing best practices.
Why Mask or Sanitise Logs?
- Compliance: Align with regulations like GDPR, CCPA, PCI-DSS.
- Security: Mitigate data breach risks.
- Privacy: Maintain user trust by protecting sensitive information.
Logback (Spring Boot 2.x)
Dependencies
Maven:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
Gradle:
implementation 'ch.qos.logback:logback-classic:1.2.11'
Configuration-Based Masking
Logback allows integration with filters for data sanitisation.
logback.xml Configuration:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
<post-encoder>
<filter class="com.example.logging.DataMaskingFilter"/>
</post-encoder>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Custom Data Masking Filter
For more tailored masking logic, create a custom filter:
Java Implementation:
public class DataMaskingFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
String maskedMessage = maskSensitiveData(event.getFormattedMessage());
event.getLoggerContextVO().getPropertyMap().put("formattedMessage", maskedMessage);
return FilterReply.NEUTRAL;
}
private String maskSensitiveData(String message) {
return message.replaceAll("\b\d{4}-?\d{4}-?\d{4}-?(\d{4})\b", "**** **** **** $1");
}
}
Example Log Output:
- Before:
User's credit card: 1234-5678-9101-1121
- After:
User's credit card: **** **** **** 1121
Log4j
Dependencies
Maven:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
Gradle:
implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
Configuration-Based Masking
Log4j supports configuration-based solutions for sanitisation.
log4j2.xml Configuration:
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%t] %-5level %logger{36} - %msg%n">
<RegexFilter regex="(\d{4}-?\d{4}-?\d{4}-?(\d{4}))" replacement="**** **** **** $2" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Custom DataMaskingLayout
For advanced masking needs, implement a custom layout:
Java Implementation:
public class DataMaskingLayout extends AbstractStringLayout {
protected DataMaskingLayout(Charset charset) {
super(charset);
}
@Override
public String toSerializable(LogEvent event) {
String message = event.getMessage().getFormattedMessage();
return maskSensitiveData(message);
}
private String maskSensitiveData(String message) {
return message.replaceAll("\b\d{3}-?\d{2}-?\d{4}\b", "***-**-$1");
}
}
Configure the Custom Layout in Log4j
To use the custom DataMaskingLayout
class in your Log4j configuration, you need to declare it in the log4j2.xml
file:
log4j2.xml Configuration:
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<com.example.logging.DataMaskingLayout charset="UTF-8"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Example Log Output:
- Before:
SSN: 123-45-6789
- After:
SSN: ***-**-6789
JUL (Java Util Logging)
Custom Formatter Example
JUL supports custom formatters for data masking.
Java Implementation:
public class DataMaskingFormatter extends Formatter {
@Override
public String format(LogRecord record) {
String message = record.getMessage();
return maskSensitiveData(message);
}
private String maskSensitiveData(String message) {
return message.replaceAll("\b\d{4}-?\d{4}-?\d{4}-?(\d{4})\b", "**** **** **** $1");
}
}
Configuration
To use the custom DataMaskingFormatter
, configure JUL by updating the logging.properties
file as follows:
logging.properties Configuration:
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=com.example.logging.DataMaskingFormatter
Example Log Output:
- Before:
Customer's card: 5678-9101-1121-3145
- After:
Customer's card: **** **** **** 3145
Common Regex Patterns By Region
Here’s a reference table that includes sensitive documents and phone numbers for the US, UK, Canada, and Australia. The table is organised by region and data type:
Region | Sensitive Data Type | Regex Pattern | Example Mask |
---|---|---|---|
Global | Credit Card Number | \b(?:\d[ -]*?){13,16}\b | **** **** **** 1234 |
Global | Bank Account Number | \b\d{8,17}\b | |
Global | IBAN | \b[A-Z]{2}\d{2}[A-Z0-9]{1,30}\b | **** **** **** 1234 |
Global | Email Address | \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b | ****@example.com |
Global | Passport Number | \b[A-Z0-9]{6,9}\b | * |
United States | Social Security Number (SSN) | \b\d{3}-\d{2}-\d{4}\b | -*- |
United States | Driver’s License | \b[A-Z0-9]{1,12}\b | |
United States | Phone Number | \(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4} | (***) **–*** |
United States | Routing Number | \b\d{9}\b | * |
United States | Health Insurance Number | \b[A-Z0-9]{8,15}\b | |
United Kingdom | National Insurance Number (NIN) | \b[A-CEGHJPR-TW-Z]{2}\d{6}[A-D]\b | ** * |
United Kingdom | Passport Number | \b\d{9}\b | * |
United Kingdom | Driver’s License | \b[A-Z9]{5}\d{6}[A-Z]{2}\b | *** |
United Kingdom | Phone Number (Mobile) | \b07\d{9}\b | 07* |
United Kingdom | Phone Number (Landline) | \b01\d{8,9}\b | 01 |
Canada | Social Insurance Number (SIN) | \b\d{3}-\d{3}-\d{3}\b | –-*** |
Canada | Passport Number | \b[A-Z]{2}\d{6}\b | |
Canada | Driver’s License | \b[A-Z0-9]{5,12}\b | |
Canada | Phone Number (Mobile) | \b\d{3}-\d{3}-\d{4}\b | –-**** |
Canada | Phone Number (Landline) | \b\d{3}-\d{3}-\d{4}\b | –-**** |
Australia | TFN | \b\d{3} \d{3} \d{3}\b | *** *** *** |
Australia | Passport Number | \b[A-Z]{2}\d{7}\b | * |
Australia | Driver’s License | \b[A-Z0-9]{6,10}\b | ** |
Australia | Phone Number (Mobile) | \b04\d{8}\b | 04 |
Australia | Phone Number (Landline) | \b\d{2} \d{4} \d{4}\b | ** **** **** |
Global | IP Address (IPv4) | \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b | ..***.123 |
Implementing log sanitisation or masking helps ensure your Java applications remain compliant and secure.