统一接口设计及日志管理
时间:2022-06-18
本文章向大家介绍统一接口设计及日志管理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
对系统中的关键操作进行记录至关重要,尤其是在对某些重要业务或数据信息进行溯源时
日志的记录越详细越好,但出于性能及业务等因素考虑,侧重点会各有不同
最基本的记录至少要包括如下信息:
1.所操作的接口
2.操作人
3.操作时间及设备信息
4.进行了何种操作
5.操作是否成功
日志记录方式无非就两种
1.高度代码耦合:在业务逻辑中直接调用日志记录接口
2.采用AOP方式:AOP方式能和业务逻辑解耦
第1种方式基本被淘汰,介绍第2种方式
采用AOP方式记录日志,则要保证接口格式一致性,这样才能方便获取接口返回的相关信息
接口返回应该包括几个方面:
1.业务数据信息
2.执行状态
3.若失败还要返回错误码
4.若失败还要返回错误信息
同时为了方便统一日志记录,还应该在每个接口中返回具体的日志信息,不过不用展示出来
所以,基本格式应该如下:
1.成功时:
{
"content": {
"sessionId": "10009",
"userId": 10,
"userName": "肖昌伟",
"lastOperateTime": "2017-08-01 15:47:42",
"token": "2d7bb2f683704cdc8baa7ccd8e993c33",
"ip": "192.168.0.1"
},
"status": "OK",
"errorCode": null,
"errorMsg": null
}
2.失败时:
{
"errorCode": "000002",
"errorMsg": "登录名[userName]不能为空, 当前值为[null]",
"status": "ERROR"
}
在业务处理后要将具体的操作详情保存到返回接口里面(不用展示出来,只为了方便在aop中获取)
日志记录的其它相关信息,比如访问的是那个模块的哪个接口等,可以通过自定义注解方式来实现
package personal.changw.xiao.web.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @since 2017年07月31日 上午11:02:29
* @author 肖昌伟 changw.xiao@qq.com
* @description 日志记录标签
* 可以使用在方法或者类上,可以根据需要决定谁的优先级更高
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RUNTIME)
public @interface LogRecord {
String system() default "";
String module() default "";
String menuLv1() default "";
String menuLv2() default "";
}
使用方式如下:
/**
* 分页查找
* @param keyWords 关键字
* @param pageSize 每页条数
* @param pageNumber 页码
* @param isPaging 是否分页(默认分页),为false时返回的条数信息请忽略
* @return
*/
@RequestMapping("/user/listByPage")
@LogRecord(system="xxxx系统",module="基础信息管理",menuLv1="用户管理",menuLv2="用户列表查询")
public Result listUserByPgae(@HibernateValidate UserQueryParam param) {
//Page<UserInfo> page = new Page<UserInfo>(param.getIsPaging(), param.getPageNumber(), param.getPageSize());
Page<UserInfo> page = new Page<UserInfo>(param);
userService.listUserByPgae(page, param);
return success(page,"查询用户列表,参数为:"+JSON.toJSONString(page));
}
AOP执行逻辑如下
package personal.changw.xiao.web.aop;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import personal.changw.xiao.web.annotation.LogRecord;
import personal.changw.xiao.web.constant.Constants;
import personal.changw.xiao.web.service.LogRecordService;
import personal.changw.xiao.web.utils.IpUtil;
import personal.changw.xiao.web.vo.common.OperaterLogVo;
import personal.changw.xiao.web.vo.common.Result;
import personal.changw.xiao.web.vo.common.UserSession;
/**
* @since 2017年07月31日 上午11:02:29
* @author 肖昌伟 changw.xiao@qq.com
* @description 日志记录AOP
*/
public class LogRecordAOP {
private static final Logger LOGGER = LoggerFactory.getLogger(LogRecordAOP.class);
@Autowired
private LogRecordService logRecordService;
@Autowired
private HttpServletRequest request;
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//先查找类上的注解,没有再找方法上的注解
LogRecord logRecord = joinPoint.getTarget().getClass().getAnnotation(LogRecord.class);
if(logRecord == null){
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
logRecord = method.getAnnotation(LogRecord.class);
}
if (logRecord != null) {
Result result = (Result) joinPoint.proceed();
UserSession userSession = (UserSession) request.getAttribute(Constants.HTTP_ATTR_SESSION_KEY);
if (userSession != null) {
OperaterLogVo operatorLog = new OperaterLogVo();
if(result != null) {
operatorLog.setRemarks(result.getStatus());
operatorLog.setLogContent(result.getLogContent());
} else {
//系统中导出功能等返回结果为非Result的接口
//默认都为成功
operatorLog.setRemarks("OK");
}
operatorLog.setSystemName(logRecord.system());
operatorLog.setModuleName(logRecord.module());
operatorLog.setMenuLv1(logRecord.menuLv1());
operatorLog.setMenuLv2(logRecord.menuLv2());
String requestURI = request.getRequestURI().substring(request.getContextPath().length());
operatorLog.setUrl(request.getProtocol().split("/")[0] + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + requestURI);
operatorLog.setUserCode(String.valueOf(userSession.getUserId()));
operatorLog.setUserName(userSession.getUserName());
operatorLog.setIp(IpUtil.getRemoteRealIP(request));
try {
logRecordService.insertOperatorLog(operatorLog);
} catch (Exception e) {
LOGGER.error("日志记录出错:"+ e.getMessage());
}
}
return result;
} else {
return joinPoint.proceed();
}
}
}
通过上面的操作,配置好切面后就可进行日志记录了
日志记录量是很大的,所以只记录关键地方并按期归档,最好是存在如elasticsearch、mongodb中,
如果存在数据库中,分表是不错的选择
- 你和PPT高手之间,就只差一个iSlide
- 如何在原生微信小程序中实现数据双向绑定
- 追溯 React Hot Loader 的实现
- 【推荐】开源项目minapp-重新定义微信小程序的开发
- 【完结汇总】iKcamp出品基于Koa2搭建Node.js实战共十一堂课(含视频)
- hadoop性能调优
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 规范与部署
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 错误处理
- DiscuzX v3.4 任意文件删除漏洞
- 系列3|走进Node.js之多进程模型
- Java中Arraylist与linkedlist的区别
- 手把手教你撸一个 Webpack Loader
- HashMap与HashTable区别
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- jQuery 介绍 以及基本使用
- 答应我,用了这个jupyter插件,别再重复造轮子了
- 商业数据分析从入门到入职(9)Python网络数据获取
- 谈一谈还原解包后小程序页面wxss样式的若干方法
- 什么?不使用selenium爬京东评论?你是不是在骗我
- 骚操作,Python操作PPT,你会吗?
- 用了这个jupyter插件,我已经半个月没打开过excel了
- Mística:一款支持任意协议的应用程序通信工具
- 为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?
- 原创 | codefroces中的病毒,这题有很深的trick,你能解开吗?
- 原创 | git的远程分支是干啥的,和本地的有什么区别?
- 京东技术主导:全新架构的分布式事务Hmily 2.1.1发布
- iOS音视频接入-TRTC接入前期key、秘钥等准备
- 你一定不知道的 Linux 使用技巧
- 当 Python 爬虫搭配起 Bilibili 唧唧,奇怪的生产力出现了