您的位置:

AOP日志详解

一、AOP日志处理

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程思想和技术,可以将一些与业务无关,却在多个模块或层中经常出现的重复操作,如日志、事务、异常处理等,抽象为一些可重用的模块,即切面(Aspect),并将其与业务模块进行关联。当业务模块执行时,切面在特定的时机被织入到业务模块中,从而达到了横向抽取重复代码的目的。

在使用AOP时,日志处理是其中最常见的场景之一。我们可以使用AOP实现统一的日志输出、日志记录、日志分析等功能,并且不对业务代码进行侵入。

二、AOP日志怎么实现

AOP日志的实现主要包括以下几个步骤:

1.定义切面类

public class LogAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.printf("LogBefore... srcClass:%s,method:%s\n",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        System.out.printf("LogAfter... srcClass:%s,method:%s\n",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }
}

2.将切面类声明为切面

@Aspect
@Component
public class LogAspect {
    ...
}

3.启用AOP

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    ...
}

三、AOP日志记录

日志记录是AOP日志功能的重要组成部分。我们可以通过以下方式将日志记录到本地文件或其他数据存储设备中。

1.使用Log4j2实现日志记录

@Configuration
public class LogConfig {
    @Bean
    public org.springframework.boot.logging.LoggingSystem loggingSystem() throws Exception {
        return new Log4j2LoggingSystem();
    }
}

2.配置Log4J2日志输出



    
   
        
    
            
     
        
    
        
    
            
     
                
      %d %p %c{1.} [%t] %m%n
      
            
     
            
     
                
      
                
      
            
     
            
     
        
    
    
   
    
   
        
    
            
     
            
     
        
    
        
    
            
     
        
    
    
   

  

四、AOP日志管理

对于大型项目,日志管理十分重要。我们需要合理的管理日志文件,对日志进行分析和监控,保障系统的正常运行。

1.使用ELK Stack对日志进行集中管理

ELK Stack指的是Elasticsearch、Logstash、Kibana三者的组合。其中,Elasticsearch用于存储日志数据,Logstash用于收集、处理和转化数据,Kibana用于展示、查询和分析数据。

2.配置Logstash收集日志

input {
  file {
    path => "/var/log/my-app.log"
    type => "my-app"
  }
}

filter {
  grok {
    match => [ "message", "%{TIMESTAMP_ISO8601:timestamp} \[%{WORD:thread}\] %{LOGLEVEL:loglevel} %{GREEDYDATA:msg}" ]
  }
}

output {
  elasticsearch {
    hosts => [ "localhost:9200" ]
    index => "my-app-%{+YYYY.MM.dd}"
  }
}

五、AOP日志输出

日志输出是AOP日志功能中最常见的操作之一。我们可以通过以下方式将日志输出,方便进行调试和排查问题。

1.使用Logback实现日志输出

  
  
   
    
    
      
     %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
     
    
    
  
   

  
   
    
    
  
   

  
   
    
    
  
   

  

2.通过邮箱将日志输出

private void sendEmail(String subject, String content) {
    SimpleMailMessage message = new SimpleMailMessage();
    message.setTo("recipient@example.com");
    message.setSubject(subject);
    message.setText(content);
    sender.send(message);
}

六、AOP日志多线程

多线程是现在的主流编程方式之一,而AOP日志同样支持多线程。我们可以通过以下方式处理多线程的日志输出。

1.使用ThreadLocal保存线程上下文

public class ContextHolder {
    private static ThreadLocal contextHolder = new ThreadLocal<>();

    public static Context getContext() {
        if (contextHolder.get() == null) {
            contextHolder.set(new Context());
        }
        return contextHolder.get();
    }

    public static void clear() {
        contextHolder.set(null);
    }
}

  

2.在切面中处理线程上下文

@Around("@annotation(log)")
public Object process(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
    Context context = ContextHolder.getContext();
    context.setUsername(log.username());

    try {
        Object result = joinPoint.proceed();
        return result;
    } finally {
        ContextHolder.clear();
    }
}

七、AOP记录日志

记录日志是AOP日志的核心功能之一,可以为我们提供详细的系统运行记录,方便日后排查问题。

1.AOP方式实现日志记录

@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        logger.info("LogBefore... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        logger.info("LogAfter... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }
}

2.使用注解方式记录日志

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value();
}

@Service
public class UserService {
    @Log("Add user")
    public void addUser(User user) {
        ...
    }
}

八、AOP注解方式

AOP注解方式是AOP日志的一种实现方式,可以更加方便地为业务代码添加切面。我们可以使用Spring AOP注解实现AOP日志的功能

1.在切面类中添加注解

@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        logger.info("LogBefore... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        logger.info("LogAfter... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }
}

2.在业务类中添加注解

@Service
public class UserService {
    @LogAnnotation
    public void addUser(User user) {
        ...
    }
}

九、AOP实现日志功能步骤

1.定义切面类

@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        logger.info("LogBefore... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        logger.info("LogAfter... srcClass:{},method:{}",joinPoint.getTarget().getClass(),joinPoint.getSignature().getName());
    }
}

2.声明切面类

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

3.使用切面类的日志方法

@Service
public class UserService {
    public void addUser(User user) {
        LogAspect.logBefore();
        ...
        LogAspect.logAfter();
    }
}

十、Spring AOP的注解

在Spring中,我们可以使用注解的方式实现AOP功能。以下是Spring AOP中与AOP日志相关的注解示例。

1.@Aspect注解:标明该类为切面类

2.@Pointcut注解:定义切点,通常使用execution表达式进行定义

3.@Before注解:在切点之前执行

4.@After注解:在切点之后执行

5.@Around注解:包含了@Before和@After的功能,可以精确控制执行时间点

6.@AfterReturning注解:在方法返回结果后执行

7.@AfterThrowing注解:在方法抛出异常时执行

十一、总结

AOP日志是一个在现代开发中十分重要的功能,使用AOP方式实现日志功能可以为我们提供更加简洁、易读的代码和高效、精确的记录和输出方式。我们可以使用多种方式去实现AOP日志,包括注解方式、XML配置方式以及Java配置方式等等。无论使用哪种方式,我们都需要深入了解AOP日志的核心思想和实现方式,才能够更加灵活地运用到业务场景之中。