当前位置:网站首页 > Java基础 > 正文

java使用AOP切面获取请求日志并记录

前言:为了完整的记录外部接口请求记录,记录内容包括:请求参数,响应参数,请求耗时,请求IP地址,请求成功还是失败,请求方法名称,请求接口地址等重要信息。

1.引入maven

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

备注:此包是为了在aop中获取request对象而引入 

2.日志记录model如下

package com.tjair.log.model; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; import java.util.Date; @Table(name = "t_tjapi_log") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class TjapiLog { @Id private Integer id; / * 采购编码 */ @Column(name = "partnerId") private String partnerid; / * 请求地址 */ private String url; / * 接口描述 */ @Column(name = "api_name") private String apiName; / * 请求时间 */ @Column(name = "create_time") private Date createTime; / * 请求方式 */ private String method; / * 请求参数 */ private String params; / * 请求状态:1 成功 0 失败 */ private Integer status; / * 请求IP */ private String ip; / * 请求来源地址 */ private String location; / * 请求耗时 */ private Integer time; / * 返回内容 */ private String response; }

3.AOP切面如下

package com.tjair.tjapi.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tjair.common.log.TjapiLogVo; import com.tjair.common.util.IPUtil; import com.tjair.tjapi.feign.LogFeignService; import com.tjair.tjapi.util.HttpHelper; import io.swagger.annotations.ApiOperation; 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.context.annotation.Configuration; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Date; import java.util.Objects; / * @description 拦截所有控制器的返回,记录响应报文 * @author unhejing * @date 2021-03-17 上午11:52:26 */ @Aspect @Configuration @Slf4j public class LogAspect { @Autowired private LogFeignService logFeignService; public LogAspect() { } @Pointcut("execution(public * com.tjair.tjapi.controller.*.*(..))") public void pointCutMethod() { } // 声明环绕通知 @Around("pointCutMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { Long startTime = System.currentTimeMillis(); ApiOperation apiOperation = ((MethodSignature)pjp.getSignature()).getMethod().getAnnotation(ApiOperation.class); // 获取request对象 RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); Object ret = pjp.proceed(); Long endTime = System.currentTimeMillis(); TjapiLogVo tjapiLogVo = getTjapiLogVo(request,Objects.nonNull(apiOperation) ? apiOperation.value() : "",endTime-startTime,JSON.toJSONString(ret)); // 添加日志 addLog(tjapiLogVo); log.info("响应数据耗时{}:响应数据{}",endTime-startTime,JSON.toJSONString(ret)); return ret; } public void addLog(TjapiLogVo tjapiLogVo) { log.info("添加日志:{}",JSON.toJSONString(tjapiLogVo)); Long startTime = System.currentTimeMillis(); logFeignService.addTjapiLog(tjapiLogVo); Long endtime = System.currentTimeMillis(); log.info("添加日志耗时:{}",endtime-startTime); } private TjapiLogVo getTjapiLogVo(HttpServletRequest request,String apiName,Long time,String response) throws IOException { TjapiLogVo tjapiLogVo = new TjapiLogVo(); String jsonBody = HttpHelper.getBodyString(request); log.info("请求参数:{}",jsonBody); JSONObject reqObj = Objects.nonNull(JSON.parseObject(jsonBody)) ? JSON.parseObject(jsonBody) : new JSONObject(); JSONObject resObj = Objects.nonNull(JSON.parseObject(response)) ? JSON.parseObject(response) : new JSONObject(); //设置请求参数 tjapiLogVo.setParams(jsonBody); tjapiLogVo.setPartnerid(Objects.nonNull(reqObj.getString("partnerId")) ? reqObj.getString("partnerId") : "-1"); // 设置IP地址 tjapiLogVo.setIp(IPUtil.getIpAddr(request)); // 设置位置 tjapiLogVo.setLocation(IPUtil.getCityInfo(tjapiLogVo.getIp())); //设置请求方法,GET,POST... tjapiLogVo.setMethod(request.getMethod()); //设置请求路径 tjapiLogVo.setUrl(request.getRequestURI()); // 设置请求方法名称 tjapiLogVo.setApiName(apiName); // 设置创建时间 tjapiLogVo.setCreateTime(new Date()); // 设置请求状态 Integer code = Objects.nonNull(resObj.getInteger("code")) ? resObj.getInteger("code") : 1; tjapiLogVo.setStatus(code); // 设置接口消耗时间 tjapiLogVo.setTime(time.intValue()); // 设置响应内容 tjapiLogVo.setResponse(response); return tjapiLogVo; } }

4.测试接口

package com.tjair.tjapi.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tjair.common.service.RedisUtils; import com.tjair.common.util.Result; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; / * @author jingyujie * @create 2021-03-16 下午2:39 / @RestController @RequestMapping("/test") @Api(tags = "测试API控制器") public class TestController { @Autowired private RedisUtils redisUtils; @PostMapping("/apiTest") @ApiOperation("接口测试") public Result apiTest(@RequestBody JSONObject obj){ return Result.returnSuccess("接口已联通,请求入参:"+ JSON.toJSONString(obj)); } } 

 

5.请求结果如下

备注说明:

1.LogFeignService是添加日志的service

2.IPUtil是获取IP和地址信息相关的工具类

3.HttpHelper是解析request中的请求参数的工具类

4.ApiOperation是获取接口的注解上面的接口描述

5.partnerId是我请求参数里面必传的用户编码

6.code是响应参数里面的状态码,用于判断接口是否请求成功

末尾附上工具类:

IPUtil.java

类中的/ip2region.db这个是一个地址库,可直接百度下载即可。

package com.tjair.common.util; import org.apache.commons.io.FileUtils; import org.lionsoul.ip2region.DataBlock; import org.lionsoul.ip2region.DbConfig; import org.lionsoul.ip2region.DbSearcher; import org.lionsoul.ip2region.Util; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.lang.reflect.Method; / * ip操作相关工具类 * @author jingyujie * @create 2020-06-24 下午2:11 / public class IPUtil { public static String getCityInfo(String ip){ try { //db String dbPath = IPUtil.class.getResource("/ip2region.db").getPath(); System.out.println("路径:"+dbPath); File file = new File(dbPath); if (file.exists() == false) { System.out.println("Error: Invalid ip2region.db file"); String tmpDir = System.getProperties().getProperty("java.io.tmpdir"); dbPath = tmpDir + "ip.db"; System.out.println(dbPath); file = new File(dbPath); FileUtils.copyInputStreamToFile(IPUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region.db"), file); } //查询算法 int algorithm = DbSearcher.BTREE_ALGORITHM; //B-tree //DbSearcher.BINARY_ALGORITHM //Binary //DbSearcher.MEMORY_ALGORITYM //Memory DbSearcher searcher = null; try { DbConfig config = new DbConfig(); searcher = new DbSearcher(config, dbPath); //define the method Method method = null; switch (algorithm) { case DbSearcher.BTREE_ALGORITHM: method = searcher.getClass().getMethod("btreeSearch", String.class); break; case DbSearcher.BINARY_ALGORITHM: method = searcher.getClass().getMethod("binarySearch", String.class); break; case DbSearcher.MEMORY_ALGORITYM: method = searcher.getClass().getMethod("memorySearch", String.class); break; } DataBlock dataBlock = null; if (Util.isIpAddress(ip) == false) { System.out.println("Error: Invalid ip address"); } dataBlock = (DataBlock) method.invoke(searcher, ip); return dataBlock.getRegion(); } catch (Exception e) { e.printStackTrace(); } finally { if (searcher != null) { searcher.close(); } } return null; } catch (Exception e) { e.printStackTrace(); } return null; } public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); System.out.println("x-forwarded-for ip: " + ip); if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个ip值,第一个ip才是真实ip if( ip.indexOf(",")!=-1 ){ ip = ip.split(",")[0]; } } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); System.out.println("Proxy-Client-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); System.out.println("WL-Proxy-Client-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); System.out.println("HTTP_CLIENT_IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); System.out.println("X-Real-IP ip: " + ip); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); System.out.println("getRemoteAddr ip: " + ip); } System.out.println("获取客户端ip: " + ip); return ip; } } 

 HttpHelper.java

此方法要获取request body参数还需要HttpServletRequestWrapper 实现对request body的二次读取,具体实现请自行百度。

package com.tjair.tjapi.util; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; public class HttpHelper { public static String getBodyString(HttpServletRequest request) throws IOException { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } } 

 

 

到此这篇java使用AOP切面获取请求日志并记录的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • windows下启动了多个java,杀死指定进程2024-12-01 16:45:10
  • 葵司JAVA25_[PAT] 1012 The Best Rank (25 分)Java2024-12-01 16:45:10
  • OSHI 是一个免费的基于 JNA(本机)的 Java 操作系统和硬件信息库2024-12-01 16:45:10
  • Java打印输出:在线天堂2024-12-01 16:45:10
  • java最新版下载地址2024-12-01 16:45:10
  • 线上一次排错:JAVA程序占用CPU超过100%2024-12-01 16:45:10
  • idea创建java的maven聚合工程2024-12-01 16:45:10
  • macbook安装低版本的jdk,提示“Oracle 的 Java 要求 Mac OS X 10.7.3 或更高版本”2024-12-01 16:45:10
  • java spring调用db.properties外部文件时出错2024-12-01 16:45:10
  • java泛型2024-12-01 16:45:10
  • 全屏图片