首页 > 其他分享 >018-管理后台操作日志功能开发

018-管理后台操作日志功能开发

时间:2023-08-29 14:04:35浏览次数:40  
标签:String request private 后台 logInfo 018 import 日志 请求

1.功能分析

1.1. 查询列表

1.1.1. 页面效果

018-管理后台操作日志功能开发_java

1.1.2. 功能要求

  • 分页查询默认查询10条每页从第1页开始查询
  • 日志只提供查询操作
  • 搜索条件
  • 日志来源:精准搜索
  • 请求ip:精准搜索
  • 点击搜索按钮是按照录入的搜索条件进行查询数据并渲染
  • 点击重置按钮的时候清空搜索条件,并重新渲染数据

1.2.插入日志

1.2.1. 功能要求

  • 日志参数包含:请求ip,请求地址,浏览器信息(如果无则为空),请求时间,请求来源(管理后台/博客前台),请求方法,模块信息,请求参数,响应结果,响应时间,响应时长(s),方法描述,日志类型(新增/修改等等),创建时间,创建人编号(如果为客户端访问则填充ip信息),创建人名称(如果为客户端访问则填充ip信息)

1.3查看详情

1.3.1. 页面效果

018-管理后台操作日志功能开发_博客_02

1.3.2. 功能要求

  • 页面仅查看无法进行操作

2.功能实现

2.1. 初期准备

2.1.1. 创建数据库 zh_log_info

CREATE TABLE `zh_log_info` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `request_ip` varchar(40) DEFAULT NULL COMMENT '请求ip',
  `request_url` varchar(255) DEFAULT NULL COMMENT '请求地址',
  `request_browser` varchar(255) DEFAULT NULL COMMENT '日志请求浏览器',
  `request_source` varchar(40)  DEFAULT NULL COMMENT '请求来源 manager管理后台 portal博客前台',
  `request_method` varchar(255) DEFAULT NULL COMMENT '请求方法',
  `request_params` text COMMENT '请求参数',
  `request_result` longtext COMMENT '请求响应结果',
  `request_start_time` datetime DEFAULT NULL COMMENT '请求开始时间',
  `request_end_time` datetime DEFAULT NULL COMMENT '响应时间/请求结束时间',
  `response_interval` bigint DEFAULT NULL COMMENT '响应时长(s)',
  `log_type` varchar(40) DEFAULT NULL COMMENT '日志类型(类型里面的value)',
  `log_identifying` varchar(40) DEFAULT NULL COMMENT '日志标识(类型里面的key)',
  `modual_info` varchar(100) DEFAULT NULL COMMENT '模块信息/请求模块',
  `method_desc` varchar(255) DEFAULT NULL COMMENT '方法描述',
  `method_type` varchar(255) DEFAULT NULL COMMENT '请求方法类别 GET/POST/PUT/DELETE',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `create_user_code` varchar(255) DEFAULT NULL COMMENT '创建人标识',
  `create_user_name` varchar(255) DEFAULT NULL COMMENT '创建人名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='日志记录表';

2.1.2. 创建控制层LogInfoController

package com.zhuhuo.modual.controller.manager;

import com.zhuhuo.modual.service.LogInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping(value = "/m/logInfo")
public class LogInfoController {

    @Autowired
    private LogInfoService logInfoService;

}

2.1.3. 创建实体映射LogInfo

package com.zhuhuo.modual.entity;

import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Id;


@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "zh_log_info")
public class LogInfo implements Serializable {

  /**
   * id
   */
  @Id
  private Long id;
  /**
   * 请求ip
   */
  private String requestIp;
  /**
   * 请求地址
   */
  private String requestUrl;
  /**
   * 日志请求浏览器
   */
  private String requestBrowser;
  /**
   * 请求来源 1管理后台 2博客前台
   */
  private Byte requestSource;
  /**
   * 请求方法
   */
  private String requestMethod;
  /**
   * 请求参数
   */
  private String requestParams;
  /**
   * 请求响应结果
   */
  private String requestResult;
  /**
   * 请求开始时间
   */
  private Date requestStartTime;
  /**
   * 响应时间/请求结束时间
   */
  private Date requestEndTime;
  /**
   * 响应时长(s)
   */
  private Integer responseInterval;
  /**
   * 日志类型(类型里面的value)
   */
  private String logType;
  /**
   * 日志标识(类型里面的key)
   */
  private String logIdentifying;
  /**
   * 模块信息/请求模块
   */
  private String modualInfo;
  /**
   * 方法描述
   */
  private String methodDesc;
  /**
   * 请求方法类别 GET/POST/PUT/DELETE
   */
  private String methodType;
  /**
   * 创建时间
   */
  private Date createTime;
  /**
   * 创建人标识
   */
  private String createUserCode;
  /**
   * 创建人名称
   */
  private String createUserName;
    
}

