java 项目日记实现两种方式:拦截器方式实现日记与定义日记注解方式实现日记

        通常只要是java web项目基本都离不开项目日记,项目日记存在的意义很多,例如:安全审计,问题追踪都离不开项目日记。下面我们说一下项目日记实现最常用的两种方式 。

一 拉截器实现项目日记

   1 实现一个拦截器基类,用于事件项目的请求日记

     用拦截器实现记录日记代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.LocalDateTime;/*** @author hua* @Description 自定义拦截器,用于记录处理Web请求的基本功能。*/
public class BaseInterceptor extends HandlerInterceptorAdapter {// ThreadLocal 用于在每个线程中存储API消息日志ThreadLocal<ApiMessageLog> apiMessageLogThreadLocal = new ThreadLocal<>();// 用于记录日志的Loggerpublic static final Logger logger = LoggerFactory.getLogger(BaseInterceptor.class);/*** 在请求完成后调用的方法,用于执行任何必要的清理或日志记录。** @param request   HTTP请求对象。* @param response  HTTP响应对象。* @param handler   处理请求的处理程序(控制器方法)。* @param ex        在请求处理过程中发生的任何异常。* @throws Exception 如果在完成后处理过程中发生异常。*/public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 获取当前线程的API消息日志ApiMessageLog apiMessageLog = apiMessageLogThreadLocal.get();// 检查API消息日志是否非空if (apiMessageLog != null) {// 计算请求所用时间long time = Duration.between(apiMessageLog.getCtime(), LocalDateTime.now()).getSeconds();apiMessageLog.setTimeLong((int) time);// 如果请求时间超过8秒,则记录错误日志if (time > 8) {logger.error("超时 > " + time + " > " + apiMessageLog.getApiName() + "\n" + apiMessageLog);}// 如果在请求处理过程中发生异常,将其设置为API消息日志的结果if (ex != null) {apiMessageLog.setResult(ex.getMessage());} else {// 如果没有异常,从请求属性中设置结果String result = (String) request.getAttribute("_REQ_RESULT");if (result != null) {apiMessageLog.setResult(result);}// 从请求属性中设置API订单号String _REQ_ORDER_SN = (String) request.getAttribute("_REQ_ORDER_SN");if (_REQ_ORDER_SN != null) {apiMessageLog.setApiOrderNo(_REQ_ORDER_SN);}// 从请求属性中设置连接器IDString _REQ_CONNECTOR_ID = (String) request.getAttribute("_REQ_CONNECTOR_ID");if (_REQ_ORDER_SN != null) {apiMessageLog.setConnectorId(_REQ_CONNECTOR_ID);}}// 将API消息日志添加到内存队列,队列批量保存效率更高MemDataTable.API_MESSAGE_LOGS.offer(apiMessageLog);}}
}

继承上面写日记基类拦截器(如果项目简单且不用选择性记录,可以把以下代码合并成一个拦截器即可)。

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;/*** @author hua* @Description 统一处理中电联的拦截器*/
@Component
public class ZdlInterceptor extends BaseInterceptor {// 注入StringRedisTemplate实例@AutowiredStringRedisTemplate stringRedisTemplate;// 注入ApiMessageLogServiceImpl实例@AutowiredApiMessageLogServiceImpl apiMessageLogService;/*** 在请求处理前执行的方法** @param request  HTTP请求对象* @param response HTTP响应对象* @param handler  处理请求的处理程序* @return 如果继续处理请求,则返回true;否则,返回false* @throws Exception 如果在处理过程中发生异常*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从请求中获取JSON字符串String reqJsonStr = IOUtils.toString(request.getInputStream(), "UTF-8");// 创建API消息日志实体并设置基本信息ApiMessageLog apiMessageLog = new ApiMessageLog();apiMessageLog.setApiName(request.getRequestURI());apiMessageLog.setMsg(reqJsonStr);apiMessageLog.setType("1");apiMessageLog.setCtime(LocalDateTime.now());// 尝试解析JSON字符串JSONObject json = null;try {json = JSONObject.parseObject(reqJsonStr);} catch (JSONException e) {// 如果JSON格式错误,返回错误信息并记录日志String r = "请求JSON格式错误!!!";response.getOutputStream().write(r.getBytes("utf-8"));apiMessageLog.setResult(r);MemDataTable.API_MESSAGE_LOGS.offer(apiMessageLog);return false;}// 从JSON中获取OperatorID并设置到API消息日志实体String operatorID = json.getString("OperatorID");apiMessageLog.setOperatorId(operatorID);// 将API消息日志的ID设置到请求属性中request.setAttribute("ApiMessageLogId", apiMessageLog.getId());// 将API消息日志存储到ThreadLocal中,以便后续处理apiMessageLogThreadLocal.set(apiMessageLog);return true;}
}

