spring集成mongodb通过aop记录业务日志

1. 编辑 pom.xml 添加 maven 依赖

<!-- mongodb -->
        <dependency>
          <groupId>org.mongodb</groupId>
          <artifactId>mongo-java-driver</artifactId>
          <version>2.14.2</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-mongodb</artifactId>
          <!-- 都是坑,用最新版的报错,下面全部用回1.7.1版 -->
          <version>1.7.1.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-mongodb-cross-store</artifactId>
          <version>1.7.1.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-mongodb-log4j</artifactId>
          <version>1.7.1.RELEASE</version>
        </dependency>
        <!-- mongodb -->

2.添加 spring-mongodb.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">

    <!--连接池配置-->
    <mongo:mongo id="mongo" host="host" port="port">
        <mongo:options connections-per-host="8"
                       threads-allowed-to-block-for-connection-multiplier="4"
                       connect-timeout="1000"
                       max-wait-time="1500"
                       auto-connect-retry="true"
                       socket-keep-alive="true"
                       socket-timeout="1500"
                       slave-ok="true"
                       write-number="1"
                       write-timeout="0"
                       write-fsync="true"/>
    </mongo:mongo>

    <!--连接池工厂配置-->
    <mongo:db-factory dbname="dbname" username="username" password="pwd" mongo-ref="mongo"/>

    <!-- MongoDBTemplate -->
    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    </bean>
</beans>

3.在 applicationContext.xml 文件中引用 spring-mongodb.xml

<import resource="spring-mongodb.xml" />

4.若 aop 在controller 层不生效,则需要在 springmvc 配置文件添加如下配置

<aop:aspectj-autoproxy proxy-target-class="true"/>

5.集成 MongoTemplate

  Model

import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "business_log")
public class BusinessLogInfo {
    /**
     * 日志级别:INFO,WARN,ERROR,DEBUG
     */
    private String level;
    /**
     * 客户端ip
     */
    private String clientIp;
    /**
     * 服务端ip,用于区分是哪台服务器
     */
    private String serverIp;
    /**
     * 请求url
     */
    private String requestUrl;
    /**
     * 目标类
     */
    private String targetClass;
    /**
     * 目标方法
     */
    private String targetMethod;
    /**
     * 请求参数
     */
    private String requestParam;
    /**
     * 日志内容
     */
    private String content;
    /**
     * 异常
     */
    private String exception;
    /**
     * 日志记录时间
     */
    private String createDate;
    
    public String getLevel() {
        return level;
    }
    public void setLevel(String level) {
        this.level = level;
    }
    public String getClientIp() {
        return clientIp;
    }
    public void setClientIp(String clientIp) {
        this.clientIp = clientIp;
    }
    public String getServerIp() {
        return serverIp;
    }
    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }
    public String getRequestUrl() {
        return requestUrl;
    }
    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }
    public String getTargetClass() {
        return targetClass;
    }
    public void setTargetClass(String targetClass) {
        this.targetClass = targetClass;
    }
    public String getTargetMethod() {
        return targetMethod;
    }
    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }
    public String getRequestParam() {
        return requestParam;
    }
    public void setRequestParam(String requestParam) {
        this.requestParam = requestParam;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getException() {
        return exception;
    }
    public void setException(String exception) {
        this.exception = exception;
    }
    public String getCreateDate() {
        return createDate;
    }
    public void setCreateDate(String createDate) {
        this.createDate = createDate;
    }
    
    @Override
    public String toString() {
        return "BusinessLogInfo [level=" + level + ", clientIp=" + clientIp + ", serverIp=" + serverIp + ", requestUrl="
                + requestUrl + ", targetClass=" + targetClass + ", targetMethod=" + targetMethod + ", requestParam="
                + requestParam + ", content=" + content + ", exception=" + exception + ", createDate=" + createDate
                + "]";
    }    
}

  Dao

import org.springframework.data.mongodb.core.MongoTemplate;

public interface MongoDao {
    // insert
    void insert(MongoTemplate mongoTemplate, Object obj);
    
     find
    @SuppressWarnings("hiding")
    <T> List<T> find(MongoTemplate mongoTemplate, Query query, Class<T> clazz);
    
    // findOne
    @SuppressWarnings("hiding")
    <T> T findOne(MongoTemplate mongoTemplate, Query query, Class<T> clazz);
    
    // modify
    
    // del
}

  DaoImpl

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