2.1.4. 创建LogInfoMapper, LogInfoMapper.xml

package com.zhuhuo.modual.mapper;

import com.zhuhuo.core.frame.mapper.BasicsMapper;
import com.zhuhuo.modual.entity.LogInfo;

public interface LogInfoMapper extends BasicsMapper<LogInfo>{

}
<resultMap id="BaseResultMap" type="com.zhuhuo.modual.entity.LogInfo">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="request_ip" property="requestIp" jdbcType="VARCHAR"/>
        <result column="request_url" property="requestUrl" jdbcType="VARCHAR"/>
        <result column="request_browser" property="requestBrowser" jdbcType="VARCHAR"/>
        <result column="request_source" property="requestSource" jdbcType="VARCHAR"/>
        <result column="request_method" property="requestMethod" jdbcType="VARCHAR"/>
        <result column="request_params" property="requestParams" jdbcType="LONGVARCHAR"/>
        <result column="request_result" property="requestResult" jdbcType="VARCHAR"/>
        <result column="request_start_time" property="requestStartTime" jdbcType="TIMESTAMP"/>
        <result column="request_end_time" property="requestEndTime" jdbcType="TIMESTAMP"/>
        <result column="response_interval" property="responseInterval" jdbcType="BIGINT"/>
        <result column="log_type" property="logType" jdbcType="VARCHAR"/>
        <result column="log_identifying" property="logIdentifying" jdbcType="VARCHAR"/>
        <result column="modual_info" property="modualInfo" jdbcType="VARCHAR"/>
        <result column="method_desc" property="methodDesc" jdbcType="VARCHAR"/>
        <result column="method_type" property="methodType" jdbcType="VARCHAR"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="create_user_code" property="createUserCode" jdbcType="VARCHAR"/>
        <result column="create_user_name" property="createUserName" jdbcType="VARCHAR"/>
    </resultMap>

    <sql id="base_column_list">
        id, request_ip, request_url, request_browser, request_source, request_method, request_params, 
        request_result,request_start_time, request_end_time, response_interval, log_type, log_identifying,
        modual_info, method_desc,method_type, create_time, create_user_code, create_user_name
    </sql>

2.1.5. 创建LogInfoService ,LogInfoServiceImpl

package com.zhuhuo.modual.service;


public interface LogInfoService {

}
package com.zhuhuo.modual.service.impl;

import com.zhuhuo.modual.service.LogInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("logInfoService")
public class LogInfoServiceImpl implements LogInfoService {

    @Autowired
    private LogInfoMapper logInfoMapper;

}

2.1. 添加日志

2.1.1 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>eu.bitwalker</groupId>
    <artifactId>UserAgentUtils</artifactId>
    <version>1.21</version>
</dependency>

2.1.2 创建日志枚举LogType

@Getter
@AllArgsConstructor
public enum LogType {

    /**
     * 新增
     */
    INSERT("insert","新增"),

    /**
     * 修改
     */
    UPDATE("update","修改"),

    /**
     * 删除
     */
    DELETE("delete","删除"),

    /**
     * 查看
     */
    VIEW("view","查看"),

    /**
     * 默认的
     */
    DEFAULTS("default","默认"),
    ;

    /**
     * 枚举标识
     */
    private String code;


    /**
     * 枚举描述
     */
    private String desc;


}

2.1.3 创建枚举LogSource

@Getter
@AllArgsConstructor
public enum LogSource {

    /**
     * 管理后台枚举
     */
    MANAGER("manager","管理后台"),

    /**
     * 博客前台枚举
     */
    PORTAL("portal","博客前台");

    /**
     * 枚举标识
     */
    private String code;


    /**
     * 枚举描述
     */
    private String desc;


}

2.1.4 创建注解ActionLog

package com.zhuhuo.core.logs.annotation;

import com.zhuhuo.core.logs.enums.LogSource;
import com.zhuhuo.core.logs.enums.LogType;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActionLog {

    /**
     * 模块相关
     * @return
     */
    String modual() default "";

    /**
     * 方法描述
     * @return
     */
    String methodDesc() default "";

    /**
     * 日志请求来源
     * @return
     */
    LogSource source() default LogSource.MANAGER;


    /**
     * 日志类别
     * @return
     */
    LogType logtype() default LogType.DEFAULTS;

}

2.1.5 创建切面LogAspect

package com.zhuhuo.core.logs.aspect;

