dubbo2.5-spring4-mybastis3.2-springmvc4-mongodb3.4-redis3.2整合(四)Spring AOP中使用log4j实现http请求日志入mongodb
2017-01-02 14:23
1126 查看
1、面向切面编程的一些术语
切面(Aspect):切面用于组织多个Advice,Advice放在切面中的定义。连接点(Jionpoint):程序执行过程中的明确的点,如方法的调用,或异常的抛出。在SpringAOP中,连接点总是方法的调用。
增强的处理(Advice):AOP框架在特定的切入点执行的增强处理。处理有around、brefore和after等类型。
切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定的要求时,该连接点将添加增强处理,该连接点也就变成了切入点。
pointcut xxxPointcut():execution(void H*.say*())
引入:将方法或字段添加到被代理的类中,Spring允许将新的接口引入到任何被调用的处理对象中。例如:你可以使用一个引入,使任何对象实现IsModified接口,以此来简化缓存。
目标对象:被AOP框架进行增强处理的对象,也被称为被增强对象,如果AOP框架采用的是动态的AOP实现 ,那该对象就是一个被代理对象。
AOP代理:AOP框架创建的对象,简单说,代理就是对目标对象的加强,Spring中的AOP代理可以是JDK的动态代理,也可以是cglib的动态代理,前者是实现接口 的目标对象的代理,后者为不实现接口的目标对象的代理。
织入(Weaving):将增强处理添加到目标对象中,并创一个被增强对象(AOP代理)的过程就是织入。织入有l两种实现方式—–编译时增强(AspectJ)和运行时增强(如Spring AOP)。Spring和其他纯Java AOP框架一样,在运行时织入。
2、AOP的基本概念
AOP是从程序运行角度考虑程序的流程,提取业务处理的过程的切面,AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。AOP框架并不会与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(PointCut),而不是某个具体类的耦合。
3、AOP框架具有如下的两个特征。
各个步骤之间良好的隔离。源代码无关。
注:Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理。从而提高了更多的复用。
4.如何在Spring中引入AOP功能
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.2</version> </dependency>
5、实现Web层的日志切面
实现AOP的切面主要有以下几个要素:使用 @Aspect 注解将一个java类定义为切面类。
使用 @Pointcut 定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
根据需要在切入点不同位置的切入内容
使用 @Before 在切入点开始处切入内容
使用 @After 在切入点结尾处切入内容
使用 @AfterReturning 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
使用 @Around 在切入点前后切入内容,并自己控制何时执行切入点自身的内容
使用 @AfterThrowing 用来处理当切入内容部分抛出异常之后的处理逻辑
package com.lidong.util; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.mongodb.BasicDBObject; /** 1. 保存请求数据到mongodb 2. @author lidong 3. @createTime 2016年12月24日 */ @Aspect @Order(1) @Component public class WebLogAspect { private Logger logger = Logger.getLogger("mongodb"); ThreadLocal<Long> startTime = new ThreadLocal<>(); HttpServletRequest request; JoinPoint mJoinPoint; @Pointcut("execution (* com.lidong.*.controller.*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); request = attributes.getRequest(); mJoinPoint = joinPoint; } private BasicDBObject getBasicDBObject(HttpServletRequest request, JoinPoint joinPoint) { BasicDBObject r = new BasicDBObject(); r.append("requestURL", request.getRequestURL().toString()); r.append("requestURI", request.getRequestURI()); r.append("queryString", request.getQueryString()); r.append("remoteAddr", request.getRemoteAddr()); r.append("remoteHost", request.getRemoteHost()); r.append("remotePort", request.getRemotePort()); r.append("localAddr", request.getLocalAddr()); r.append("localName", request.getLocalName()); r.append("method", request.getMethod()); r.append("headers", getHeadersInfo(request)); r.append("parameters", request.getParameterMap()); r.append("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); r.append("args", Arrays.toString(joinPoint.getArgs())); return r; } /** * 获取请求头部的信息 * @param request * @return */ private Map<String, String> getHeadersInfo(HttpServletRequest request) { Map<String, String> map = new HashMap<>(); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String key = (String) headerNames.nextElement(); String value = request.getHeader(key); map.put(key, value); } return map; } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { BasicDBObject logInfo = getBasicDBObject(request, mJoinPoint); // 处理完请求,返回内容 logInfo.append("response",ret.toString()); logInfo.append("spend_time", (System.currentTimeMillis() - startTime.get())); logger.info(logInfo); } }
注意:在WebLogAspect切面中,分别通过doBefore和doAfterReturning两个独立函数实现了切点头部和切点返回后执行的内容,若我们想统计请求的处理时间,就需要在doBefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算得到请求处理的消耗时间。
6、实现Web层的日志的日志保存到mongodb数据库中
6.1.通过自定义appender实现
思路:log4j提供的输出器实现自Appender接口,要自定义appender输出到MongoDB,只需要继承AppenderSkeleton类,并实现几个方法即可完成。引入mongodb的驱动,在pom.xml中引入下面依赖
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver</artifactId> <version>3.2.2</version> </dependency>
实现MongoAppender
编写MongoAppender类继承AppenderSkeleton,实现如下:
package com.lidong.util; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.spi.LoggingEvent; import com.mongodb.BasicDBObject; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; /** * 自定义appender实现输出日志MongoDB * @author lidong * */ public class MongoAppender extends AppenderSkeleton { private MongoClient mongoClient; private MongoDatabase mongoDatabase; private MongoCollection<BasicDBObject> logsCollection; /** * mongodb连接的url */ private String connectionUrl; /** * 数据库的名称 */ private String databaseName; /** * 集合的名称 */ private String collectionName; @Override protected void append(LoggingEvent loggingEvent) { if(mongoDatabase == null) { MongoClientURI connectionString = new MongoClientURI(connectionUrl); mongoClient = new MongoClient(connectionString); mongoDatabase = mongoClient.getDatabase(databaseName); logsCollection = mongoDatabase.getCollection(collectionName, BasicDBObject.class); } //将日志插入到集合 logsCollection.insertOne((BasicDBObject) loggingEvent.getMessage()); } @Override public void close() { if(mongoClient != null) { mongoClient.close(); } } @Override public boolean requiresLayout() { return false; } public MongoClient getMongoClient() { return mongoClient; } public void setMongoClient(MongoClient mongoClient) { this.mongoClient = mongoClient; } public MongoDatabase getMongoDatabase() { return mongoDatabase; } public void setMongoDatabase(MongoDatabase mongoDatabase) { this.mongoDatabase = mongoDatabase; } public MongoCollection<BasicDBObject> getLogsCollection() { return logsCollection; } public void setLogsCollection(MongoCollection<BasicDBObject> logsCollection) { this.logsCollection = logsCollection; } public String getConnectionUrl() { return connectionUrl; } public void setConnectionUrl(String connectionUrl) { this.connectionUrl = connectionUrl; } public String getDatabaseName() { return databaseName; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public String getCollectionName() { return collectionName; } public void setCollectionName(String collectionName) { this.collectionName = collectionName; } }
6.3 在log4j.prperties中配置
# mongodb输出 log4j.logger.mongodb=INFO, mongodb log4j.appender.mongodb=com.lidong.util.MongoAppender log4j.appender.mongodb.connectionUrl=mongodb://127.0.0.1:27017 log4j.appender.mongodb.databaseName=logs log4j.appender.mongodb.collectionName=logs_request
6.4在spring-mvc.xml中开启统一处理请求日志
<!-- web统一管理 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean class="com.lidong.util.WebLogAspect" />
6.5在数据库中查看保存的日志
代码地址
相关文章推荐
- httpd安全控制
- 基于winpcap获取设备列表
- RocketMQ源码学习---网络通信篇
- httpd基础
- tcp协议
- mitmproxy抓取https请求实践
- 虚拟机的几种网络连接方式
- 17 - 01 - 19 计算机网络(09)(子网传输)
- 交换机端口安全
- 分享下我的网络赚钱经验
- 网络IO
- PPP chap,pap认证
- android网络传输问题..跪求大神解答
- xmlhttp方法抓取下一页问题
- 交换机SPAN功能配置
- # 网络
- android fragment 中请求网络最好写在那个生命周期中
- http请求解析
- ios获取当前网络信息该怎么获取
- 如何实现多个activity与一个网络线程进行通信