@Service
implements MongoDao{
 insert
    @Override
    public void insert(MongoTemplate mongoTemplate, Object obj) {
        mongoTemplate.insert(obj);        
    }

    // find
    @SuppressWarnings("hiding")
    @Override
    public <T> List<T> find(MongoTemplate mongoTemplate, Query query, Class<T> clazz) {
        return mongoTemplate.find(query, clazz);
    }

    // findOne
    @SuppressWarnings("hiding")
    @Override
    public <T> T findOne(MongoTemplate mongoTemplate, Query query, Class<T> clazz) {
        return mongoTemplate.findOne(query, clazz);
    }
}

  Service

public interface MongoService {
    // insert
    void insert(Object obj);
    
    // find
    <T> List<T> find(Query query, Class<T> clazz);
    
    // findOne
    <T> T findOne(Query query, Class<T> clazz);
}

  ServiceImpl

import javax.annotation.Resource;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

@Service("mongoService")
public class MongoServiceImpl implements MongoService {
    @Resource
    MongoDao mongoDao;
    
    @Resource(name = "mongoTemplate")
    private MongoTemplate mongoTemplate;
    
    // insert
    @Override
    public void insert(Object obj) {
        mongoTemplate.insert(obj);
    }
    
    // find
    public <T> List<T> find(Query query, Class<T> clazz) {
        return mongoDao.find(mongoTemplate, query, clazz);
    }

    // findOne
    @Override
    public <T> T findOne(Query query, Class<T> clazz) {
        return mongoDao.findOne(mongoTemplate, query, clazz);
    }
}

  Facade

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

@Service("mongoLoggerFacade")
public class MongoLoggerFacade {
    private static final Logger LOGGER = LoggerFactory.getLogger(MongoLoggerFacade.class);

    @Resource
    MongoService mongoService;

    @Resource(name = "mongoTemplate")
    private MongoTemplate mongoTemplate;

    // private static final String COLLECTION = "business_log";

    /**
     * 本机ip
     */
    private static String LOCAL_IP = "";

    /**
     * 客户端ip
     */
    private String clientIp;

    /**
     * 请求url
     */
    private String requestUrl;

    /**
     * 目标对象
     */
    private String targetClass;

    /**
     * 目标方法
     */
    private String targetMethod;

    /**
     * 请求参数
     */
    private String requestParam;

    /**
     * 捕获异常
     */
    private String exception;

    public String getClientIp() {
        return clientIp;
    }

    public void setClientIp(String clientIp) {
        this.clientIp = clientIp;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public String getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(String targetClass) {
        this.targetClass = targetClass;
    }

    public String getTargetMethod() {
        return targetMethod;
    }

    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }

    public String getRequestParam() {
        return requestParam;
    }

    public void setRequestParam(String requestParam) {
        this.requestParam = requestParam;
    }

    public String getException() {
        return exception;
    }

    public void setException(String exception) {
        this.exception = exception;
    }