import cn.hutool.http.ContentType;
import com.zhuhuo.common.JacksonUtil;
import com.zhuhuo.common.RequestUtil;
import com.zhuhuo.core.logs.annotation.ActionLog;
import com.zhuhuo.modual.entity.LogInfo;
import com.zhuhuo.modual.mapper.LogInfoMapper;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private LogInfoMapper logInfoMapper;

    /**
     * 配置日志的切入点
     */
    @Pointcut("@annotation(com.zhuhuo.core.logs.annotation.ActionLog)")
    public void logPointCut(){

    }


    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //做一些事情
        //1.获取日志的注解 拿到模块信息,方法描述,日志类别,日志来源
        ActionLog actionLog = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(ActionLog.class);
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        //2.通过HttpServletRequest 获取请求相关信息 例如 请求地址 请求的ip信息,请求参数
        HttpServletRequest request = RequestUtil.getHttpServletRequest();
        //3.计算我们程序整体的请求响应时间
        long startTimeMillis = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTimeMillis = System.currentTimeMillis();
        long diffTimeMillis = endTimeMillis - startTimeMillis;
        //4.获取浏览器相关信息
        final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        //5.拼接对应的参数到日志表里面进行存储
        LogInfo logInfo = new LogInfo();
        logInfo.setRequestIp(RequestUtil.getHttpServletRequestIpAddress());
        logInfo.setRequestUrl(request.getRequestURI());
        logInfo.setRequestStartTime(new Date(startTimeMillis));
        logInfo.setRequestEndTime(new Date(endTimeMillis));
        logInfo.setResponseInterval(diffTimeMillis);
        logInfo.setModualInfo(actionLog.modual());
        logInfo.setMethodDesc(actionLog.methodDesc());
        logInfo.setRequestSource(actionLog.source().getDesc());
        logInfo.setLogType(actionLog.logtype().getDesc());
        logInfo.setLogIdentifying(actionLog.logtype().getCode());
        logInfo.setMethodType(request.getMethod());
        logInfo.setRequestMethod(className+methodName);
        logInfo.setRequestResult(JacksonUtil.transformJSONCompact(result));
        logInfo.setRequestBrowser(userAgent.getBrowser().getName());

        logInfo.setCreateTime(new Date());
        //设置创建人信息
        if("manager".equals(actionLog.source().getDesc())){
            // 在后面做完用户模块的时候进行完善
            // 解析用户信息 并获取用户编号和用户名称存放到对应的地方
        }else {
            //1.用户编号存储ip地址
            logInfo.setCreateUserCode(RequestUtil.getHttpServletRequestIpAddress());
            //2.用户名称存储ip地址,可以通过ip库反查询到ip信息 获取详细的省市区  在烛火项目中我们存储ip
            logInfo.setCreateUserName(RequestUtil.getHttpServletRequestIpAddress());
        }
        logInfo.setRequestParams(spliceingParameters(request));
        logInfoMapper.insertSelective(logInfo);
        //返回
        return result;
    }

    /**
     * 获取请求参数信息
     * @param request
     * @return
     */
    private String spliceingParameters(HttpServletRequest request) throws IOException {
        String requestParams = null;
        String contentType = request.getContentType();
        if(ContentType.JSON.getValue().equals(contentType)){
            //通过流的方式去获取参数信息
            StringBuilder sb = new StringBuilder();
            BufferedReader bufferedReader = request.getReader();
            String line;
            while ((line = bufferedReader.readLine()) != null){
                sb.append(line);
            }
            requestParams = sb.toString();

        } else if (ContentType.FORM_URLENCODED.getValue().equals(contentType)) {
            Map<String,Object> requestParmsMap = new HashMap<>();
            Enumeration<String> enumerations = request.getParameterNames();
            while (enumerations.hasMoreElements()){
                String key = (String)enumerations.nextElement();
                String values[] = request.getParameterValues(key);
                requestParmsMap.put(key,values.length == 1 ? request.getParameter(key):values);
            }

            requestParams = JacksonUtil.transformJSONCompact(requestParmsMap);
        }
        return requestParams;
    }
}

2.1.6 解决getInputStream() has already been called for this request

2.1.6.1. CustomerRequestWrapper