2 拦截器配置

这里可以选择性只记录哪些请求开头的日记。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author hua* @Description Web配置类,实现WebMvcConfigurer接口用于配置Web相关的功能。*/
@Configuration
public class WebConfiguration implements WebMvcConfigurer {/*** 配置跨域访问规则** @return CorsConfiguration对象*/private CorsConfiguration corsConfig() {CorsConfiguration corsConfiguration = new CorsConfiguration();// 允许所有来源corsConfiguration.addAllowedOrigin("*");corsConfiguration.addAllowedHeader("*");corsConfiguration.addAllowedMethod("*");return corsConfiguration;}/*** 配置CorsFilter,用于处理跨域请求** @return CorsFilter对象*/@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfig());return new CorsFilter(source);}/*** 配置中电联拦截器的Bean** @return ZdlInterceptor对象*/@Beanpublic HandlerInterceptor getZdlInterceptor() {return new ZdlInterceptor();}/*** 配置拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(getZdlInterceptor())// 设置不拦截的路径.excludePathPatterns("/public/**", "")// 设置拦截的路径.addPathPatterns("/api/zdl/**");}
}

3 实现效果

最终保存在数据库效果,可以根据业务情况记录,如记录访问ip,时间等。

 

 

二 注解方式实现项目日记

1 引入需用到jar包

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

  2 实现日记注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 日记*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnno {/*** 操作内容*/String desc() default "";/*** 关键参数* @return*/String key() default "";}
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.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;@Aspect
@Component
public class LogAspect {@Autowiredprivate ISysLogService sysLogService;/*** 定义切点*/@Pointcut("@annotation(cn.enjoyiot.backend.config.aop.LogAnno)")public void initLogAnno() {}@Around(value = "initLogAnno()")public Object saveSysLog(ProceedingJoinPoint joinPoint) {SysLog sysLog = new SysLog();MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取切入点所在的方法Method method = signature.getMethod();LogAnno operation = method.getAnnotation(LogAnno.class);if (operation != null) {String value = operation.desc();sysLog.setOperation(value);//保存获取的操作}//获取请求的类名String className = joinPoint.getTarget().getClass().getName();//获取请求的方法名String methodName = method.getName();sysLog.setMethod(className + "." + methodName);//请求的参数Object[] args = joinPoint.getArgs();//将参数所在的数组转换成jsonString params = "";try {if (args.length > 0) {for (Object p : args) {if(checkLogParam(p)){params = params + JSONObject.toJSONString(p) + " ";}}}} catch (Exception e) {e.printStackTrace();}sysLog.setParams(params);HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();sysLog.setIp(IpUtil.getRealIP(request));sysLog.setUri(request.getRequestURI());SysUser sysUser = SessionUtil.getSysUser();if (sysUser != null) {sysLog.setUserId(sysUser.getId());sysLog.setUsername(sysUser.getUsername());sysLog.setAccount(sysUser.getAccount());}LocalDateTime begin = LocalDateTime.now();/*执行目标方法*/try {Object result = joinPoint.proceed();return result;} catch (Throwable e) {e.printStackTrace();sysLog.setExceptionMessage(e.getMessage());return RespUtil.respErr(e.getMessage());} finally {LocalDateTime end = LocalDateTime.now();long sec = Duration.between(begin, end).getSeconds();sysLog.setCreateTime(end);sysLog.setTime(sec);sysLogService.save(sysLog);}}private boolean checkLogParam(Object p) {if(p==null){return false;}if(StringUtils.isEmpty(p)){return false;}if(p instanceof HttpServletRequest){return false;}else if(p instanceof HttpServletResponse){return false;}else if(p instanceof InputStreamSource){return false;}return true;}}

 3 注解使用

