ArchUnit测试实现:禁止Log4j打印带特定注解方法的返回值
场景说明
假设存在如下Person类,其getAccountNumber()方法带有自定义注解@ShouldNotBeLogged:
public class Person { private String name; private String accountNumber; @ShouldNotBeLogged public String getAccountNumber() { return accountNumber; } }
现需要编写ArchUnit测试规则,实现如下校验逻辑:所有声明了org.apache.logging.log4j.Logger类型静态字段的类,不得在日志中打印带有@ShouldNotBeLogged注解的方法的返回值。
如下方
HelloWorld类的logMessage方法就属于违规场景,应当被ArchUnit测试识别上报:该方法通过日志打印了Person对象带@ShouldNotBeLogged注解的getAccountNumber()方法的返回值。
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class HelloWorld { private static final Logger logger = LogManager.getLogger(HelloWorld.class); public void logMessage(Person person) { logger.debug("logging Acc No. - {}", person.getAccountNumber()); } }
规则实现
你可以直接通过ArchUnit的方法调用匹配能力实现上述校验,核心逻辑是定位所有持有Log4j静态Logger的类,拦截这些类中所有Logger实例的日志方法调用,检查传入参数是否包含被@ShouldNotBeLogged标记的方法返回值,完整测试代码如下:
import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.lang.ArchRule; import org.junit.jupiter.api.Test; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; public class SensitiveLogCheckTest { // 替换为你自己项目需要扫描的根包路径 private static final JavaClasses CLASSES = new ClassFileImporter() .importPackages("cn.your.project.base.package"); @Test void forbid_logging_sensitive_data_marked_by_annotation() { ArchRule sensitiveLogRule = noClasses() // 筛选所有声明了Log4j Logger静态字段的类 .that().haveDeclaredFields(field -> field.getRawType().isAssignableTo(org.apache.logging.log4j.Logger.class) && field.isStatic() ) // 校验这类类中不允许出现违规的方法调用 .should().callMethodWhere(call -> // 匹配所有对Logger实例的方法调用 call.getTargetOwner().isAssignableTo(org.apache.logging.log4j.Logger.class) // 检查调用参数中是否存在被@ShouldNotBeLogged标记的方法调用 .and(call.getArguments().stream() .anyMatch(argAccess -> argAccess.getTarget().isAnnotatedWith(ShouldNotBeLogged.class) )) ) .because("日志中禁止输出标记了@ShouldNotBeLogged的敏感信息,避免数据泄露"); sensitiveLogRule.check(CLASSES); } }
补充说明
- 上述规则默认覆盖Logger的所有级别日志方法(debug/info/warn/error/trace/fatal等),如果需要单独限制特定日志级别,可以在方法调用匹配逻辑中追加方法名判断
- 如果需要支持Slf4j、Logback等其他日志框架,只需要修改
getTargetOwner匹配的Logger类型为对应框架的Logger全类名即可 - 该规则可以识别直接在日志参数中调用敏感方法的场景,也就是示例中
HelloWorld类的违规写法;如果需要覆盖先把敏感方法返回值存入局部变量/成员变量后再传入日志的场景,可以搭配ArchUnit的数据流分析扩展实现,常规业务场景下当前规则已经可以覆盖90%以上的违规写法
内容的提问来源于stack exchange,提问作者Mohit Charaya
相关产品推荐
相关产品推荐

