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); } }