public class CustomerRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 存储流信息
     */
    private final byte[] body;

    /**
     * 创建一个新的request请求
     * @param request
     */
    public CustomerRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toByteArray(request.getInputStream());
    }


    /**
     * 重写getReader 主要是重写inputStream信息 在读取的时候读取我们自定义的body的
     * @return
     * @throws IOException
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }
}

2.1.6.2. CustomerHttpServletRequestFilter

@Component
public class CustomerHttpServletRequestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest servletWrapper = null;
        if(servletRequest instanceof HttpServletRequest){
            servletWrapper = new CustomerRequestWrapper((HttpServletRequest) servletRequest);
        }
        if(servletRequest == null){
            filterChain.doFilter(servletRequest,servletResponse);
        }else {
            try {
                filterChain.doFilter(servletWrapper,servletResponse);
            }catch (IOException e){
                e.printStackTrace();
            }catch (ServletException e){
                e.printStackTrace();
            }
        }
    }
}

2.3. 查询日志列表

2.3.1. 静态页面

2.3.1.1. 页面布局

<!DOCTYPE html>
<html lang="en">
<head>
    <div th:replace = "/manager/common/common :: core-head('日志','日志','')"></div>
    <div th:replace = "/manager/common/common :: core-css"></div>
    <div th:replace = "/manager/common/common :: lib-bootstrap-table-css"></div>
</head>
<body class="gray-bg">
     <div class="wrapper wrapper-content">
         <!-- 搜索区域-->
         <div class="panel">
             <div class="panel-body">
                 <form role="search-form" class="form-inline" id="search-form">
                     <div class="form-group">
                         <label class="control-label">请求IP</label>
                         <input type="text" placeholder="请输入请求IP" id="requestIp" name="requestIp" class="form-control">
                     </div>

                     <div class="form-group">
                         <label class="control-label">请求来源</label>
                         <select class="form-control" name="requestSource" id="requestSource">
                             <option value="">全部来源</option>
                             <option value="manager" >管理后台</option>
                             <option value="portal">博客前台</option>
                         </select>
                     </div>

                     <a class="btn btn-primary" id="searchBtn"  notallow="$.bstable.search()">
                         <i class="fa fa-search"></i> 搜索
                     </a>
                     <a class="btn btn-warning" id="resetBtn" notallow="$.bstable.reset()">
                         <i class="fa fa-refresh"></i> 重置
                     </a>
                 </form>
             </div>
         </div>
         <!-- 表格区域-->
         <div class="panel">
             <div class="panel-body">
                 <!-- 内容区域-->
                 <div class="select-table table-striped">
                     <table id="bootstrap-table-list" ></table>
                 </div>
             </div>
         </div>
     </div>
    <div th:replace = "/manager/common/common :: core-js"></div>
    <div th:replace = "/manager/common/common :: lib-bootstrap-table-js"></div>
    <script src="../../../static/local/js/zhuhuo.js" th:src="@{/local/js/zhuhuo.js}" ></script>
</body>
</html>

2.3.1.2. 初始化表格js

<script>
    let options = {
        url:"/m/logInfo/findLogInfoList",
        viewPageUrl: "/m/logInfo/viewLogInfoPage/{id}",
        modualName: "日志",
        columns: [
            {
                checkbox: true
            },
            {
                field: 'id',
                title: 'id'
            },
            {
                field: 'modualInfo',
                title: '模块信息'
            },
            {
                field: 'requestUrl',
                title: '请求地址'
            },
            {
                field: 'requestIp',
                title: '请求IP'
            },
            {
                field: 'requestBrowser',
                title: '浏览器'
            },

            {
                field: 'requestSource',
                title: '请求来源',
                formatter: function (value, item, index) {
                    if (item.requestSource == 'manager') {
                        return '管理后台';
                    } else  {
                        return '博客前台';
                    }
                }
            },
            {
                field: 'createTime',
                title: '创建时间'
            },
            {
                title: '操作',
                align: 'center',
                formatter: function actionFormatter(value, item) {
                        let btnArr = [];
                        btnArr.push('<button type="button" class="btn btn-sm btn-secondary" 
                                    data-toggle="tooltip" title="查看明细" data-width="720" data-height="450"
                                    notallow="$.action.viewPage('+item.id+')">
                                    <i class="fa fa-search"></i></button>');
                        return btnArr.join(" ");

                },
            }
        ],
    }
    $.bstable.init(options);
</script>

2.3.1.3. sidebar修改

<li>
  <a class="zh-menu-item" th:href="@{/m/logInfo/findLogInfoPage}">
    <i class="fa fa-navicon"></i>日志管理
  </a>
<li>  

2.3.2. 列表功能

2.3.2.1. 创建查询列表页面方法

@ActionLog(methodDesc = "查询日志列表页面",source = LogSource.MANAGER,modual = "日志管理" ,logtype = LogType.VIEW)
@GetMapping(value = "/findLogInfoPage")
public String findLogInfoPage(){
    return "/manager/loginfo/list";
}

2.3.2.2.创建请求对象和响应对象

2.3.2.2.1. 请求对象 NavListBO
@Data
public class LogInfoListBO {


    /**
     * 请求ip
     */
    private String requestIp;

    /**
     * 请求来源
     */
    private String reuqestSource;

    /**
     * 每页展示的数量
     */
    private Integer pageSize;


    /**
     * 分页的索引 当前是第几页
     */
    private Integer pageNum;
}
2.3.2.2.2. 响应对象 NavListDTO
@Data
public class LogInfoListDTO {
    /**
     * id
     */
    @Id
    private Long id;
    /**
     * 请求ip
     */
    private String requestIp;
    /**
     * 请求地址
     */
    private String requestUrl;
    /**
     * 日志请求浏览器
     */
    private String requestBrowser;
    /**
     * 请求来源  manager 管理后台 portal 博客前台
     */
    private String requestSource;
    /**
     * 请求方法
     */
    private String requestMethod;
    /**
     * 日志类型(类型里面的value)
     */
    private String logType;
    /**
     * 模块信息/请求模块
     */
    private String modualInfo;

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,shape = JsonFormat.Shape.STRING,timezone = "GTM+8")
    private Date createTime;

}

