spring的aop功能可以在尽量减少代码侵入的情况下对原有的功能进行扩展和监控,用来做日志是最适合不过的了。
开发web服务器时需要记录用户的访问和返回信息的日志,因为需求较晚,原有服务代码较多,懒得修改,所以就想起了spring框架的aop功能来实现一个监控日志。
服务器使用框架:spring boot+mongodb,使用gradle构建
要使用aop功能,需要添加依赖:
"org.springframework.boot:spring-boot-starter-aop:1.2.5.RELEASE",
服务的所有controller都放在 org.youqu.server.controller 包下 ,创建一个日志类ControllerAopLogger用于记录日志。
spring相关配置如下:
<bean name="aopLogger" class="org.youqu.server.aspect.ControllerAopLogger"/> <aop:config> <aop:aspect ref="aopLogger"> <aop:before method="before" pointcut="execution(* org.youqu.server.controller.*.*.*(..))"/> <aop:after-returning method="after" pointcut="execution(* org.youqu.server.controller.*.*.*(..))" returning="returnValue"/> <aop:after-returning method="error" pointcut="execution(* org.youqu.server.controller.BaseController.exceptionHandler(..))" returning="returnValue"/> </aop:aspect> </aop:config>
此处包括了三个切面。
前两个是标准流程的切面:
aop:before标志在执行前,获取输入信息的切面,此处处理纯粹的是为了打印日志便与调试。避免因为报错忽略了部分输入日志。
aop:after-returning表示在返回之后,获取返回信息的切面
切面的描述语句:
execution(* org.youqu.server.controller.*.*.*(..))
第一个*表示返回值,然后就是org.youquer.server.controller包下的所有子包下得所有类的所有有任意个参数的方法。
恩,很拗口。简单地说:
最后的括号里面代表参数,(..)表示任意个参数。
倒数第一个*代表方法名称,倒数第二个星号代表类名称,然后就全都是包名,一层一层的包名。
然后是第三个切面,因为之前将所有的异常都统一进行了捕获和处理,所以在此选择了处理方法的返回参数来记录报错的信息
ControllerAopLogger的核心代码如下:
public void after(JoinPoint point,Object returnValue){ if(!(point.getThis() instanceof BaseController)) return; BaseController bc = (BaseController) point.getThis(); HttpServletRequest httpServletRequest = bc.getRequest(); String url = ""; if(httpServletRequest.getRequestURI()!=null) url = httpServletRequest.getRequestURI().toString(); long requestDate = System.currentTimeMillis(); String clientIp = getIpAddr(httpServletRequest); String requestMethod = httpServletRequest.getMethod(); String charset = httpServletRequest.getCharacterEncoding(); Object[] args = point.getArgs(); String executeMethod = point.getSignature().getName(); String result = returnValue.toString(); float resultLength = ((returnValue.toString().length()+40f)/1024f); if(result.length()>100) log.info("返回参数:"+result.substring(0,100)); else log.info("返回参数:"+result); log.info("返回参数大小:"+resultLength); log.info("--------------------------------"); BasicDBObject logUnit = new BasicDBObject(); logUnit.append("url",url) .append("requestDate", requestDate) .append("clientIp",clientIp) .append("requestMethod",requestMethod) .append("charset",charset) .append("args",args) .append("executeMethod", executeMethod) .append("result",result) .append("resultLength", resultLength) .append("status", 200); mongoDao.create(Attributes.MONGO_COL_LOG, logUnit); }
这个方法干什么的参照配置文件。目的在于获取各种需要记录的信息,最后保存到mongodb数据库中。
BaseController是我自己创建的所有controller的父类。包括了获取HttpServletRequest/Response和捕获异常的功能。
以上就完成了记录访问日志的功能。需要打印到日志文件的也可以使用各种log4j,logback之类的库进行处理。