日志组件

背景

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);
    }
}

参考资料

posted @   向着朝阳  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示