2.3.2.3. 创建查询列表明细方法

2.3.2.3.1. controller
@ActionLog(methodDesc = "查询日志列表明细",source = LogSource.MANAGER,modual = "日志管理" ,logtype = LogType.VIEW)
@ResponseBody
@GetMapping(value = "/findLogInfoList")
public RespJsonPageData<LogInfoListDTO> findLogInfoList(LogInfoListBO logInfoListBO){
    return logInfoService.findLogInfoList(logInfoListBO);
}
2.3.2.3.2. service
RespJsonPageData<LogInfoListDTO> findLogInfoList(LogInfoListBO logInfoListBO);
2.3.2.3.3. serviceImpl
@Override
public RespJsonPageData<LogInfoListDTO> findLogInfoList(LogInfoListBO logInfoListBO) {
    PageQuery pageQuery = new PageQuery(logInfoListBO);
    Page<Object> result = PageHelper.startPage(pageQuery.getPageNum(),pageQuery.getPageSize());
    List<LogInfo> logInfoList = logInfoMapper.findLogInfoList(MapUtils.objToMap(logInfoListBO));
    List<LogInfoListDTO> logInfoListDTOList = 
    JacksonUtil.transformList(JacksonUtil.transformJSONCompact(logInfoList),LogInfoListDTO.class);
    return RespJsonPageData.success(logInfoListDTOList,result.getTotal());
}
2.3.2.3.4. mapper
List<LogInfo> findLogInfoList(Map<String, Object> params);
2.3.2.3.5. xml
<select id="findLogInfoList" parameterType="java.util.Map" resultMap="BaseResultMap">
    select
    <include refid="base_column_list"/>
    from zh_log_info
    <include refid="search_list_condition"/>
    order by create_time desc ,id desc
</select>


<!-- 搜索参数 -->
<sql id="search_list_condition">
    <where>
        <if test="requestIp != null and requestIp != ''">
            and request_ip =#{requestIp}
        </if>

        <if test="requestSource != null and requestSource != ''">
            and request_source =#{requestSource}
        </if>

    </where>
</sql>

2.3.2.3. 测试查询列表

018-管理后台操作日志功能开发_java_03

2.4. 查询日志明细

2.4.1. 静态页面

2.4.1.1. 页面布局

<!DOCTYPE html>
<html lang="en">
<head>
    <div th:replace = "/manager/common/common :: core-head('查询导航明细','zhuhuo-blog,烛火博客,blog','')"></div>
    <div th:replace = "/manager/common/common :: core-css"></div>
</head>
<body class="gray-bg">

