linux安装mongodb教程:https://www.runoob.com/mongodb/mongodb-linux-install.html
centos7安装mongodb教程参考:基于centos7的简易服务器搭建过程
AOP+自定义注解实现API监控教程:自定义注解+AOP实现请求日志记录
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
spring:
data:
mongodb:
uri: mongodb://www.mongodb.com:27017/technology
有用到mybatis的pagehelper插件,仅仅只是用于保存数据,可自己实现一个PageHelper
import com.github.pagehelper.PageInfo;
import com.nys.technology_springboot.bean.APIDealInfo;
import com.nys.technology_springboot.bean.APIRecord;
import java.util.List;
/**
* API调用情况保存到mongodb——业务层接口
* @author nysheng
*/
public interface IAPIRecordService {
PageInfo<APIRecord> getAPIRecordList(String requestPath, Integer pageNum, Integer pageSize);
void addAPIRecord(APIRecord apiRecord);
List<APIDealInfo> getAPIDealInfoList();
}
import com.github.pagehelper.PageInfo;
import com.nys.technology_springboot.bean.APIDealInfo;
import com.nys.technology_springboot.bean.APIRecord;
import com.nys.technology_springboot.exceptions.ServiceException;
import com.nys.technology_springboot.service.IAPIRecordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* API调用情况保存到mongodb——业务层实现
* @author nysheng
*/
@Service
public class APIRecordServiceImpl implements IAPIRecordService {
@Autowired
private MongoTemplate mongoTemplate;
Logger logger= LoggerFactory.getLogger(APIRecordServiceImpl.class);
/**
* 分页查询指定API执行信息
* @param requestPath
* @param pageNum
* @param pageSize
* @return
*/
@Override
public PageInfo<APIRecord> getAPIRecordList(String requestPath, Integer pageNum, Integer pageSize) {
if(StringUtils.isEmpty(requestPath)||pageNum==null||pageSize==null){
throw new ServiceException(this.getClass().getName()+".getAPIRecordList():获取API记录失败");
}
try{
Criteria criteria=Criteria.where("requestPath").is(requestPath);
//查询分页数据
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.sort(Sort.Direction.DESC, "startTime"),
Aggregation.skip((long) ((pageNum - 1) * pageSize)),
Aggregation.limit(pageSize)
);
AggregationResults<APIRecord> aggregationResults = mongoTemplate.aggregate(aggregation, "apirecord", APIRecord.class);
List<APIRecord> apiRecordList = aggregationResults.getMappedResults();
//查询总条数
Long count=mongoTemplate.count(new Query(criteria),APIRecord.class);
PageInfo<APIRecord> pageInfo=new PageInfo(apiRecordList);
pageInfo.setPageNum(pageNum);
pageInfo.setPageSize(pageSize);
pageInfo.setTotal(count);
pageInfo.setPages((int)(Math.ceil((double)count/pageSize)));
return pageInfo;
}catch (Exception e){
e.printStackTrace();
logger.error("【Mongodb消息提示】查询失败:{}",e.getMessage());
return null;
}
}
/**
* 新增API执行记录
* @param apiRecord
*/
@Override
public void addAPIRecord(APIRecord apiRecord) {
try {
mongoTemplate.save(apiRecord);
}catch (Exception e){
e.printStackTrace();
logger.error("【Mongodb消息提示】新增失败:{}",e.getMessage());
}
}
/**
* 获取API执行统计情况
* @return
*/
@Override
public List<APIDealInfo> getAPIDealInfoList() {
try {
//计算总执行次数,平均时间,最小时间,最大时间,总时间,,,记录api地址
final Aggregation aggregation=Aggregation.newAggregation(
Aggregation.group("requestPath")
.count().as("totalExec")
.avg("requestTime").as("avgDate")
.min("requestTime").as("minDate")
.max("requestTime").as("maxDate")
.sum("requestTime").as("totalDate")
.first("requestPath").as("url"),
Aggregation.sort(Sort.Direction.DESC, "totalExec")
);
AggregationResults<APIDealInfo> aggregate = mongoTemplate.aggregate(aggregation,"apirecord", APIDealInfo.class);
List<APIDealInfo> mappedResults = aggregate.getMappedResults();
return mappedResults;
}catch (Exception e){
e.printStackTrace();
logger.error("【Mongodb消息提示】查询失败:{}",e.getMessage());
return null;
}
}
}
import com.github.pagehelper.PageInfo;
import com.nys.technology_springboot.bean.APIDealInfo;
import com.nys.technology_springboot.bean.APIRecord;
import com.nys.technology_springboot.service.IAPIRecordService;
import com.nys.technology_springboot.vo.ResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 日志查看接口定义
* @author nysheng
*/
@RestController
@RequestMapping("/apiRecord")
public class APIRecordController {
@Autowired
private IAPIRecordService apiRecordService;
@GetMapping("/get")
public ResultVO get(){
List<APIDealInfo> apiDealInfoList = apiRecordService.getAPIDealInfoList();
return ResultVO.success().add("apiDealInfoList",apiDealInfoList);
}
@GetMapping("/getByURL")
public ResultVO getByURL(@RequestParam("url")String url,@RequestParam("pageNum")Integer pageNum,@RequestParam("pageSize")Integer pageSize){
PageInfo<APIRecord> pageInfo = apiRecordService.getAPIRecordList(url, pageNum, pageSize);
return ResultVO.success().add("pageInfo",pageInfo);
}
}
自定义注解+AOP横向记录接口调用日志
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解-API监控
* @author nysheng
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiEyes {
String value() default "";
}
import com.nys.technology_springboot.annotation.ApiEyes;
import com.nys.technology_springboot.bean.APIRecord;
import com.nys.technology_springboot.service.IAPIRecordService;
import com.nys.technology_springboot.service.admin.IAdminLogService;
import com.nys.technology_springboot.utils.DateUtil;
import com.nys.technology_springboot.utils.IPUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* AOP实现
* @author nysheng
*/
@Aspect
@Component
public class MyAspect {
@Autowired
private IAPIRecordService apiRecordService;
//日志记录
private Logger logger= LoggerFactory.getLogger(MyAspect.class);
//切入点配置为注解@ApiEyes
@Pointcut("@annotation(com.nys.technology_springboot.annotation.ApiEyes)")
public void apiEyes(){
}
/**
* 环绕通知——监听接口调用
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("apiEyes()")
public Object around4ApiEyes(ProceedingJoinPoint joinPoint) throws Throwable {
//1.请求开始
logger.info("【ApiEyes消息】=====请求进入======");
long start=System.currentTimeMillis();
//2.获取注解对象
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature=(MethodSignature)signature;
Method method = methodSignature.getMethod();
ApiEyes apiEyes = method.getAnnotation(ApiEyes.class);
//3.获取注解属性
String value = apiEyes.value();
//4.获取request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//封装数据
APIRecord apiRecord=new APIRecord();
apiRecord.setRequestPath(request.getRequestURL().toString());
apiRecord.setRequestMethod(request.getMethod());
apiRecord.setRequestIP(IPUtil.getIpAddress(request));
apiRecord.setRequestMsg(value);
apiRecord.setStartTime(DateUtil.dateFormat(start));
//5.记录日志
logger.info("【ApiEyes消息】请求链接:{}",apiRecord.getRequestPath());
logger.info("【ApiEyes消息】请求类型:{}",apiRecord.getRequestMethod());
logger.info("【ApiEyes消息】请求IP:{}",apiRecord.getRequestIP());
logger.info("【ApiEyes消息】请求描述:{}",apiRecord.getRequestMsg());
//6.执行
Object object=joinPoint.proceed();
//7.请求结束
long end=System.currentTimeMillis();
apiRecord.setRequestTime(end-start);
apiRecordService.addAPIRecord(apiRecord);
logger.info("【ApiEyes消息】请求用时:{}",apiRecord.getRequestTime());
logger.info("【ApiEyes消息】=====请求结束======");
return object;
}
}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>接口日志记录title>
<link rel="stylesheet" href="/statics/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="/statics/js/jquery.js">script>
<script src="/statics/bootstrap-3.3.7-dist/js/bootstrap.min.js">script>
head>
<style>
#api-record-container{
height: 100%;
overflow-y: auto;
}
#api_record_detail_div{
height: 425px;
overflow-y: auto;
margin-left: 15px;
margin-bottom: 20px;
}
#api_record_detail_more{
width: 100%;
border: 0px;
}
style>
<body>
<div class="modal fade" id="api_record_detail_modal" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×button>
<h4 class="modal-title">
接口调用详情
h4>
div>
<div class="modal-body">
<div id="api_record_detail_div">
<table class="table table-hover" id="api_record_detail_table">
<thead>
<tr>
<th>序号th>
<th>请求路径th>
<th>请求方法th>
<th>请求IPth>
<th>请求描述th>
<th>请求耗时th>
<th>请求时间th>
tr>
thead>
<tbody>
tbody>
table>
<button style="display: none" id="api_record_detail_more" type="button" class="btn btn-default">加载更多...button>
div>
div>
div>
div>
div>
<div class="container" id="api-record-container">
<div class="row">
<div>
<h1>接口调用日志h1>
div>
div>
<div class="row">
<div>
<table class="table table-hover" id="api-record-table">
<thead>
<tr>
<th>接口th>
<th>总执行次数th>
<th>总执行时间th>
<th>平均用时th>
<th>最快th>
<th>最慢th>
<th>操作th>
tr>
thead>
<tbody>
tbody>
table>
div>
div>
div>
body>
<script>
$(function () {
init_apiRecord();
//查看详情按钮事件
$(document).off("click",".api-record-btn-detail").on("click",".api-record-btn-detail",function(){
$("#api_record_detail_table tbody").empty();
//获取接口调用日志
var url=$(this).attr("url");
get_api_record_detail(url,1,10);
$("#api_record_detail_modal").modal({
backdrop : "static"
});
});
});
function init_apiRecord() {
$.ajax({
url : "http://localhost:8082/apiRecord/get",
type : "GET",
xhrFields:{
withCredentials:true
},
dataType:"json",
success : function(result) {
if(result.code==1){
//解析显示单元格数据
build_api_record_table(result);
}else{
alert(result.data.errorMsg);
}
}
});
}
//解析显示单元格数据
function build_api_record_table(result) {
$("#api-record-table tbody").empty();
var apiDealInfoList=result.data.apiDealInfoList;
$.each(apiDealInfoList,function (index, item) {
var url=$(" ").append(item.url);//api
var totalExec=$(" ").append(item.totalExec);//总执行次数
var totalDate=$(" ").append(item.totalDate+"ms");//总执行时间
var avgDate=$(" ").append(item.avgDate+"ms");//平均用时
var minDate=$(" ").append(item.minDate+"ms");//最快用时
var maxDate=$(" ").append(item.maxDate+"ms");//最慢用时
var detailButton=$("").addClass(
"btn btn-primary btn-sm btn-modify api-record-btn-detail").append(
$("").addClass(
"glyphicon glyphicon-pencil")).append("详情");
detailButton.attr("url", item.url);
var tr=$(" ")
.append(url)
.append(totalExec)
.append(totalDate)
.append(avgDate)
.append(minDate)
.append(maxDate)
.append(detailButton);
$("#api-record-table tbody").append(tr);
})
}
//获取接口调用日志
function get_api_record_detail(url,pageNum, pageSize) {
$.ajax({
url : "http://localhost:8082/apiRecord/getByURL",
data: "url="+url+"&pageNum="+pageNum+"&pageSize="+pageSize,
type : "GET",
xhrFields:{
withCredentials:true
},
dataType:"json",
success : function(result) {
if(result.code==1){
console.log(result);
//解析显示详情单元格数据
build_api_record_detail_table(result);
//日志分页处理
build_page_for_api_record_detail(url,result);
}else{
alert(result.data.errorMsg);
}
}
});
}
//解析显示详情单元格数据
function build_api_record_detail_table(result) {
var pageInfo=result.data.pageInfo;
var apiRecordList=pageInfo.list;
$.each(apiRecordList,function (index, item) {
var order=$(" ").append((pageInfo.pageNum-1)*pageInfo.pageSize+index+1);
var requestPath=$(" ").append(item.requestPath);//请求路径
var requestMethod=$(" ").append(item.requestMethod);//请求方法
var requestIP=$(" ").append(item.requestIP);//请求IP
var requestMsg=$(" ").append(item.requestMsg);//请求描述
var requestTime=$(" ").append(item.requestTime+"ms");//请求耗时
var startTime=$(" ").append(item.startTime);//开始时间
$(" ").append(order).append(requestPath).append(requestMethod)
.append(requestIP).append(requestMsg).append(requestTime).append(startTime)
.appendTo("#api_record_detail_table tbody")
})
}
//日志分页处理
function build_page_for_api_record_detail(url,result) {
var pageInfo=result.data.pageInfo;
if(pageInfo.pageNum<pageInfo.pages){
$("#api_record_detail_more").show();
$("#api_record_detail_more").off("click").click(function () {
get_api_record_detail(url,pageInfo.pageNum+1,pageInfo.pageSize);
});
}else{
$("#api_record_detail_more").hide();
}
}
script>
html>