摘要

最近新接手的项目经常要查问题,但是,前面一拨人,日志打的非常乱,好多就根本没有打日志,所以弄一个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. C2. Power Transmission (Hard Edition) 解析(思維、幾何)

    Codeforce 1163 C2. Power Transmission (Hard Edition) 解析(思維.幾何) 今天我們來看看CF1163C2 題目連結 題目 給一堆點,每兩個點會造成一 ...

  2. c语言之指针特性

    0x01.指针的声明 char x; char* x; short y; short* y; int z; int* z; float f; float* f; double d; double* d ...

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

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

  4. 谈谈nginx和lvs各自的优缺点以及使用

            在最开始呢,咱们先说一下什么叫负载均衡,负载均衡呢,就是将一批请求,根据请求的内容,分发到不同的后端去进行相应的处理,从而提供负载分担,主备切换等功能.                 ...

  5. sqlsugar入门(3)-DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff")源码修改

    1.注释SqlSugar\ExpressionsToSql\ResolveItems\MethodCallExpressionResolve文件下的GetMethodValue方法 case &quo ...

  6. 湖湘杯2020_ReMe

    查壳后发现是由Python2.7环境下编译得到的exe可执行文件 由此想到可将exe转为pyc文件再反编译成py文件 且该方法只适用于py2 无混淆 因为py3的字节码结构有些许变化 step1: 在 ...

  7. (1)ASP.NET Core3.1 Ocelot介绍

    1.简介 Ocelot原本设计仅为与.NET Core一起使用的,它是一个.NET API网关,作为面向使用.NET运行微型服务/面向服务的体系结构需要统一的系统入口点,即当客户端(Web站点,手机A ...

  8. monkey及其的日志管理和分析

    1.   monkey 1.1.  介绍 通过monkey程序模拟用户触摸屏幕,滑动Trackball.按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常,检查和评估被测程序的稳定性 ...

  9. 要求用户输入用户名和密码,只要不是admin、888888就

    要求用户输入用户名和密码,只要不是admin.888888就一直提示用户名或密码错误,请重新输入 Console.WriteLine("输入账号和密码"); string a = ...

  10. Java—字符流

    一.字符流 字符流概述: 在操作过程中字节流可以操作所有数据,操作的文件中有中文字符,并且需要对 中文字符做出处理 二.字符编码表 文字-->(数字):编码."abc".ge ...