<div class="wrapper wrapper-content">
    <div class="panel">
        <div class="panel-body">
            <form  class="form-horizontal m" id="view-form" style="padding-left: 20px;padding-right: 20px" th:object="${viewLogInfo}">

                <input type="hidden" name="id" th:field="*{id}">

                <div class="form-group">
                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求ip</span>
                            <input type="text" class="form-control" id="requestIp" name="requestIp" th:field="*{requestIp}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求地址</span>
                            <input type="text" class="form-control" id="requestUrl" name="requestUrl" th:field="*{requestUrl}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求来源</span>

                            <select class="form-control m-b" name="requestSource" id="requestSource" th:field="*{requestSource}" disabled>
                                <option value="manager">管理后台</option>
                                <option value="portal">博客前台</option>
                            </select>

                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">模块信息</span>
                            <input type="text" class="form-control" id="modualInfo" name="modualInfo" th:field="*{modualInfo}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">方法描述</span>
                            <input type="text" class="form-control" id="methodDesc" name="methodDesc" th:field="*{methodDesc}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求方式</span>
                            <input type="text" class="form-control" id="methodType" name="methodType" th:field="*{methodType}" disabled>
                        </div>
                    </div>
                </div>


                <div class="form-group">
                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">浏览器</span>
                            <input type="text" class="form-control" id="requestBrowser" name="requestBrowser" th:field="*{requestBrowser}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">日志标识</span>
                            <input type="text" class="form-control" id="logIdentifying" name="logIdentifying" th:field="*{logIdentifying}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">日志类型</span>
                            <input type="text" class="form-control" id="logType" name="logType" th:field="*{logType}" disabled>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">开始时间</span>
                            <input type="text" class="form-control" id="requestStartTime" name="requestStartTime" th:field="*{requestStartTime}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">结束时间</span>
                            <input type="text" class="form-control" id="requestEndTime" name="requestEndTime" th:field="*{requestEndTime}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">响应时长(ms)</span>
                            <input type="text" class="form-control" id="responseInterval" name="responseInterval" th:field="*{responseInterval}" disabled>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-md-12">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求方法</span>
                            <input type="text" class="form-control" id="requestMethod" name="requestMethod" th:field="*{requestMethod}" disabled>
                        </div>
                    </div>
                </div>


                <div class="form-group">
                    <div class="col-md-12">
                        <div class="input-group m-b">
                            <span class="input-group-addon">请求参数</span>
                            <textarea class="form-control" id="requestParams" name="requestParams" th:field="*{requestParams}" disabled></textarea>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-md-12">
                        <div class="input-group m-b">
                            <span class="input-group-addon">响应结果</span>
                            <textarea class="form-control" id="requestResult" name="requestResult" th:field="*{requestResult}" disabled></textarea>
                        </div>
                    </div>
                </div>


                <div class="form-group">
                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">创建时间</span>
                            <input type="text" class="form-control" id="createTime" name="createTime" th:field="*{createTime}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">创建人标识</span>
                            <input type="text" class="form-control" id="createUserCode" name="createUserCode" th:field="*{createUserCode}" disabled>
                        </div>
                    </div>

                    <div class="col-md-4">
                        <div class="input-group m-b">
                            <span class="input-group-addon">创建人名称</span>
                            <input type="text" class="form-control" id="createUserName" name="createUserName" th:field="*{createUserName}" disabled>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>
<div th:replace = "/manager/common/common :: core-js"></div>
</body>
</html>

2.4.2.明细功能

2.4.2.1.创建响应对象

2.4.2.1.1. 响应对象 LogInfoDetailDTO
@Data
public class LogInfoDetailDTO {

    /**
     * id
     */
    private Long id;
    /**
     * 请求ip
     */
    private String requestIp;
    /**
     * 请求地址
     */
    private String requestUrl;
    /**
     * 日志请求浏览器
     */
    private String requestBrowser;
    /**
     * 请求来源  manager 管理后台 portal 博客前台
     */
    private String requestSource;
    /**
     * 请求方法
     */
    private String requestMethod;
    /**
     * 请求参数
     */
    private String requestParams;
    /**
     * 请求响应结果
     */
    private String requestResult;
    /**
     * 请求开始时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,shape = JsonFormat.Shape.STRING,timezone = "GTM+8")
    private Date requestStartTime;
    /**
     * 响应时间/请求结束时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,shape = JsonFormat.Shape.STRING,timezone = "GTM+8")
    private Date requestEndTime;
    /**
     * 响应时长(ms)
     */
    private Long responseInterval;
    /**
     * 日志类型(类型里面的value)
     */
    private String logType;
    /**
     * 日志标识(类型里面的key)
     */
    private String logIdentifying;
    /**
     * 模块信息/请求模块
     */
    private String modualInfo;
    /**
     * 方法描述
     */
    private String methodDesc;
    /**
     * 请求方法类别 GET/POST/PUT/DELETE
     */
    private String methodType;
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,shape = JsonFormat.Shape.STRING,timezone = "GTM+8")
    private Date createTime;
    /**
     * 创建人标识
     */
    private String createUserCode;
    /**
     * 创建人名称
     */
    private String createUserName;
}

2.4.2.2. 创建查询导航明细方法