    @PostMapping(value = "/couponList")@LogAnno(desc="账户列表")@PreAuthorize("hasPermission(filterObject,'accountList')")public String accountList(@RequestBody PageParam param) {return "test";}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/292461.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTF之misc杂项解题技巧总结(2)——流量分析

通常比赛中会提供一个包含流量数据的 PCAP 文件&#xff0c;有时候也会需要选手们先进行修复或重构传输文件后&#xff0c;再进行分析。 PCAP 这一块作为重点考察方向&#xff0c;复杂的地方在于数据包里充满着大量无关的流量信息&#xff0c;因此如何分类和过滤数据是参赛者需…

专业数据中台哪个好?亿发数字化运营平台解决方案,助力大中型企业腾飞

数据中台的核心在于避免数据的重复计算&#xff0c;通过数据服务化的方式提升数据的共享能力&#xff0c;为数据应用赋能。 在信息技术时代&#xff0c;企业的信息系统建设通常是由各个组织和功能单元独立完成&#xff0c;以满足各自的需求。这导致了“数据孤岛”和“数据烟囱”…

如何设计更优雅的 React 组件?

在日常开发中&#xff0c;团队中每个人组织代码的方式不尽相同。下面我们就从代码结构的角度来看看如何组织一个更加优雅的 React 组件&#xff01; 1. 导入依赖项 我们通常会在组件文件顶部导入组件所需的依赖项。对于不同类别的依赖项&#xff0c;建议对它们进行分组&#…

歌曲春节回家:李白的诗意与荆涛的歌声

歌曲春节回家&#xff1a;李白的诗意与荆涛的歌声 “春节回家&#xff0c;春节回家&#xff0c;又是一个春节到&#xff0c;漫天雪花飘。”随着歌手荆涛深情的嗓音&#xff0c;我们仿佛置身于那漫天飞雪的冬日&#xff0c;期待着与家人团聚的温馨时刻。这首歌曲不仅是对春节回…

场奇妙的视听盛宴

近年来&#xff0c;随着科技的发展&#xff0c;手机的功能越来越强大。手机无人直播作为一种新兴的直播方式&#xff0c;正逐渐引起了人们的关注和热爱。手机无人直播&#xff0c;顾名思义&#xff0c;就是利用手机进行直播&#xff0c;不需要主持人操控&#xff0c;完全依靠智…

哈希表..

文章目录 1. 两数之和-力扣 1 题 1. 两数之和-力扣 1 题 思路&#xff1a; 循环遍历数组&#xff0c;拿到每个数字x以target-x作为key到map中查找 若没找到&#xff0c;将x 作为key&#xff0c;它的索引作为value 存入map 若找到了&#xff0c;返回 x 和它配对数的索引即可 …

Java智慧工地源码 SAAS智慧工地源码 智慧工地管理可视化平台源码 带移动APP

一、系统主要功能介绍 系统功能介绍&#xff1a; 【项目人员管理】 1. 项目管理&#xff1a;项目名称、施工单位名称、项目地址、项目地址、总造价、总面积、施工准可证、开工日期、计划竣工日期、项目状态等。 2. 人员信息管理&#xff1a;支持身份证及人脸信息采集&#…

Ubuntu 20.4镜像国内地址下载较快

Ubuntu20.04版本比较稳定&#xff0c;部署OJ大都用这个版本。 推荐阿里云镜像点&#xff0c;点进去根据你的电脑版本下载iso后缀那个 ubuntu-releases-20.04安装包下载_开源镜像站-阿里云 下载速度较快 其他版本 http://mirrors.aliyun.com/ubuntu-releases/ 如果使用云服务…

JSON Wizard for Mac - 解析你的 JSON 数据

JSON Wizard for Mac 是一款强大的工具&#xff0c;旨在帮助你处理和解析 JSON 数据。无论你是开发人员、数据分析师还是对 JSON 数据感兴趣的用户&#xff0c;这个工具都能方便地处理和编辑 JSON 文件。 ✨主要功能包括&#xff1a; 1️⃣ JSON 格式验证&#xff1a;JSON Wi…

毕业“寄” 划算,享闪侠惠递巨额优惠福利,助你轻装向未来!

毕业季&#xff0c;你是否也为“行李难题”而烦恼&#xff01;别着急额&#xff0c;闪侠惠递来帮你&#xff0c;让您轻松寄快递&#xff0c;省时省力省心回家&#xff01; 毕业季即将来临&#xff0c;相信大家已经开始为苦读一年的自己计划一个美好的寒假暑期。而在期待假期的…

ansible的脚本—playbook剧本

目录 目录 一、playbook 1、简介 2、playbook组成部分&#xff1a; 3、如何编写Playbook&#xff1f; 4、语句的横向/纵向写法 二、playbook实例&#xff1a; 1、playbook模版&#xff1a; 2、playbook的条件判断&#xff1a; 3、playbook中的循环&#xff1a; 4、循…

Flink电商实时数仓(三)

DIM层代码流程图 维度层的重点和难点在于实时电商数仓需要的维度信息一般是动态的变化的&#xff0c;并且由于实时数仓一般需要一直运行&#xff0c;无法使用常规的配置文件重启加载方式来修改需要读取的ODS层数据&#xff0c;因此需要通过Flink-cdc实时监控MySql中的维度数据…