    /**
     * 记录INFO级别日志
     * 
     * @param content
     */
    public void INFO(String content) {
        BusinessLogInfo businessLog = getBusinessLog("INFO", content);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录INFO级别日志
     * 
     * @param content
     */
    public void INFO(String content, String exception) {
        BusinessLogInfo businessLog = getBusinessLog("INFO", content, exception);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录ERROR级别日志
     * 
     * @param content
     */
    public void ERROR(String content) {
        BusinessLogInfo businessLog = getBusinessLog("ERROR", content);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录ERROR级别日志
     * 
     * @param content
     */
    public void ERROR(String content, String exception) {
        BusinessLogInfo businessLog = getBusinessLog("ERROR", content, exception);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录WARN级别日志
     * 
     * @param content
     */
    public void WARN(String content) {
        BusinessLogInfo businessLog = getBusinessLog("WARN", content);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录WARN级别日志
     * 
     * @param content
     */
    public void WARN(String content, String exception) {
        BusinessLogInfo businessLog = getBusinessLog("WARN", content, exception);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录DEBUG级别日志
     * 
     * @param content
     */
    public void DEBUG(String content) {
        BusinessLogInfo businessLog = getBusinessLog("DEBUG", content);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    /**
     * 记录DEBUG级别日志
     * 
     * @param content
     */
    public void DEBUG(String content, String exception) {
        BusinessLogInfo businessLog = getBusinessLog("DEBUG", content, exception);
        // 插入mongodb
        mongoService.insert(businessLog);
    }

    // 获取封装BusinessLogInfo对象
    private BusinessLogInfo getBusinessLog(String level, String content) {
        BusinessLogInfo businessLog = new BusinessLogInfo();
        setField(businessLog, level, content);
        // 设置异常
        String thisException = getException();
        if(StringUtils.isBlank(thisException)) {
            businessLog.setException("");
        } else {
            businessLog.setException(getException());
        }
        return businessLog;
    }

    // 获取封装BusinessLogInfo对象
    private BusinessLogInfo getBusinessLog(String level, String content, String customException) {
        BusinessLogInfo businessLog = new BusinessLogInfo();
        setField(businessLog, level, content);
        // 设置异常
        businessLog.setException(customException);
        return businessLog;
    }

    // 设置属性值
    private void setField(BusinessLogInfo businessLog, String level, String content) {
        // 设置日志级别
        businessLog.setLevel(level);
        // 设置内容
        businessLog.setContent(content);
        // 获取客户端ip
        businessLog.setClientIp(getClientIp());
        // 设置本机ip
        businessLog.setServerIp(getLocalIp());
        // 设置请求url
        businessLog.setRequestUrl(getRequestUrl());
        // 设置目标类
        businessLog.setTargetClass(getTargetClass());
        // 设置目标方法
        businessLog.setTargetMethod(getTargetMethod());
        // 获取请求参数
        businessLog.setRequestParam(getRequestParam());
        // 设置记录时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        businessLog.setCreateDate(sdf.format(new Date()));
    }

    // 获取服务端ip
    private String getLocalIp() {
        if (StringUtils.isBlank(LOCAL_IP)) {
            InetAddress inetAddress = null;
            try {
                inetAddress = InetAddress.getLocalHost();
                LOCAL_IP = inetAddress.getHostAddress();
            } catch (UnknownHostException e) {
                LOGGER.error("===== get local host error =====");
            }
        }
        return LOCAL_IP;
    }

    /**
     * 查询BusinessLogInfo
     * 
     * @param conditions
     * @return
     */
    public BusinessLogInfo findBusinessLog(Map<String, String> conditions) {
        Query query = new Query();
        Set<String> keys = conditions.keySet();
        for (String key : keys) {
            String val = conditions.get(key);
            if (StringUtils.isNotBlank(val)) {
                query.addCriteria(Criteria.where(key).is(val));
            }
        }
        return mongoService.findOne(query, BusinessLogInfo.class);
    }

    /**
     * 查询BusinessLogInfo列表
     * 
     * @param conditions
     * @return
     */
    public List<BusinessLogInfo> findBusinessLogList(Map<String, String> conditions) {
        Query query = new Query();
        Set<String> keys = conditions.keySet();
        for (String key : keys) {
            String val = conditions.get(key);
            if (StringUtils.isNotBlank(val)) {
                query.addCriteria(Criteria.where(key).is(val));
            }
        }
        return mongoService.find(query, BusinessLogInfo.class);
    }
}

  Interceptor

import java.io.PrintWriter;
import java.io.StringWriter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class MongoLoggerInterceptor {
    @Resource
    private MongoLoggerFacade mongoLoggerFacade;

    // 连接点是@RequestMapping注解的方法
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private void webPointcut() {
    }

    /**
     * 记录日志前获取目标对象和目标方法
     * 
     * @param joinPoint
     * @throws Throwable
     */
    @Before(value = "execution(* com.kmei.*.*.*.*(..)) and !execution(* com.kmei.mongo.facade.*.*(..))")
    public void befordMedthod(JoinPoint joinPoint) {
        preOperation(joinPoint);
    }

    /**
     * 捕获controller类抛出的异常
     * 
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "webPointcut()", throwing = "e")
    public void handleThrowing(JoinPoint joinPoint, Exception e) {
        preOperation(joinPoint);
        mongoLoggerFacade.setException(getExceptionText(e));
        mongoLoggerFacade.ERROR("aop cache exception");
    }

    // 预处理操作
    private void preOperation(JoinPoint joinPoint) {
        // 获取目标对象和方法
        String targetClass = joinPoint.getTarget().getClass().getName();
        String targetMethod = joinPoint.getSignature().getName();
        mongoLoggerFacade.setTargetClass(targetClass);
        mongoLoggerFacade.setTargetMethod(targetMethod);
        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            String requestParam = "requestParam:[{";
            for (int i = 0; i < args.length; i++) {
                int j = i + 1;
                if (args[i] != null && StringUtils.isNotBlank(args[i].toString())) {
                    requestParam += "param" + j + ":" + args[i].toString() + ", ";
                } else {
                    requestParam += "param" + j + ":null, ";
                }
            }
            requestParam = requestParam.substring(0, requestParam.length() - 2) + "}]";
            mongoLoggerFacade.setRequestParam(requestParam);
            // Map<String, String> paramMap = getFieldsName(targetClass,
            // targetMethod, args);
            // mongoLoggerFacade.setRequestParam(paramMap.toString());
        }
        // 获取客户端真实ip
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
        if (sra != null) {
            HttpServletRequest request = sra.getRequest();
            mongoLoggerFacade.setClientIp(getRealIp(request));
            mongoLoggerFacade.setRequestUrl(request.getRequestURI());
        }
    }

    // 下列代码用于获取参数名,存在问题:当参数包含HttpServletRequest和HttpServletResponse时会抛出异常,暂时注释不启用
    // private Map<String, String> getFieldsName(String targetClass, String
    // targetMethod, Object[] args) throws ClassNotFoundException,
    // NoSuchMethodException {
    // // 获取参数值
    // Class<?>[] classes = new Class[args.length];
    // for (int i = 0; i < args.length; i++) {
    // if (!args[i].getClass().isPrimitive()) {
    // // 获取的是封装类型而不是基础类型
    // String result = args[i].getClass().getName();
    // if("javax.servlet.http.HttpServletRequest".equals(result) ||
    // "javax.servlet.http.HttpServletResponse".equals(result)) {
    // continue;
    // }
    // Class<?> s = classMap.get(result);
    // classes[i] = s == null ? args[i].getClass() : s;
    // }
    // }
    // ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
    // // 获取指定的方法,第二个参数可以不传,但是为了防止有重载的现象,还是需要传入参数的类型
    // Method method = Class.forName(targetClass).getMethod(targetMethod);
    // // 参数名
    // String[] parameterNames = pnd.getParameterNames(method);
    // if(parameterNames == null) {
    // System.out.println("===== parameterNames is null =====");
    // return null;
    // }
    // System.out.println("===== parameterNames: " + parameterNames.toString());
    // // 通过map封装参数和参数值
    // Map<String, String> paramMap = new HashMap<String, String>();
    // for (int i = 0; i < parameterNames.length; i++) {
    // paramMap.put(parameterNames[i], args[i].toString());
    // }
    // return paramMap;
    // }

    // @SuppressWarnings("rawtypes")
    // private static HashMap<String, Class> classMap = new HashMap<String,
    // Class>() {
    // private static final long serialVersionUID = 8205702023950800531L;
    //
    // {
    // put("java.lang.Integer", int.class);
    // put("java.lang.Double", double.class);
    // put("java.lang.Float", float.class);
    // put("java.lang.Long", long.class);
    // put("java.lang.Short", short.class);
    // put("java.lang.Boolean", boolean.class);
    // put("java.lang.Char", char.class);
    // }
    // };

    // 打印异常堆栈信息
    private String getExceptionText(Exception e) {
        String text = "";
        if (e == null) {
            return text;
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        text = sw.toString();
        text = e.getMessage() + "
" + text;
        return text;
    }

    // 获取客户端真实ip
    private String getRealIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotBlank(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotBlank(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }
}

  Controller

import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Component
@Controller
@RequestMapping("/testmongo")
public class TestMongoController {
    @Resource
    MongoLoggerFacade mongoLoggerFacade;
    
    @RequestMapping(value = "/testcontent", method = RequestMethod.GET)
    @ResponseBody
    public void testContent(@RequestParam("content") String content, @RequestParam("test") String test, BusinessLogInfo businessLogInfo) throws Exception {
        System.out.println("===== test content =====");
        mongoLoggerFacade.INFO(content);
    }

    @RequestMapping(value = "/testinfo", method = RequestMethod.GET)
    @ResponseBody
    public void testInfo() throws Exception {
        System.out.println("===== test info =====");
        mongoLoggerFacade.INFO("test log info");
    }
    
    @RequestMapping(value = "/testerror", method = RequestMethod.GET)
    @ResponseBody
    public void testError() throws Exception {
        System.out.println("===== test error =====");
        mongoLoggerFacade.ERROR("test log error");
    }
    
    @RequestMapping(value = "/testwarn", method = RequestMethod.GET)
    @ResponseBody
    public void testWarn() throws Exception {
        System.out.println("===== test warn =====");
        mongoLoggerFacade.WARN("test log warn");
    }

    @RequestMapping(value = "/testfindone", method = RequestMethod.GET)
    @ResponseBody
    public void test2() throws Exception {
        System.out.println("===== test findOne =====");
        Map<String, String> conditions = new HashMap<>();
        conditions.put("level", "INFO");
        BusinessLogInfo result = mongoLoggerFacade.findBusinessLog(conditions);
        System.out.println(result);
    }
    
    @RequestMapping(value = "/testfind", method = RequestMethod.GET)
    @ResponseBody
    public void test3() throws Exception {
        System.out.println("===== test find =====");
        Map<String, String> conditions = new HashMap<>();
        conditions.put("level", "INFO");
        List<BusinessLogInfo> result = mongoLoggerFacade.findBusinessLogList(conditions);
        System.out.println(result);
    }
}