2.3.2.2.1. controller
@ActionLog(methodDesc = "查询日志详情",source = LogSource.MANAGER,modual = "日志管理" ,logtype = LogType.VIEW)
@GetMapping(value = "/viewLogInfoPage/{id}")
public String viewLogInfoPage(@PathVariable("id") Long id, ModelMap modelMap){
    return logInfoService.viewLogInfoPage(id,modelMap);
}
2.3.2.2.2. service
String viewLogInfoPage(Long id, ModelMap modelMap);
2.3.2.2.3. serviceImpl
@Override
public String viewLogInfoPage(Long id, ModelMap modelMap) {
    LogInfo logInfo = logInfoMapper.selectByPrimaryKey(id);
    LogInfoDetailDTO logInfoDetailDTO = 
    JacksonUtil.transformObject(JacksonUtil.transformJSONCompact(logInfo),LogInfoDetailDTO.class);
    modelMap.put("viewLogInfo",logInfoDetailDTO);
    return "/manager/loginfo/view";
}

2.4.2.3. 测试查询明细

018-管理后台操作日志功能开发_spring_04

2.5. 拓展拦截器方式实现日志插入

package com.zhuhuo.core.interceptor;

import cn.hutool.http.ContentType;
import com.zhuhuo.common.JacksonUtil;
import com.zhuhuo.common.RequestUtil;
import com.zhuhuo.core.logs.annotation.ActionLog;
import com.zhuhuo.modual.entity.LogInfo;
import com.zhuhuo.modual.mapper.LogInfoMapper;
import eu.bitwalker.useragentutils.UserAgent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 玖零
 * @version v0.0.1
 * @project zhuhuo-blog
 * @package com.zhuhuo.core.interceptor
 * @date 2023-08-22 03:13
 * @desc TODO
 */
public class UserLogInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private LogInfoMapper logInfoMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTimeMillis = System.currentTimeMillis();
        request.setAttribute("startTimeMillis",startTimeMillis);
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long endTimeMillis = System.currentTimeMillis();
        request.setAttribute("endTimeMillis",endTimeMillis);
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //做一些事情
        //1.获取日志的注解 拿到模块信息,方法描述,日志类别,日志来源
        ActionLog actionLog = handlerMethod.getBeanType().getAnnotation(ActionLog.class);
        String className = handlerMethod.getClass().getName();
        String methodName = handlerMethod.getMethod().getName();
        //3.计算我们程序整体的请求响应时间
        long startTimeMillis = (long)request.getAttribute("startTimeMillis");
        long endTimeMillis = (long)request.getAttribute("endTimeMillis");
        long diffTimeMillis = endTimeMillis - startTimeMillis;
        //4.获取浏览器相关信息
        final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        //5.拼接对应的参数到日志表里面进行存储
        LogInfo logInfo = new LogInfo();
        logInfo.setRequestIp(RequestUtil.getHttpServletRequestIpAddress());
        logInfo.setRequestUrl(request.getRequestURI());
        logInfo.setRequestStartTime(new Date(startTimeMillis));
        logInfo.setRequestEndTime(new Date(endTimeMillis));
        logInfo.setResponseInterval(diffTimeMillis);
        logInfo.setModualInfo(actionLog.modual());
        logInfo.setMethodDesc(actionLog.methodDesc());
        logInfo.setRequestSource(actionLog.source().getCode());
        logInfo.setLogType(actionLog.logtype().getDesc());
        logInfo.setLogIdentifying(actionLog.logtype().getCode());
        logInfo.setMethodType(request.getMethod());
        logInfo.setRequestMethod(className+methodName);
        logInfo.setRequestResult(JacksonUtil.transformJSONCompact());
        logInfo.setRequestBrowser(userAgent.getBrowser().getName());

        logInfo.setCreateTime(new Date());
        //设置创建人信息
        if("manager".equals(actionLog.source().getDesc())){
            // 在后面做完用户模块的时候进行完善
            // 解析用户信息 并获取用户编号和用户名称存放到对应的地方
        }else {
            //1.用户编号存储ip地址
            logInfo.setCreateUserCode(RequestUtil.getHttpServletRequestIpAddress());
            //2.用户名称存储ip地址,可以通过ip库反查询到ip信息 获取详细的省市区  在烛火项目中我们存储ip
            logInfo.setCreateUserName(RequestUtil.getHttpServletRequestIpAddress());
        }
        logInfo.setRequestParams(spliceingParameters(request));
        logInfoMapper.insertSelective(logInfo);
        //返回
        super.afterCompletion(request, response, handler, ex);
    }


    /**
     * 获取请求参数信息
     * @param request
     * @return
     */
    private String spliceingParameters(HttpServletRequest request) throws IOException {
        String requestParams = null;
        String contentType = request.getContentType();
        if(ContentType.JSON.getValue().equals(contentType)){
            //通过流的方式去获取参数信息
            StringBuilder sb = new StringBuilder();
            BufferedReader bufferedReader = request.getReader();
            String line;
            while ((line = bufferedReader.readLine()) != null){
                sb.append(line);
            }
            requestParams = sb.toString();

        } else if (ContentType.FORM_URLENCODED.getValue().equals(contentType)) {
            Map<String,Object> requestParmsMap = new HashMap<>();
            Enumeration<String> enumerations = request.getParameterNames();
            while (enumerations.hasMoreElements()){
                String key = (String)enumerations.nextElement();
                String values[] = request.getParameterValues(key);
                requestParmsMap.put(key,values.length == 1 ? request.getParameter(key):values);
            }

            requestParams = JacksonUtil.transformJSONCompact(requestParmsMap);
        }
        return requestParams;
    }
}



