摘要

最近新接手的项目经常要查问题,但是,前面一拨人,日志打的非常乱,好多就根本没有打日志,所以弄一个AOP统一打印一下 请求数据和响应数据

框架

spring+springmvc+jersey

正文

这个项目有点老啦,竟然还有前端页面用jsp写的,哎,说起来都是泪。下面说说我做这个项目踩得坑吧

统一打印请求的请求数据和响应数据(接口是restful 风格,用jersey框架实现的),肯定第一反应想到老罗的Spring aop,一开始木有仔细研究框架,以为会一招spring就可以吃遍天下啦。o(╥﹏╥)o,所以花了半个小时,写了一个spring aop,调试了1天,就是没看见spring 给目标类生成代理。其中我怀疑过,打的切点不对,spring配置文件不对,等等,反正就木有考虑过spring 本身的问题。后来我才发现要做代理的目标package 根本就木有交给spring 托管,他是由jersey直接托管(以前也木有玩过jersey框架,看了web.xml配置以后才茅塞顿开)

下面的代码是spring+jersey框架下日志aop (注:没法用spring aop ,因为restful风格的package 根本就木有交给spring托管)

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import java.io.*;
import java.net.URI;
import java.util.List;

/**
* Created by huxuhong on 2019/11/27.
*/

@Provider
public class OperationLogFilter implements ContainerRequestFilter,ContainerResponseFilter{
Logger logger = LoggerFactory.getLogger(OperationLogFilter.class);
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if(containerRequestContext!=null){
URI uri = null;
String path = null;
UriInfo uriInfo = containerRequestContext.getUriInfo();
if(uriInfo != null){
uri = uriInfo.getAbsolutePath();
}
if(uri != null){
path = uri.getPath();
}
String method = containerRequestContext.getMethod();
String params = inputStreamToString(containerRequestContext);
printReqInfo(path,method,params);
}else{
logger.info("请求request不存在");
}

}

@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
if(containerResponseContext ==null){
logger.info("响应response不存在");
}else{
String response = outputStreamToString(containerResponseContext);
printResInfo(response);
}

}

private String inputStreamToString(ContainerRequestContext containerRequestContext ) {
StringBuffer stringBuffer = new StringBuffer();
ByteArrayOutputStream baos = null;
InputStream repeatStreamRead = null;
InputStream repeatStreamWrite = null;
try{
InputStream in = containerRequestContext.getEntityStream();
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > -1 ) {
baos.write(buffer, 0, len);
}
baos.flush();
repeatStreamRead = new ByteArrayInputStream(baos.toByteArray());
List<String> paramsList = IOUtils.readLines(repeatStreamRead,"UTF-8");
if(paramsList!=null && !paramsList.isEmpty()){
for(String str : paramsList){
stringBuffer.append(str);
}
}

repeatStreamWrite = new ByteArrayInputStream(baos.toByteArray());
containerRequestContext.setEntityStream(repeatStreamWrite);
}catch (Throwable e){
logger.warn("解析输入流失败{}",e);
}finally {
try {
if(baos != null){
baos.close();
}
if(repeatStreamRead != null){
repeatStreamRead.close();
}
if(repeatStreamWrite != null){
repeatStreamWrite.close();
}
} catch (Throwable e) {
logger.warn("关闭流失败{}",e);
}
}
return stringBuffer.toString();

}

private String outputStreamToString(ContainerResponseContext containerResponseContext ) throws IOException {
String responseStr = null;
try{
Object obj = containerResponseContext.getEntity();
if(obj != null){
responseStr = JSON.toJSON(obj).toString();
}
}catch (Throwable e){
logger.warn("解析响应数据异常{}",e);
}
return responseStr;
}

public void printReqInfo(String url,String method,String params){
logger.info("请求地址:{},请求方式:{},请求参数:{}",url,method,params);
}

public void printResInfo(String response){
logger.info("响应数据:{}",response);
}
}

下面是采用spring 的aop日志 实现方式

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.container.ContainerRequestContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

/**
* Created by huxuhong on 2019/11/26.
*/

@Component
@Aspect
public class OperationLogAspect {
Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);

ThreadLocal<Long> startTime = new ThreadLocal<Long>();

/**
* 定义拦截规则:拦截com.ppdai.wechat.spring.controller包下面的所有类
* execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
*/
@Pointcut(value = "execution(* com.ppdai.wechat.spring.controller..*(..))")
public void serviceMethodPointcut() {

}

@Before(value = "serviceMethodPointcut()")
public void doBefore(JoinPoint joinPoint){
String url = null;
String method = null;
String param = null;
String reqConcreteClass = null;
try {
startTime.set(System.currentTimeMillis());
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes == null){
return;
}
HttpServletRequest request = attributes.getRequest();

url = request.getRequestURL().toString();
method = request.getMethod();
if(method.toUpperCase().equals("GET")){
param = request.getQueryString();
}else{
for(Object obj :joinPoint.getArgs()){
if(obj instanceof MultipartFile
|| obj instanceof HttpServletRequest
|| obj instanceof HttpServletResponse){
continue;
}
param = JSON.toJSON(obj).toString();
}
}
reqConcreteClass = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
}catch (Throwable e){
logger.warn("解析请求信息异常",e);
}
printReqInfo(url,method,reqConcreteClass,param);

}

@AfterReturning(returning = "ret", pointcut = "serviceMethodPointcut()")
public void doAfterReturning(Object ret) throws Throwable {
String responseStr = null;
try {
if(ret != null){
responseStr = JSON.toJSON(ret).toString();
}
Long times = System.currentTimeMillis() - startTime.get();
printResponseInfo(responseStr,times);
}catch (Throwable e){
logger.warn("解析响应内容异常",e);
}

}

