日志组件
目录
背景
JAVA项目,平时需要打印日志方便诊断,如果都开发自己打印很繁琐,如果封装组件会遇到以下问题
1 日志量很大,特别是有些批量查询接口会返回大量的日志
2 安全合规要求敏感数据不能落盘,需要脱敏才能落盘
实现思路
1 自定义注解
通过自定义注解,灵活控制哪些接口需要采集日志,以及采集哪些参数和返回值,从而减少不必要的日志记录。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLog {
// 是否记录请求参数
boolean logRequest() default true;
// 是否记录响应结果
boolean logResponse() default true;
// 如 @Loggable(requestValues = "$.name") , 请求对象的name属性需要打印日志
String[] requestValues() default {};
String[] responseValues() default {};
}
2 定义敏感字段的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SensitiveInfo {
}
自定义类,敏感字段的属性使用 @SensitiveInfo 注解
class User {
private String name;
@SensitiveInfo
private String idCard;
@SensitiveInfo
private String phone;
}
3 通过Spring AOP对接口做拦截。根据Loggable 注解,只打印需要采集字段的日志。
json path用法参考 https://www.cnblogs.com/aibi1/p/18728942
@Aspect
@Component
public class LogAspect {
private final ObjectMapper om = new ObjectMapper();
@PostConstruct
public void init() {
om.registerModule(new JavaTimeModule());
}
@Before("execution(public * com.example.demo..*(..)) && @annotation(apiLog)")
public void logMethod(JoinPoint joinPoint, ApiLog apiLog) {
try {
//获取方法入参的类型列表
List<Object> args = Arrays.asList(joinPoint.getArgs());
if (apiLog.logAll()) {
List<String> logValues = args.stream().map(this::obj2Str).collect(Collectors.toList());
return;
}
List<String> allStringArgValues = args.stream().filter(String.class::isInstance)
.map(String.class::cast)
.collect(Collectors.toList());
List<String> allJsonPathValues = Arrays.stream(apiLog.value())
.map(logPath -> findLogValue(logPath, args))
.collect(Collectors.toList());
allStringArgValues.addAll(allJsonPathValues);
// getRequestHeaderInfoPrintLog(joinPoint, summary);
log.info(":" + summary + ":" + allStringArgValues);
} catch (Exception ex) {
log.info("{}", joinPoint.getSignature().getName());
}
}
private String findLogValue(String logPath, List<Object> args) {
return args.stream()
.filter(arg -> logPath.startsWith("$") && JSONPath.contains(arg, logPath))
.map(arg -> (String) JSONPath.extract(obj2Str(arg), logPath))
.findFirst()
.orElse("");
}
private String obj2Str(Object obj) {
try {
return om.writeValueAsString(obj);
} catch (JsonProcessingException e) {
return "";
}
}
}
4 对象转String,针对敏感类属性做脱敏代码示例
// 定义脱敏序列化过滤器
class SensitiveInfoFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
try {
java.lang.reflect.Field field = object.getClass().getDeclaredField(name);
if (field.isAnnotationPresent(SensitiveInfo.class) && value instanceof String) {
String str = (String) value;
if (str.length() > 4) {
// 简单的脱敏处理,保留前两位和后两位
return str.substring(0, 2) + "****" + str.substring(str.length() - 2);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return value;
}
}
public class Main {
public static void main(String[] args) {
User user = new User("张三", "123456789012345678", "13800138000");
// 创建脱敏序列化过滤器实例
SensitiveInfoFilter filter = new SensitiveInfoFilter();
// 使用过滤器将对象转换为 JSON 字符串
String jsonString = JSON.toJSONString(user, filter);
System.out.println(jsonString);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南