标签:String,request,private,后台,logInfo,018,import,日志,请求
From: https://blog.51cto.com/cykj20210317/7276002

相关文章

  • 016-管理后台导航功能开发
    1.功能分析1.1.查询列表1.1.1.页面效果1.1.2.功能要求分页查询默认查询10条每页从第1页开始查询默认导航信息只提供查询按钮非默认导航提供查询,修改,删除按钮点击新增按钮弹出新增导航页面搜索条件导航名称:支持模糊搜索点击搜索按钮是按照录入的搜索条件进行查询数据并渲染点......
  • 017-管理后台通用js提取
    //定义全局常量,可供全局使用varzhuhuo={config:{},//bootstrap-table属性配置信息options:{},/***参数初始化*/set:function(id){ //判断配置信息里面是否有值,且当前的事件监听不为空if($.tools.getLength(zhuhu......
  • 015-管理后台框架布局搭建
    1.功能分析管理后台我们先看下大体页面布局如下包含左侧菜单栏,头部导航栏,tab窗体,还有内容显示区域,以及页脚.2.基本实现2.1.文件引入2.2.页面引入引入hplus下的index.html2.3.页面调整我们需要对css,js等做调整,可以使用thymeleaf方式引入<!--css相关调整--><linkrel="sho......
  • 双缓冲异步日志(Async Logging)
    文章目录一、日志系统简介二、功能需求三、性能需求四、高效的异步日志1、异步日志的概念2、双缓冲异步日志解析3、AsyncLogging源码4、代码运行图示五、双缓冲异步日志的相关问题一、日志系统简介日志通常用于故障诊断和追踪(trace),也可用于性能分析。日志通常是分布式系统中事......
  • 【AGC】集成APMS SDK后台无数据问题
    【问题描述】开发者按照文档集成了APMSSDK,但是在AGC后台没有数据,需要帮忙定位。【问题分析】后台没有性能数据的原因有很多,要从端侧和与云侧进行定位分析。1.     首先需要查看端侧的调试日志,调试日志可以直观的看到性能信息的收集与上报动作。打开调试模式方法,在应用的Andro......
  • 【AGC】集成APMS SDK后台无数据问题
    ​【问题描述】开发者按照文档集成了APMSSDK,但是在AGC后台没有数据,需要帮忙定位。 【问题分析】后台没有性能数据的原因有很多,要从端侧和与云侧进行定位分析。1.     首先需要查看端侧的调试日志,调试日志可以直观的看到性能信息的收集与上报动作。打开调试模式方法,在......
  • 百亿数据查询秒级响应,观测体系之日志中心该如何玩转?
    日志是处理生产故障、性能优化、业务分析的重要参考依据,是系统稳定运行不可或缺的一部分。随着业务系统规模急剧膨胀增大,尤其是是微服务架构逐渐普及,一个系统可能涉及多个应用模块与服务实例,传统模式下运维人员去定位问题显得异常困难,效率低下。当服务器资源增加时,各种类型的系统日......
  • 洛谷P5865 [SEERC2018] Tree
    P5865[SEERC2018]Tree题目传送门分析本题不难,只要枚举即可。假设两点之间的距离为树的端点,然后再去枚举其他点,符合的加入集合。若黑色点的个数超出了定义个数,那么就更新一遍。最后求最小值。ACCode:#include<bits/stdc++.h>//保命万能头usingnamespacestd;//命名空......
  • 认识.NET 日志系统
    认识.NET日志系统基本概念日志级别:Trace<Debug<Information<Waring<Error<Critical日志提供者(LoggingProvider):把日志输出控制台、文件、数据库等。.NET的日志非常灵活,对于业务代码只要注入日志对象记录日志即可,具体哪些日志输出到哪里、什么样的格式、是否输出等都有配置......
  • BUUCTF [HCTF 2018]admin
    寻找破解知识点方法1-Flasksession伪造首先,先注册一个再登录,在changepassword那里查看源码,可以看到有提示:原链接已经404,看的这一个https://github.com/Wkh19/hctf_flask这是一个flask模板,这样来看,这是一道模板注入类型的题目。Session机制详解session可以在此处查看......