对于一些比较重要的管理系统,操作日志的记录是非常重要,它可以记录下管理人员的任何操作,也便于开发人员在故障中排查问题,可以维护和扩展的日志是尤为重要的。
注解的日志方便和实用,只需要在需要记录的日志方法上加入注解,下面不多说直接上代码
首先创建一个注解和日志实体
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
/** 日志操作类型 */
OperateTypes type();
/** 日志操作对象 */
OperateObj object();
/** 日志信息描述 */
String description() default "";
/**
* @Description:日志信息描述(支持实体类字段)
* 传值方式:根据实体类的字段名获取参数,(参数名与参数值":"分隔,参数之间","分隔)
* 如:用户名:userName,姓名:name,手机:phone
* @author XPY
* @date 2016年7月28日上午10:42:43
*/
String descriptionField() default "";
/**
* @Description:日志信息描述(支持表达式)
* 传值方式:{0}表示方法参数内的第一个参数,{1}表示方法参数内的第二个参数,以此类推
* 如:科目:{0},课程:{1}
* @author XPY
* @date 2016年7月28日上午10:42:43
*/
String descriptionRex() default "";
public class LogEntity{
/**
*
*/
private static final long serialVersionUID = 1L;
private Long id;
/**
* 操作人ID
*/
private Long userid;
/**
* 操作人名称
*/
private String userName;
/**
* 实体名称
*/
private String obj;
/**
* 操作类型
*/
private String type;
/**
* 日志字符串
*/
private String log;
/**
* 日志中记录的操作结果
*/
private String result;
/**
* 操作发生的时间戳
*/
private Date crtDate;
/**
* 发起请求的IP地址
*/
private String requestIP;
/**
* 异常信息
*/
private String error;
public LogEntity() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserid() {
return userid;
}
public void setUserid(Long userid) {
this.userid = userid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getObj() {
return obj;
}
public void setObj(String obj) {
this.obj = obj;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getLog() {
return log;
}
public void setLog(String log) {
this.log = log;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Date getCrtDate() {
return crtDate;
}
public void setCrtDate(Date crtDate) {
this.crtDate = crtDate;
}
public String getRequestIP() {
return requestIP;
}
public void setRequestIP(String requestIP) {
this.requestIP = requestIP;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
然后就是日志的切面类,包括日志的记录以及日志的描述
/**
*
* @Description 日志切入类
* @author XPY
* @date 2016年7月25日下午7:32:25
*/
@Aspect
@Component
public class SystemLogAspect {
private static String splitParaStr = ",";
private static String splitNameValueStr = ":";
// 注入Service用于把日志保存数据库
@Resource
private LogService logService;
// 本地异常日志记录对象
private static final Logger logger = LoggerFactory
.getLogger(SystemLogAspect.class);
// 基于注解的切入点,service层及controller层都可使用
@Pointcut("@annotation(com.noah.system.log.annotation.SystemLog)")
public void serviceAspect() {
}
/* @Around("serviceAspect()")
public Object process(ProceedingJoinPoint point) throws Throwable {
System.out.println("@Around:执行目标方法之前...");
//访问目标方法的参数:
Object[] args = point.getArgs(); //用改变后的参数执行目标方法 Object
Object returnValue = point.proceed(args);
returnValue = point.proceed(args);
System.out.println("@Around:执行目标方法之后...");
System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
return "原返回值:" + returnValue + ",这是返回结果的后缀";
}*/
/**
* 正常返回通知 用于拦截Service层记录用户的操作
*
* @param joinPoint
* 切点
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@AfterReturning(pointcut = "serviceAspect()")
public void doAfter(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
// 读取session中的用户
User user = (User) LiveUtils.getCurrentUser();
if (user == null) {
logger.info("=====读取用户信息失败=====");
return;
}
// 请求的IP
String ip = IpUtil.getIpAddress(request);
// 获取用户请求方法的参数并序列化为JSON格式字符串
/* String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
}
}*/
try {
// *========控制台输出=========*//
logger.info("=====正常返回通知开始=====");
logger.info("请求方法:"
+ (joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName() + "()"));
logger.info("方法描述:" + getServiceMethod(joinPoint).getLog());
logger.info("请求人:" + user.getName());
logger.info("请求IP:" + ip);
// *========数据库日志=========*//
Object[] param = joinPoint.getArgs();
Class[] parameterTypes = ((MethodSignature) joinPoint
.getSignature()).getMethod().getParameterTypes();
Method method = null;
String methodName = joinPoint.getSignature().getName();
Class targetClass = joinPoint.getTarget().getClass();
method = targetClass.getMethod(methodName, parameterTypes);
if (method != null) {
SystemLog ann = method.getAnnotation(SystemLog.class);
LogEntity log = (LogEntity) SpringBeanUtil.getBean("logEntity");
log.setType(getServiceMethod(joinPoint).getType());
log.setObj(getServiceMethod(joinPoint).getObj());
log.setRequestIP(ip);
log.setResult(Constants.SUCCESS);
log.setUserid(user.getId());
log.setUserName(user.getUserName());
log.setCrtDate(new Date());
log.setError("");
if (getServiceMethod(joinPoint).getLog().isEmpty()) {
log.setLog(getParamsRemark(ann, param));
}else{
log.setLog(getServiceMethod(joinPoint).getLog());
}
// 保存数据库
logService.add(log);
}
logger.info("=====正常返回通知结束=====");
} catch (Exception e) {
// 记录本地异常日志
logger.error("==正常返回通知异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
/**
* 异常通知 用于拦截service层记录异常日志
*
* @param joinPoint
* @param e
* @throws Throwable
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e)
throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
// 读取session中的用户
User user = (User) LiveUtils.getCurrentUser();
// 请求的IP
String ip = IpUtil.getIpAddress(request);
// 获取用户请求方法的参数并序列化为JSON格式字符串
String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
}
}
try {
/* ========控制台输出========= */
logger.info("=====异常通知开始=====");
logger.info("异常代码:" + e.getClass().getName());
logger.info("异常信息:" + e.getMessage());
logger.info("异常方法:"
+ (joinPoint.getTarget().getClass().getSimpleName() + "."
+ joinPoint.getSignature().getName() + "()"));
logger.info("方法描述:" + getServiceMethod(joinPoint).getLog());
logger.info("请求人:" + user.getName());
logger.info("请求IP:" + ip);
logger.info("请求参数:" + params);
/* ==========数据库日志========= */
Object[] param = joinPoint.getArgs();
Class[] parameterTypes = ((MethodSignature) joinPoint
.getSignature()).getMethod().getParameterTypes();
Method method = null;
String methodName = joinPoint.getSignature().getName();
Class targetClass = joinPoint.getTarget().getClass();
method = targetClass.getMethod(methodName, parameterTypes);
if (method != null) {
SystemLog ann = method.getAnnotation(SystemLog.class);
LogEntity log = (LogEntity) SpringBeanUtil.getBean("logEntity");
log.setType(getServiceMethod(joinPoint).getType());
log.setObj(getServiceMethod(joinPoint).getObj());
log.setRequestIP(ip);
log.setResult(Constants.FAILTURE);
log.setUserid(user.getId());
log.setUserName(user.getUserName());
log.setCrtDate(new Date());
log.setError("异常代码:" + e.getClass().getName()+"异常信息:" + e.getMessage());
if (getServiceMethod(joinPoint).getLog().isEmpty()) {
log.setLog(getParamsRemark(ann, param));
}else{
log.setLog(getServiceMethod(joinPoint).getLog());
}
// 保存数据库
logService.add(log);
logger.info("=====异常通知结束=====");
}
} catch (Exception ex) {
// 记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
}
/* ==========记录本地异常日志========== */
logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget()
.getClass().getName()
+ joinPoint.getSignature().getName(), e.getClass().getName(),
e.getMessage(), params);
}
/**
* 获取注解中对方法的描述信息 用于service层注解
*
* @param joinPoint
* 切点
* @return 方法描述
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static LogEntity getServiceMethod(JoinPoint joinPoint)
throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
LogEntity logEntity = new LogEntity();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
String type = method.getAnnotation(SystemLog.class).type().type;
String obj = method.getAnnotation(SystemLog.class).object().type;
String description = method.getAnnotation(SystemLog.class)
.description();
logEntity.setType(type);
logEntity.setObj(obj);
logEntity.setLog(description);
break;
}
}
}
return logEntity;
}
/**
* 通过java反射来从传入的参数object里取出我们需要记录的字段等属性
* @param obj 参数实体
* @param param 参数字段
* @return 参数字段的值
*/
private String getID(Object obj, String param) {
if (obj instanceof String) {
return obj.toString();
}
PropertyDescriptor pd = null;
Method method = null;
String v = "";
try {
pd = new PropertyDescriptor(param, obj.getClass());
method = pd.getReadMethod();
v = String.valueOf(method.invoke(obj));
} catch (Exception e) {
e.printStackTrace();
}
return v;
}
/**
* 通过反射调用bean的get方法或{1}这样的表达式生成描述信息
* {0}为第一位参数的值,{1}为第二位参数的值
* 如果参数为bean的情况可以"用户名:userName"这样的方式获取参数值
* @param param 所有参数
* @return 描述信息
*/
private String getParamsRemark(SystemLog annotation, Object[] param) {
String propertys = annotation.descriptionField();
String params = annotation.descriptionRex();
StringBuffer sb = new StringBuffer();
String desc1 = "";
String desc2 = "";
if (null != propertys && !"".equals(propertys)) {
String[] propertysRemark = annotation.descriptionField().split(
splitParaStr);
for (String property : propertysRemark) {// 按参数对象属性,取参数值
String name = property.split(splitNameValueStr)[0];
String value = property.split(splitNameValueStr)[1];
sb.append(name + splitNameValueStr + getID(param[0], value)
+ splitParaStr).append(",");
}
desc1 = sb.toString().substring(0, sb.length() - 1);
}
if(null != params && !"".equals(params)) {
List<String> list = StringUtil.getList("\\{(.*?)\\}", params);// 按参数位置取参数值
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
Object obj = param[Integer.parseInt(str)];
params = StringUtil.replease("\\{" + Integer.parseInt(str)
+ "\\}", params, obj.toString())+",";
}
desc2 = params;
}
return desc1+desc2;
}
}
public interface BeanSelfAware {
//AOP增强后的代理对象
public void setSelf(Object proxyBean);
}
/** * @Description 动态注入代理类 * @author XPY * @date 2016年7月28日下午2:08:25 */public class InjectBeanSelfProcessor implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof BeanSelfAware) { System.out.println("inject proxy:" + bean.getClass()); BeanSelfAware proxyBean = (BeanSelfAware)bean; proxyBean.setSelf(bean); return proxyBean; } return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } }
最终的使用方式,在方法加入注解即可
@SystemLog(type=OperateTypes.login,object=OperateObj.SysUser,description="登录系统")
public ModelAndView login(HttpServletRequest request,HttpSession session,ModelMap model) {}