Spring boot整合MongoDB做API监控日志记录

linux安装mongodb教程:https://www.runoob.com/mongodb/mongodb-linux-install.html
centos7安装mongodb教程参考:基于centos7的简易服务器搭建过程
AOP+自定义注解实现API监控教程:自定义注解+AOP实现请求日志记录


流程

      • 一、pom依赖
      • 二、yml配置
      • 三、Service接口:
      • 四、Service实现:
      • 五、Controller实现
      • 六、自定义注解
      • 七、AOP环绕通知添加日志
      • 八、在需要监控的API上加上注解
      • 九、访问接口后,查看mongodb数据库
      • 十、编写前端界面,查看日志信息
      • 十一、效果图

一、pom依赖

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-mongodbartifactId>
        dependency>

二、yml配置

spring:
  data:
    mongodb:
      uri: mongodb://www.mongodb.com:27017/technology

三、Service接口:

有用到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();
}

四、Service实现:

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;
        }
    }
}

五、Controller实现

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 "";
}

七、AOP环绕通知添加日志

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;
    }
}    

八、在需要监控的API上加上注解

Spring boot整合MongoDB做API监控日志记录_第1张图片


九、访问接口后,查看mongodb数据库

Spring boot整合MongoDB做API监控日志记录_第2张图片


十、编写前端界面,查看日志信息


<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>

十一、效果图

Spring boot整合MongoDB做API监控日志记录_第3张图片
Spring boot整合MongoDB做API监控日志记录_第4张图片

你可能感兴趣的:(编程技术,java,spring,mongodb)