private void printResponseInfo(String response,long times){
logger.info("响应内容: {},响应耗时:{} " , response,times );
}

private void printReqInfo(String url,String method,String concreteClass,String param){
logger.info("请求URL: {},类路径:{}, 请求方式: {}, 请求参数: {}", url,concreteClass, method, param);
}

}

望后来的人不要跟我犯同样的错误,都是先入为主的观念害人
参考资料
ssm (spring springmvc mybatis) maven 项目集成 Jersey2 入门指南
https://blog.csdn.net/gianttj/article/details/86144582
https://www.jianshu.com/p/9e135faa3efaJersey实现对方法进行过滤拦截
https://blog.csdn.net/qq_28334711/article/details/72925495
 

关于Jersey框架下的Aop日志 和Spring 框架下的Aop日志的更多相关文章

  1. Java框架之spring框架的优点,为什么要学习spring框架

    为什么要学习Spring的框架a: 方便解耦,简化开发    Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理 b:AOP编程的支持      Spring提供面向切 ...

  2. 「框架」菜鸟简单模仿一下spring的ioc和aop思想,欢迎大家进来阅读指教

    *博客搬家:初版发布于 2015/12/04 16:41    原博客地址:https://my.oschina.net/sunqinwen/blog/539397 spring最核心的部分莫过于io ...

  3. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  4. Spring框架之IoC和AOP

    Spring框架简介: 2003年2月,Spring框架正式成为一个开源项目,并发布于SourceForge中.致力于Java EE应用的各种解决方案,而并不是仅仅专注于某一层的方案,是企业应用开发的 ...

  5. Spring 系列: Spring 框架简介 -7个部分

    Spring 系列: Spring 框架简介 Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级 ...

  6. Spring 系列: Spring 框架简介(转载)

    Spring 系列: Spring 框架简介 http://www.ibm.com/developerworks/cn/java/wa-spring1/ Spring AOP 和 IOC 容器入门 在 ...

  7. Spring框架入门

    技术分析之什么是Spring框架        1. Spring框架的概述        * Spring是一个开源框架        * Spring是于2003 年兴起的一个轻量级的Java开发 ...

  8. spring框架篇(一)-------spring简介与配置文件使用控制反转事例

    spring简介 Spring 是一个开源框架,中文意思就是春天,也许是作者想让自己的这个框架给Java开发人员带来春天吧.其官方网站是 https://spring.io/ ,可以在官方网站下载到完 ...

  9. Spring框架的第四天(整合ssh框架)

    ## Spring框架的第四天 ## ---------- **课程回顾:Spring框架第三天** 1. AOP注解方式 * 编写切面类(包含通知和切入点) * 开启自动代理 2. JDBC模板技术 ...

随机推荐

  1. 论文解读《Understanding the Effective Receptive Field in Deep Convolutional Neural Networks》

    感知野的概念尤为重要,对于理解和诊断CNN网络是否工作,其中一个神经元的感知野之外的图像并不会对神经元的值产生影响,所以去确保这个神经元覆盖的所有相关的图像区域是十分重要的:需要对输出图像的单个像素进 ...

  2. 如果在Yii中,使用AR查询,不直接写sql,则在使用的时候会报错

    如果在Yii中,使用AR查询,不直接写sql,则在使用的时候会报错 Student::find() ->select("id,name,from_unixtime(create_tim ...

  3. fidder 学习

    前提 你要清楚下面两个问题的答案 1.接口是什么? 2.抓包是什么? 在提一嘴 想要获取手机上的时时请求 首先要把手机和电脑连接同一个网络 也就是在同一频道上 开始 1.安装 Fidder Every ...

  4. 剑指41和为s的连续整数序列

    题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...

  5. C# out参数的学习

    out参数一直不是很会用,学习下记录下来 . 先来看一下out参数的使用场景 如果你在一个方法中 ,返回多个相同类型的值的时候,可以考虑返回一个数组.(举例:写一个方法,传入一个数组,返回数组的最大值 ...

  6. BIM与GIS融合的意义——从智慧工地到智慧城市

    随着智慧城市概念的发展,BIM与GIS融合的概念深入人心,通过整合BIM的参数化描述建筑组件性质的特性与GIS宏观的几何空间概念,将 BIM 描述单体建筑物的特性通过 GIS 拓展至三维城市. BIM ...

  7. JavaScript探秘:可执行的上下文堆栈

    这一系列的文章,挺不错的,值得收藏细读 JavaScript探秘:可执行的上下文堆栈

  8. ubutun 服务器中文设置

    在连接到服务器时,我们看不到图形界面,按照常规的安装搜狗输入法行不通,查阅相关的资料找到一篇可以解决的 sudo apt-get update 如果出现 E: 无法下载 404 Not Found I ...

  9. AQS详解,并发编程的半壁江山

    千呼万唤始出来,终于写到AQS这个一章了,其实为了写这一章,前面也是做了很多的铺垫,比如之前的 深度理解volatile关键字 线程之间的协作(等待通知模式) JUC 常用4大并发工具类 CAS 原子 ...

  10. 接口自动化测试:apiAutoTest使用re 处理数据依赖

    目录 废话 2020/11/19 参数依赖 更新后的效果 新版依赖数据如何使用 源码地址 道谢 废话 目前在工作中写脚本的时候发现了一些之前开源的apiAutoTest的可优化项,后面应该也是会慢慢的 ...