Log Desensitization Practices and APT‑Based Automatic toString Generation in Java
This article describes how ZhaiZhai implements log desensitization for personal data protection using a reusable Java utility class, JSON serializer customizations with Jackson, and an annotation‑processing tool that automatically generates masked toString methods, while also discussing operational challenges and future plans.
In recent years, China has enacted laws such as the Cybersecurity Law and the Personal Information Protection Law, prompting enterprises to protect sensitive data in logs. The article introduces ZhaiZhai's approach to log desensitization, aiming to mask phone numbers, ID numbers, bank cards, and other personal information.
The objectives are to establish a sustainable mechanism for controlling sensitive log data, with measures that include detecting services containing sensitive logs, developing low‑cost desensitization tools, driving business iteration, and maintaining long‑term monitoring.
Implementation starts with a Java utility class DesensitizeUtil offering methods like desensitize(String input) , desensitizeByInputLength(String input) , desensitizePhoneNumber(String phoneNumber) , desensitizeIDCard(String idCard) , and desensitizeBankCardNumber(String bankCardNumber) . Sample code:
public final class DesensitizeUtil {
/**
* 根据字符串长度匹配不同的脱敏函数, 强制脱敏
*/
public static String desensitizeByInputLength(String input) {
int length = input.length();
// 手机号
if (length == 11) {
return desensitizePhoneNumber(input);
}
// ...
}
/**
* 脱敏手机号, 前3位和后4位,中间的数字用*代替
*/
public static String desensitizePhoneNumber(String phoneNumber) {
if (phoneNumber.length() == 11) {
return phoneNumber.substring(0, phoneNumber.length() - 8) + "****" + phoneNumber.substring(phoneNumber.length() - 4);
}
return phoneNumber;
}
// 省略其他脱敏函数...
}For JSON logs, a custom Jackson annotation @Desensitize is defined, and a JacksonDesensitizeSerializerModifier modifies field serialization based on this annotation. The JsonUtil class registers the modifier and provides object2DesensitizeString to produce automatically masked JSON strings.
public @interface Desensitize {}
public class JacksonDesensitizeSerializerModifier extends BeanSerializerModifier {
@Override
public List
changeProperties(SerializationConfig config, BeanDescription beanDesc,
List
beanProperties) {
for (BeanPropertyWriter beanProperty : beanProperties) {
Desensitize desensitize = beanProperty.getAnnotation(Desensitize.class);
if (desensitize != null) {
beanProperty.assignSerializer(new Desensitization());
}
}
return beanProperties;
}
// ...
}
public class JsonUtil {
private static final ObjectMapper DESENSITIZE_OBJECT_MAPPER = newObjectMapper();
private static ObjectMapper newObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("SimpleModuleDesensitize");
simpleModule.setSerializerModifier(new JacksonDesensitizeSerializerModifier());
mapper.registerModule(simpleModule);
return mapper;
}
public static
String object2DesensitizeString(T object) throws JsonProcessingException {
return DESENSITIZE_OBJECT_MAPPER.writeValueAsString(object);
}
}To reduce manual effort, an annotation‑processing tool (APT) similar to Lombok is introduced. It automatically generates a masked toString() method for classes that lack one, respecting the @Desensitize annotation. The generated method uses StringJoiner and calls DesensitizeUtil.desensitizeByInputLength for annotated fields.
class User {
private String username;
@Desensitize
private String password;
public String toString() {
StringJoiner sj = new StringJoiner(", ", "User[", "]");
if (this.username != null) {
sj.add("username=" + this.username);
}
if (this.password != null) {
sj.add("password=" + DesensitizeUtil.desensitizeByInputLength(password));
}
return sj.toString();
}
}Operational challenges encountered include OOM caused by automatically generated toString() for Spring beans with large local caches, leading to a decision to skip APT generation for Spring beans, and serialization issues where the generated toString() altered class signatures, affecting serialVersionUID and causing deserialization failures.
The article also mentions a deprecated approach of globally intercepting log output with regex, which was rejected due to performance and false‑positive concerns.
Future planning involves building a dependency‑management system to automate jar version updates, testing, gray releases, and rollbacks, acknowledging that this is more of an operational task than a pure technical problem.
In summary, the multi‑stage log desensitization effort—from tool development to JSON handling and APT automation—highlights the importance of balancing security requirements, developer productivity, and system stability while promoting a culture of proactive problem solving.
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.