通过 Spring AOP 从日志记录中排除一些 fields/parameters
exclusion of some fields/parameters from logging via Spring AOP
在我的 spring 项目中,我有这样一个方面 class 用于日志记录
@Aspect
@Component
public class BaseLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(BaseLoggingAspect.class);
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface NonLoggingField {
}
@Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
@Pointcut("within(img.imaginary.service.*)")
private void inServices() {
}
@Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
@Before("allPublicMethods() && inServices() || inDao()")
public void logBeforeCall(JoinPoint joinPoint) {
if (logger.isDebugEnabled()) {
logger.debug("begin method {} in {} class with arguments: {}", joinPoint.getSignature().getName(),
joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getArgs());
}
}
}
这方面简单的捕获service和dao层的所有public方法,并在开始执行时输出到log中方法名,class,以及方法参数值的大小
在这方面,我创建了一个 NonLoggingField 注释,我想将其应用到那些可以传递给这些记录方法的参数的对象的 classes 的某些字段,例如:
public class User {
@NonLoggingField
public String userEmail;
public name;
public User(String userEmail, String name) {
this.userEmail = userEmail;
this.name= name;
}
public String tiString() {
return String.format("user name: %s and his email: %s", name, userEmail);
}
}
事实是,此类对象将通过其 toString 方法写入日志,但有必要使用 notLoggingField 注释以某种方式使电子邮件不进入日志,同时我脑子里有一些想法要做通过反射,但不清楚如何在没有使用反射的复杂代码的情况下做到这一点,特别是考虑到对象内部可能有其他类型的对象,这些对象可能具有带注释的相同字段或具有此类字段的对象的集合。也许 AspectJ 库可以提供帮助,但我在其中找不到这样的机制。请帮我想出点什么
在运行时,方法参数只是一个值。此时 JVM 不知道调用者是使用常量、文字、字段还是其他方法调用的结果来调用该方法。那种信息,你只能在源代码中看到。在字节代码中,确定参数值(或对相应对象的引用)所需的任何取消引用操作或计算都在 在 调用方法之前完成。所以跟字段注解没有联系。
注释方法参数是您的替代方案吗?
如果您的要求非常具体,例如拦截来自 toString
方法和 return 虚拟值的字段访问,如果该字段被注释,那将是可能的。但这不会是 fool-proof。例如,假设 toString
调用 getter 方法而不是直接访问该字段,或者 toString
以外的方法记录该字段。您并不总是希望在读取访问时验证字段值,因为应用程序的其他部分可能依赖于它的正常工作。并非每次 toString
调用都是为了记录某些内容。
我认为你应该用另一种方式解决问题,例如通过为您使用的日志记录工具应用过滤规则。或者如果你真的想在应用程序级别解决它,你可以创建一个像
这样的接口
public interface PrivacyLogger {
String toStringSensitive();
}
并使每个包含敏感信息的 class 实现该接口。然后,日志记录方面可以为每个打印的对象确定它是否是 instanceof toStringSensitive()
。如果是这样,它将记录 toStringSensitive()
而不是 toString()
的结果,即在最简单的情况下类似于
Object toBeLogged = whatever();
logger.log(
toBeLogged instanceof PrivacyLogger
? ((PrivacyLogger) toBeLogged).toStringSensitive()
: toBeLogged
);
当然,您需要遍历 getArgs()
并为每个对象确定正确的日志字符串。可能您想为整个参数数组编写一个实用程序方法。
此外,在复杂的 class 中,toStringSensitive()
实现当然也应该检查它自己的字段是否是 PrivacyLogger
实例,在这种情况下折叠它们的响应值 toStringSensitive()
方法到它自己,以便它递归工作。
很抱歉,我没有更好的消息告诉您,但隐私也是需要从头开始构建到应用程序中的东西。没有简单的 fool-proof 方法可以从一个小方面做到这一点。切面可以利用现有的应用基础设施,避免分散和纠结,但它不能自己决定什么需要禁止记录,什么不可以。
在我的 spring 项目中,我有这样一个方面 class 用于日志记录
@Aspect
@Component
public class BaseLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(BaseLoggingAspect.class);
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface NonLoggingField {
}
@Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
@Pointcut("within(img.imaginary.service.*)")
private void inServices() {
}
@Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
@Before("allPublicMethods() && inServices() || inDao()")
public void logBeforeCall(JoinPoint joinPoint) {
if (logger.isDebugEnabled()) {
logger.debug("begin method {} in {} class with arguments: {}", joinPoint.getSignature().getName(),
joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getArgs());
}
}
}
这方面简单的捕获service和dao层的所有public方法,并在开始执行时输出到log中方法名,class,以及方法参数值的大小
在这方面,我创建了一个 NonLoggingField 注释,我想将其应用到那些可以传递给这些记录方法的参数的对象的 classes 的某些字段,例如:
public class User {
@NonLoggingField
public String userEmail;
public name;
public User(String userEmail, String name) {
this.userEmail = userEmail;
this.name= name;
}
public String tiString() {
return String.format("user name: %s and his email: %s", name, userEmail);
}
}
事实是,此类对象将通过其 toString 方法写入日志,但有必要使用 notLoggingField 注释以某种方式使电子邮件不进入日志,同时我脑子里有一些想法要做通过反射,但不清楚如何在没有使用反射的复杂代码的情况下做到这一点,特别是考虑到对象内部可能有其他类型的对象,这些对象可能具有带注释的相同字段或具有此类字段的对象的集合。也许 AspectJ 库可以提供帮助,但我在其中找不到这样的机制。请帮我想出点什么
在运行时,方法参数只是一个值。此时 JVM 不知道调用者是使用常量、文字、字段还是其他方法调用的结果来调用该方法。那种信息,你只能在源代码中看到。在字节代码中,确定参数值(或对相应对象的引用)所需的任何取消引用操作或计算都在 在 调用方法之前完成。所以跟字段注解没有联系。
注释方法参数是您的替代方案吗?
如果您的要求非常具体,例如拦截来自 toString
方法和 return 虚拟值的字段访问,如果该字段被注释,那将是可能的。但这不会是 fool-proof。例如,假设 toString
调用 getter 方法而不是直接访问该字段,或者 toString
以外的方法记录该字段。您并不总是希望在读取访问时验证字段值,因为应用程序的其他部分可能依赖于它的正常工作。并非每次 toString
调用都是为了记录某些内容。
我认为你应该用另一种方式解决问题,例如通过为您使用的日志记录工具应用过滤规则。或者如果你真的想在应用程序级别解决它,你可以创建一个像
这样的接口public interface PrivacyLogger {
String toStringSensitive();
}
并使每个包含敏感信息的 class 实现该接口。然后,日志记录方面可以为每个打印的对象确定它是否是 instanceof toStringSensitive()
。如果是这样,它将记录 toStringSensitive()
而不是 toString()
的结果,即在最简单的情况下类似于
Object toBeLogged = whatever();
logger.log(
toBeLogged instanceof PrivacyLogger
? ((PrivacyLogger) toBeLogged).toStringSensitive()
: toBeLogged
);
当然,您需要遍历 getArgs()
并为每个对象确定正确的日志字符串。可能您想为整个参数数组编写一个实用程序方法。
此外,在复杂的 class 中,toStringSensitive()
实现当然也应该检查它自己的字段是否是 PrivacyLogger
实例,在这种情况下折叠它们的响应值 toStringSensitive()
方法到它自己,以便它递归工作。
很抱歉,我没有更好的消息告诉您,但隐私也是需要从头开始构建到应用程序中的东西。没有简单的 fool-proof 方法可以从一个小方面做到这一点。切面可以利用现有的应用基础设施,避免分散和纠结,但它不能自己决定什么需要禁止记录,什么不可以。