Spring全家桶实践-AOP日志

简介

SLF4J是Simple Logging Facade for Java的缩写。它主要提供了Java目前现有日志框架的简单抽象。它使用户能够使用单个依赖项处理任何日志框架,例如:Log4j,Logback和JUL。也可以在运行/部署时迁移到对应的日志记录框架。

优点

*可以在部署时迁移到所需的日志记录框架。
*提供了对所有流行的日志框架的绑定。
*支持参数化日志记录消息。
*程序和日志记录框架分离。
*提供了一个简单的日志迁移工具。

常规使用

参看slf4j官网网站
参看易佰教程

这个模块需要的知识贮备

实际项目使用自定义注解和aop的方式来实现操作访问记录功能

自定义注解

参看Java的注解

AOP

Spring AOP简单样例
暂时不记录数据库,等学习完jpa后,在记录到数据中。

实践

maven文件

        
            org.springframework.boot
            spring-boot-starter-aop
        

目录结构

Spring全家桶实践-AOP日志_第1张图片
image.png

源码

定义记录那些信息Log类(Log.java)

package com.springboot.action.saas.common.logging.domain;

import lombok.Data;

import java.sql.Timestamp;

@Data
public class Log {
    //描述
    private String description;
    //方法名
    private String method;
    //参数
    private String params;
    //日志类型
    private String logType;
    //请求ip
    private String requestIp;
    //请求耗时
    private Long time;
    //异常详细
    private String exceptionDetail;
    //请求实践
    private Timestamp createTime;
    //构造函数(代参)
    public Log(String logType, Long time) {
        this.logType = logType;
        this.time = time;
    }
}

日志记录业务接口(LogService.java)

package com.springboot.action.saas.common.logging.service;

import com.springboot.action.saas.common.logging.domain.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.scheduling.annotation.Async;

/*
* 日志记录业务
*
*/
public interface LogService {
    /*
    * 记录日志,异步执行,不影响正常业务流程
    * 参数 joinPoint 切面方法的信息,当前切入点各种信息
    *     log 要记录的日志信息有那些
    * */
    @Async
    void save(ProceedingJoinPoint joinPoint, Log log);
}

日志记录业务接口实现(LogServiceImpl.java)

package com.springboot.action.saas.common.logging.service.impl;

import com.springboot.action.saas.common.logging.domain.Log;
import com.springboot.action.saas.common.logging.service.LogService;
import com.springboot.action.saas.common.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Service
public class LogServiceImpl implements LogService {
    /*
    * 记录日志接口实现
    **/
    @Override
    public void save(ProceedingJoinPoint joinPoint, Log log) {
        //获取request 请求对象
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
                .getRequest();
        //getSignature获取切面相关信息,比如方法名、目标方法参数等信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取抽象类(代理对象)方法
        Method method = signature.getMethod();
        //返回该元素的指定类型的注释,这里是Log注解
        com.springboot.action.saas.common.logging.annotation.Log aopLog = method.getAnnotation(com.springboot.action.saas.common.logging.annotation.Log.class);
        //获取注解传递的参数
        if (log != null) {
            log.setDescription(aopLog.value());
        }
        //通过最笨的反射方法,获取方法路径
        String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()";
        log.setMethod(methodName);
        //参数处理
        //获取参数值
        Object[] argValues = joinPoint.getArgs();
        //获取参数名
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //组织参数列表
        String params = "{";
        if(argValues != null){
            for (int i = 0; i < argValues.length; i++) {
                params += " " + argNames[i] + ": " + argValues[i];
            }
        }
        log.setParams(params + " }");
        //获取IP地址
        log.setRequestIp(StringUtils.getIP(request));
        //输出下日志到控制台
        System.out.println(log.toString());
    }
}

配置切点和切点对应的动作(好多文档都说是通知)
LogAspect.java

package com.springboot.action.saas.common.logging.aspect;

import com.springboot.action.saas.common.logging.domain.Log;
import com.springboot.action.saas.common.logging.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {
    //日志业务
    @Autowired
    private LogService logService;
    //当前时间
    private long currentTime = 0L;

    /**
     * 配置切入点, 匹配连接点的方法是否有Log注解,定义切点
     */
    @Pointcut("@annotation(com.springboot.action.saas.common.logging.annotation.Log)")
    public void logPointcut() {
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点,具体要通知在什么条件下执行和执行什么动作
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint){
        //返回值
        Object result = null;
        //获取当前时间
        currentTime = System.currentTimeMillis();
        try {
            //执行目标方法
            result = joinPoint.proceed();
        } catch (Throwable e) {
            //抛异常
            throw new RuntimeException(e.getMessage());
        }
        //创建日志对象
        Log log = new Log("INFO",System.currentTimeMillis() - currentTime);
        //记录日志
        logService.save(joinPoint, log);
        //返回目标方法的返回值
        return result;
    }

    /**
     * 配置异常通知,异常的日志也要记录
     *
     * @param joinPoint join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        //创建日志对象
        Log log = new Log("ERROR",System.currentTimeMillis() - currentTime);
        //异常设置
        log.setExceptionDetail(e.getMessage());
        //记录异常
        logService.save((ProceedingJoinPoint)joinPoint, log);
    }
}

REST接口

/**
     * 显示所有Member,请求url:"http://xxx/member/v1/findall"
     *
     * @return List
     */
    //这里增加了自定义日志注解
    @Log("获取全部用户列表")
    @RequestMapping(value = "/v1/findall")
    public List findAllMember() {
        return memberService.findAllMember();
    }

实际运行效果
请求

http://localhost:8080/member/v1/findall

浏览器

{"success":true,"code":0,"data":[{"id":1,"phone":null,"password":null,"nickname":"demo1","email":null,"ctime":null,"utime":null,"last_login_time":null,"last_login_ip":null,"invite_code":null,"is_active":null,"is_delete":null},{"id":2,"phone":null,"password":null,"nickname":"demo2","email":null,"ctime":null,"utime":null,"last_login_time":null,"last_login_ip":null,"invite_code":null,"is_active":null,"is_delete":null}],"message":"","currentTime":1564631416588}

控制台

Log(description=获取全部用户列表, method=com.springboot.action.saas.modules.user.controller.MemberController.findAllMember(), params={ }, logType=INFO, requestIp=127.0.0.1, time=4, exceptionDetail=null, createTime=null)

打tag 1.0.4版本,提交代码。

git tag -a v1.0.4 -m "实现AOP日志信息获取"

git push origin v1.0.4

github地址:https://github.com/horacepei/springsaas

你可能感兴趣的:(Spring全家桶实践-AOP日志)