摘要

最近新接手的项目经常要查问题,但是,前面一拨人,日志打的非常乱,好多就根本没有打日志,所以弄一个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. deconv的弊端

    https://chuansongme.com/n/2630693453218 学习到deconvlution会带来棋盘鬼影,比较重要的解决方法就是resize-deconvlution

  2. uart与usart区别

    uart 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器,是电脑硬件的一部分.它将要传输的资料在串 ...

  3. Spring 最常用的 7 大类注解,哪些你还不知道?

    随着技术的更新迭代,Java5.0开始支持注解.而作为java中的领军框架spring,自从更新了2.5版本之后也开始慢慢舍弃xml配置,更多使用注解来控制spring框架. 而spring的的注解那 ...

  4. 从小白到 6 个 offer,我究竟是怎么刷题的?

    最近自习室里又兴起了一阵刷题潮,大家相约刷题~ 今天和大家系统分享下我去年转行时的一个刷题过程和方法,希望对你有所帮助. 首先介绍下我的编程基础,我学的是金融工程专业,硕士时学过 C++ 的课,这也是 ...

  5. [Luogu P1613]跑路 (DP+倍增+最短路)

    题面 传送门:https://www.luogu.org/problemnew/show/P1613 Solution 挺有意思的一道题. 题面已经挺明显的描述出了这题的主要思想:倍增. 先这样想,我 ...

  6. 力扣 - 445. 两数相加 II

    目录 题目 思路 代码实现 题目 给你两个 非空 链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储一位数字.将这两数相加会返回一个新的链表. 你可以假设除了数字 0 之外,这两 ...

  7. vscode实现远程linux服务器上Python开发

      最近需要训练一个生成对抗网络模型,然后开发接口,不得不在一台有显卡的远程linux服务器上进行,所以,趁着这个机会研究了下怎么使用vscode来进行远程开发.   1 配置免密登录¶   (1)在 ...

  8. Thinkphp3.2 cms之文章模块

    二.文章模块 <?php namespace Admin\Controller; use Think\Controller; class NewController extends Common ...

  9. ubuntu下安装ESP8266开发环境步骤中可能出现的问题及解决办法

    安装步骤参考如下链接 https://www.jianshu.com/p/e9ce2a60df83 1.在GitHub上拉取代码时发生错误:RPC failed; curl 18 transfer c ...

  10. [MIT6.006] 10. Open Addressing, Cryptographic Hashing 开放定址,加密哈希

    前几节课讲散列表的时候,我们需要用Chaining,链接法需要用到指针pointer,但有一种方法可以不要Chaining和指针,还能在发生冲突时,为产生冲突的关键字寻找下一个"空" ...