log4j 如何为slf4j logger创建方面切入点?

ntjbwcob  于 2023-11-18  发布在  其他
关注(0)|答案(2)|浏览(197)

我想添加字符串到我所有的日志来实现这一点,我计划使用aop,但我不能为我所有的logger对象声明切入点。

Logger logger = LoggerFactory.getLogger(InterviewService.class);
logger.error(ex.getMessage(),ex);

字符串


的数据
我试图在ss中拦截logger,所以在打印日志之前,我可以改变消息中的参数并添加我的文本,我稍微改变了我的代码,目前我可以捕获抛出的异常,但仍然不能在方面方法中拦截logger.error(“一些错误日志”)消息。

@Pointcut("within(org.apache.logging.log4j.*)")
public void logPointcut() {}

@Pointcut("within(*..Log4jLogger.*)")
public void logPointcuts() {}

@Pointcut("execution(* *.error(*))")
public void logPointcutsMethod() {}

@Pointcut("within(*.error)")
public void logPointcutsMethodw() {}

@Around("logPointcutsMethod() || logPointcuts() || logPointcut() || logPointcutsMethodw()")
    public Object logError(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        String user = "";
        if (state)
            user = getUser();
        logger.error(
            "Exception in {} User :{}.{}() with cause = {}", 
            joinPoint.getSignature().getDeclaringTypeName(),
            user,
            joinPoint.getSignature().getName()
        );
        return result;
    }


总而言之,我想为每个logger.error调用创建切入点,这样我就可以将字符串添加到每条消息中

laawzig2

laawzig21#

这里有一个完整的MCVE,用于如何在原生的Apache J中解决这个问题,使用Apache J编译器的 inpath 上的Slf 4J和Log4J库使用加载时编织或编译时编织。因为Spring AOP只能拦截Spring beans/组件内的方法,但Slf 4j和Log4j都不是Spring组件,所以您需要一个面向成人的AOP框架,不是像Spring AOP那样的AOP lite解决方案。幸运的是,Spring有一个非常好的AOP J集成,并且在它的手册中也解释了它。

package de.scrum_master.app;

import org.apache.log4j.BasicConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Application {
  private static final Logger logger = LoggerFactory.getLogger(Application.class);

  public static void main(String[] args) {
    BasicConfigurator.configure();
    logger.info("Hello Slf4j");
    logger.error("uh-oh", new Exception("oops!"));
    logger.error("WTF dude?!?");
  }
}

个字符
控制台日志:

0 [main] INFO de.scrum_master.app.Application  - Hello Slf4j
7 [main] ERROR de.scrum_master.app.Application  - [MY PREFIX] uh-oh
java.lang.Exception: oops!
    at de.scrum_master.app.Application.main(Application.java:13)
8 [main] ERROR de.scrum_master.app.Application  - [MY PREFIX] WTF dude?!?


这是最简单的解决方案。如果您需要日志输出中的更多信息,例如调用者或异常的特殊信息,则可以很容易地调整切入点。此外,Logger.error(..)有许多重载。我的示例切入点仅捕获第一个参数为String的那些,例如error(String)error(String, Throwable)。如果您需要其他信息,你需要调整这个方面。但这是它基本上是如何工作的。
请注意,这将拦截所有错误日志,甚至是从应用程序外部调用的错误日志。如果你想将其限制在应用程序的基本包中,你必须再次调整切入点,并使用call()而不是execution(),类似于within(my.base.pkg..*) && call(void org.slf4j.Logger.error(String, ..)) && args(msg, ..)
有这么多的方法来调整这个解决方案,这将需要几个小时的时间来列出例子。

mzmfm0qo

mzmfm0qo2#

我找到了另一种方法,但上面的解决方案是更合适和更好的方法来做这件事
我在Spring Aop中使用了ThreadContext。下面的代码部分属于我的Spring Aop类,所以我为每个端点调用创建了一个Pointcut。当有一个API调用时,它会进入logAround方法,我在其中声明ThreadContext

@Pointcut("within(com.saas.contoller.*)")
    public void applicationPackagePointcut() {
        // Method is empty as this is just a Pointcut, the implementations are in the advices.
    }
    /**
     * Pointcut that matches all Spring beans in the application's main packages.
     */
    @Pointcut("within(com.saas.service.*)")
    public void servicePackagePointcut() {
        // Method is empty as this is just a Pointcut, the implementations are in the advices.
    }

   @Around("applicationPackagePointcut() && springBeanPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String user = "";
        if (state) {
            user = getUser();
        }
        ThreadContext.put("user",user);
        Object result = joinPoint.proceed();
        ThreadContext.remove("user");

字符串
并像这样将用户前缀添加到log4j模式中

<Property name="LOG_PATTERN_CONSOLE" >
            %d{yyyy-MM-dd HH:mm:ss.SSS}  - user:%X{user}  %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=blue, TRACE=cyan}  %style{[%15.15t]}{magenta} : %style{%-40.40c{1.}}{cyan} : %m%n%ex
        </Property>


通过这种方式,在每次API调用之前,我将username添加到线程上下文,然后log4j为每个logger使用此user参数。error(这取决于你如何安排你的log4j.xml文件。从这一点上,你可以添加到跟踪或信息或你的自定义日志级别)和API调用后,我必须删除这个用户名从线程上下文,因为如果你不删除这个值从线程上下文,这个线程将进入线程池,并将被另一个进程调用,它也将携带你添加的值,在我的情况下,它是username。所以,如果我不删除我的值,我可以看到一个username,username不应该被看到

相关问题