您的位置:首页 > 编程语言 > Java开发

Spring AOP实现统一日志打印框架

2016-12-12 19:39 381 查看
    系统的使用过程中,经常需要去查看一些历史操作的记录,用于排错、追责、统计分析或一些其他的需求使用,所以定义一个易用的统一日志打印框架非常必要,此实例使用spring aop完成,可作参考。

    首先,声明一个作为切点的注解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();
}

}
日志记录操作包括将操作日志记录到数据库和记录日志文件,操作日志的数据表字段如下,作为参考。



至此,简单的操作日志统一打印功能就完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息