如何使用Logging Aspect隐藏日志中特定字段的值?
解决方案:日志中隐藏特定字段值
方法一:直接修改原对象(需注意业务影响)
如果入参允许被修改,可在你代码的注释位置添加字段隐藏逻辑,比如将冗余字段设为null或占位符:
public void beforeAdvice(final JoinPoint joinPoint) { final String className = joinPoint.getTarget().getClass().getSimpleName(); final String methodName = joinPoint.getSignature().getName(); final List<Object> args = Arrays.asList(joinPoint.getArgs()); args.stream().forEach(a -> { if (a.getClass().equals(CreateBlogRequest.class)) { ((CreateBlogRequest) a).getSections().stream().forEach(s -> { if(s.getSectionType().equals("image")) { // 隐藏image类型的特定字段,比如设为占位符或null s.setImageUrl("[HIDDEN]"); // 若要移除整个section,需改用迭代器操作(stream中不能直接remove) } }); } }); log.info("Invoking: " + className + "." + methodName + " with args:" + args); }
⚠️ 注意:修改原对象可能影响后续业务逻辑,仅在确认入参无需保留原始值时使用。
方法二:创建对象副本修改(不影响原业务,推荐)
为避免修改原请求对象,可创建副本处理后再输出日志:
import java.util.stream.Collectors; public void beforeAdvice(final JoinPoint joinPoint) { final String className = joinPoint.getTarget().getClass().getSimpleName(); final String methodName = joinPoint.getSignature().getName(); final List<Object> args = Arrays.asList(joinPoint.getArgs()); // 生成用于日志的参数副本,不修改原业务对象 List<Object> logArgs = args.stream().map(arg -> { if (arg.getClass().equals(CreateBlogRequest.class)) { CreateBlogRequest originalReq = (CreateBlogRequest) arg; // 深拷贝请求对象(复杂对象可使用Jackson序列化/反序列化实现深拷贝) CreateBlogRequest logReq = new CreateBlogRequest(); // 复制其他业务字段 logReq.setTitle(originalReq.getTitle()); logReq.setAuthor(originalReq.getAuthor()); // 处理sections列表 List<Section> logSections = originalReq.getSections().stream().map(section -> { Section logSection = new Section(); logSection.setSectionType(section.getSectionType()); if (!"image".equals(section.getSectionType())) { // 非image类型保留完整字段 logSection.setContent(section.getContent()); logSection.setImageUrl(section.getImageUrl()); } else { // image类型隐藏指定字段 logSection.setImageUrl("[HIDDEN]"); } return logSection; }).collect(Collectors.toList()); logReq.setSections(logSections); return logReq; } return arg; }).collect(Collectors.toList()); log.info("Invoking: " + className + "." + methodName + " with args:" + logArgs); }
方法三:自定义序列化规则(优雅持久化方案)
通过Jackson注解自定义序列化逻辑,无需在切面中编写业务处理代码:
- 在
Section类中添加自定义序列化器:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; public class Section { private String sectionType; private String imageUrl; private String content; // 为imageUrl字段绑定自定义序列化器 @JsonSerialize(using = SectionImageUrlSerializer.class) public String getImageUrl() { return imageUrl; } // 自定义序列化逻辑 public static class SectionImageUrlSerializer extends JsonSerializer<String> { @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { Section currentSection = (Section) gen.getCurrentValue(); if ("image".equals(currentSection.getSectionType())) { gen.writeString("[HIDDEN]"); } else { gen.writeString(value); } } } // 其他getter/setter方法 }
- 日志输出时使用Jackson序列化对象,自动触发隐藏逻辑:
log.info("Invoking: {} with args: {}", className + "." + methodName, new ObjectMapper().writeValueAsString(logArgs));
内容的提问来源于stack exchange,提问作者Clancinio
相关产品推荐
相关产品推荐

