Spring AOP实现统一日志打印框架
2016-12-12 19:39
381 查看
系统的使用过程中,经常需要去查看一些历史操作的记录,用于排错、追责、统计分析或一些其他的需求使用,所以定义一个易用的统一日志打印框架非常必要,此实例使用spring aop完成,可作参考。
首先,声明一个作为切点的注解RecordLog,注解带一个LogDict类型的参数,主要用于定义该接口的基本信息,比如:接口编号、接口功能、异常日志级别等。
以及Dict类对日志级别的定义,作为参考:
随后创建AOP的切面LogAspect,里面使用是常规AOP的方法,定义around方法打印日志,具体参考以下代码:
至此,简单的操作日志统一打印功能就完成了。
首先,声明一个作为切点的注解RecordLog,注解带一个LogDict类型的参数,主要用于定义该接口的基本信息,比如:接口编号、接口功能、异常日志级别等。
package com.alien.constant.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.alien.constant.enums.LogDict; /** * @Description: 日志注解,请在需要打印日志的方法上加入该注解 * @author alien * @date 2016年10月24日 下午3:22:10 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RecordLog { LogDict log() default LogDict.Un_Know; }LogDict是一个枚举类型,用于定义记录日志时关心的自定义字段,以下是几条示例:
Un_Know(Dict.Error_Level_Warn, 000000, "未知操作"), QUERY_USER_LIST(Dict.Error_Level_Warn, 100004, "查询用户列表"), ADD_USER(Dict.Error_Level_Error, 100006, "新增用户"), UPDATE_USER(Dict.Error_Level_Error, 100008, "修改用户"),
以及Dict类对日志级别的定义,作为参考:
Error_Level_Info (1, "一般", "操作日志记录,功能运行正常!"), Error_Level_Debug (2, "警告", "测试功能出错,对业务无影响,需要修复!"), Error_Level_Warn (3, "紧急", "显示功能出错,对业务无影响,需及时修复!"), Error_Level_Error (4, "严重", "修改功能出错,对业务有影响,短时间内可能不会引起业务问题,需尽快解决!"), Error_Level_Accident(5, "事故", "业务功能出错,已经对业务造成影响,需要马上解决!"), Error_Level_Fatal (6, "危险", "涉及系统运程问题,直接会导致系统宕机!"),定义好了切点,那么在需要记录日志的方法上加上这个注解,就可以自动记录下日志了,使用示例如下:
@RequestMapping("/work/UserController/queryUserList") @ResponseBody @RecordLog(log = LogDict.QUERY_USER_LIST) public ResponseInfo<List<User>> queryUserList(@Valid QueryListForm form,HttpServletRequest request,HttpServletResponse response) throws Exception { ResponseInfo<List<User>> responseInfo = new ResponseInfo<List<User>>(); List<User> list = userService.queryUserList(form); responseInfo.setCode(RtnConstant.code.APP_RET_SUCCESS_CODE); responseInfo.setMsg(RtnConstant.msg.APP_RET_SUCCESS_CODE); responseInfo.setData(list); return responseInfo; }
随后创建AOP的切面LogAspect,里面使用是常规AOP的方法,定义around方法打印日志,具体参考以下代码:
package com.alien.constant.aspect; import java.util.Enumeration; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alien.constant.BaseComponent; import com.alien.constant.FosConstant; import com.alien.constant.annotations.RecordLog; import com.alien.constant.enums.Dict; import com.alien.constant.enums.LogDict; import com.alien.service.OprLogService; import com.alien.utils.CommonUtils; import com.alien.vo.OprLog; import com.alien.vo.User; /** * @Description: 通过AOP来实现日志记录 * @author alien */ @Aspect @Component public class LogAspect extends BaseComponent { @Resource protected OprLogService logService; /** * 切入点:表示在哪个类的哪个方法进行切入。配置切入点表达式 */ @Pointcut("@annotation(com.alien.constant.annotations.RecordLog)") public void aspectPoint() { } /** * 前置通知 用于拦截RecordLog注解方法的执行,记录用户的操作 * * @param joinPoint切点 * @throws Throwable */ @Around("aspectPoint() && @annotation(recordLog)") public Object doAround(JoinPoint joinPoint, RecordLog recordLog) throws Throwable { long start = System.currentTimeMillis(); Object object = null; HttpServletRequest request = getRequest(joinPoint); try { object = ((ProceedingJoinPoint) joinPoint).proceed(); recordLogByDB(request, System.currentTimeMillis() - start, recordLog.log(), null); } catch (Throwable e) { recordLogByDB(request, System.currentTimeMillis() - start, recordLog.log(), e); throw e; } return object; } /** * @Description: 将日志写入数据表,并记录到文件 * @author alien * @date 2016年10月24日 下午3:25:25 * @version Version 1.0.1 */ private void recordLogByDB(HttpServletRequest request, long costTime, LogDict log, Throwable e) { try { // 记录DB日志 User loginUser = new User(); String url = ""; String params = ""; if (request != null) { loginUser = (User) request.getSession().getAttribute(FosConstant.LOGIN_SESSION_KEY); url = request.getRequestURL().toString(); params = getParams(request); } if (params.getBytes("UTF-8").length > 200) { params = params.substring(0, 199); } else if (params.endsWith("&")) { params = params.substring(0, params.length() - 2); } Dict level = e == null ? Dict.Error_Level_Info : log.getLevel(); String eMsg = e == null ? "执行成功" : e.getMessage().getBytes("UTF-8").length < 300 ? "执行失败:" + e.getMessage() : "执行失败:" + e.getMessage().substring(0, 299); String clientIp = CommonUtils.getClientIp(request); String serverIp = CommonUtils.getServerIp(); OprLog oprLog = new OprLog(); oprLog.setClientIp(clientIp); oprLog.setServerIp(CommonUtils.getServerIp()); oprLog.setCostTime(costTime); oprLog.setLogLevel(level.getName()); oprLog.setOperationDesc(eMsg); oprLog.setOperationStatus(e == null ? Dict.Result_success.getValue() : Dict.Result_success.getValue()); oprLog.setOperationName(log.getName()); oprLog.setOperator(loginUser == null ? "" : loginUser.getLoginName()); oprLog.setUrl(url); oprLog.setParams(params); logService.addLog(oprLog); // 记录文件日志 StringBuffer msg = new StringBuffer(); if (e != null) { msg.append("### 错误等级: ").append(level.getName()).append(" ### \n"); } msg.append("登录用户[").append(loginUser == null ? "unknow" : loginUser.getLoginName()).append(",") .append(clientIp).append("], "); msg.append("服务节点[").append(serverIp).append("], "); msg.append("功能[").append(log.getName()).append("], "); msg.append("耗时 ").append(costTime).append(" ms, "); msg.append(eMsg).append("! "); logger.info(msg.toString()); } catch (Exception exception) { logger.error("日志写入异常!", exception); } } /** * 从连接点获取request对象并且解析出log部分对象信息 * * @param joinPoint * @return */ public HttpServletRequest getRequest(JoinPoint joinPoint) throws Exception { HttpServletRequest request = null; if (null != RequestContextHolder.getRequestAttributes()) { request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); logger.debug("Find {} from RequestContextHolder", request); } if (null == request) { Object[] getArgs = joinPoint.getArgs();// 返回目标方法的参数 if (null != getArgs) { for (Object object : getArgs) { if (object instanceof HttpServletRequest) { request = (HttpServletRequest) object; break; } } } } return request; } /** * 从request中获取请求参数 * * @param request * @return * @throws Exception */ private String getParams(HttpServletRequest request) throws Exception { Enumeration<String> enu = request.getParameterNames(); StringBuffer sb = new StringBuffer(); while (enu.hasMoreElements()) { String paraName = (String) enu.nextElement(); sb.append(paraName); sb.append("="); sb.append(request.getParameter(paraName)); sb.append("&"); } return sb.toString(); } }日志记录操作包括将操作日志记录到数据库和记录日志文件,操作日志的数据表字段如下,作为参考。
至此,简单的操作日志统一打印功能就完成了。
相关文章推荐
- spring aop实现拦截接口请求打印日志
- 一种Java日志系统框架的设计与实现(转)
- 18、通过可变参数实现打印日志
- spring aop实现日志功能
- 日志框架统一切换之(二):切换步骤
- 日志框架统一切换之(一):需求分析
- 设计并实现日志和异常框架
- 日志系统框架的设计与实现
- 在tomcat日志里打印memcache日志,并实现session共享
- Java日志系统框架的设计与实现
- log4j定制类实现(一):配置间隔时间,定时打印日志
- Spring AOP 实现业务日志记录
- Mybatis源码研究4:日志框架的实现
- Struts+Spring+log4j框架下的日志管理的实现
- 一种Java日志系统框架的设计与实现
- spring统一打印error日志
- 函数体中打印日志信息自动附加函数名字的一种实现办法
- Mybatis源码研究4:日志框架的实现
- 利用log4j+mongodb实现分布式系统中日志统一管理
- Spring aop实现用户操作日志写入数据库