我的编程空间,编程开发者的网络收藏夹
学习永远不晚

java如何实现统一打印入参出参等日志

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

java如何实现统一打印入参出参等日志

这篇文章主要介绍“java如何实现统一打印入参出参等日志”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java如何实现统一打印入参出参等日志”文章能帮助大家解决问题。

    1.背景   

    SpringBoot项目中,之前都是在controller方法的第一行手动打印 log,return之前再打印返回值。有多个返回点时,就需要出现多少重复代码,过多的非业务代码显得十分凌乱。

    本文将采用AOP 配置自定义注解实现 入参、出参的日志打印(方法的入参和返回值都采用 fastjson 序列化)。

    2.设计思路

    将特定包下所有的controller生成代理类对象,并交由Spring容器管理,并重写invoke方法进行增强(入参、出参的打印).

    3.核心代码

    3.1 自定义注解

    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import({InteractRecordBeanPostProcessor.class})public @interface EnableInteractRecord {        String[] basePackages() default {};        String[] exclusions() default {};}

    3.2 实现BeanFactoryPostProcessor接口

    作用:获取EnableInteractRecord注解对象,用于获取需要创建代理对象的包名,以及需要排除的包名

    @Componentpublic class InteractRecordFactoryPostProcessor implements BeanFactoryPostProcessor {    private static Logger logger = LoggerFactory.getLogger(InteractRecordFactoryPostProcessor.class);    private EnableInteractRecord enableInteractRecord;    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        try {            String[] names = beanFactory.getBeanNamesForAnnotation(EnableInteractRecord.class);            for (String name : names) {                enableInteractRecord = beanFactory.findAnnotationOnBean(name, EnableInteractRecord.class);                logger.info("开启交互记录 ", enableInteractRecord);            }        } catch (Exception e) {            logger.error("postProcessBeanFactory() Exception ", e);        }    }    public EnableInteractRecord getEnableInteractRecord() {        return enableInteractRecord;    }}

    3.3 实现MethodInterceptor编写打印日志逻辑

    作用:进行入参、出参打印,包含是否打印逻辑

    @Componentpublic class ControllerMethodInterceptor implements MethodInterceptor {    private static Logger logger = LoggerFactory.getLogger(ControllerMethodInterceptor.class);    // 请求开始时间    ThreadLocal<Long> startTime = new ThreadLocal<>();    private String localIp = "";    @PostConstruct    public void init() {        try {            localIp = InetAddress.getLocalHost().getHostAddress();        } catch (UnknownHostException e) {            logger.error("本地IP初始化失败 : ", e);        }    }    @Override    public Object invoke(MethodInvocation invocation) {        pre(invocation);        Object result;        try {            result = invocation.proceed();            post(invocation, result);            return result;        } catch (Throwable ex) {            logger.error("controller 执行异常: ", ex);            error(invocation, ex);        }        return null;    }    public void error(MethodInvocation invocation, Throwable ex) {        String msgText = ex.getMessage();        logger.info(startTime.get() + " 异常,请求结束");        logger.info("RESPONSE : " + msgText);        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));    }    private void pre(MethodInvocation invocation) {        long now = System.currentTimeMillis();        startTime.set(now);        logger.info(now + " 请求开始");        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        HttpServletRequest request = attributes.getRequest();        logger.info("URL : " + request.getRequestURL().toString());        logger.info("HTTP_METHOD : " + request.getMethod());        logger.info("REMOTE_IP : " + getRemoteIp(request));        logger.info("LOCAL_IP : " + localIp);        logger.info("METHOD : " + request.getMethod());        logger.info("CLASS_METHOD : " + getTargetClassName(invocation) + "." + invocation.getMethod().getName());        // 获取请求头header参数        Map<String, String> map = new HashMap<String, String>();        Enumeration<String> headerNames = request.getHeaderNames();        while (headerNames.hasMoreElements()) {            String key = (String) headerNames.nextElement();            String value = request.getHeader(key);            map.put(key, value);        }        logger.info("HEADERS : " + JSONObject.toJSONString(map));        Date createTime = new Date(now);        // 请求报文        Object[] args = invocation.getArguments();// 参数        String msgText = "";        Annotation[][] annotationss = invocation.getMethod().getParameterAnnotations();        for (int i = 0; i < args.length; i++) {            Object arg = args[i];            if (!(arg instanceof ServletRequest)                    && !(arg instanceof ServletResponse)                    && !(arg instanceof Model)) {                RequestParam rp = null;                Annotation[] annotations = annotationss[i];                for (Annotation annotation : annotations) {                    if (annotation instanceof RequestParam) {                        rp = (RequestParam) annotation;                    }                }                if (msgText.equals("")) {                    msgText += (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);                } else {                    msgText += "," + (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);                }            }        }        logger.info("PARAMS : " + msgText);    }    private void post(MethodInvocation invocation, Object result) {        logger.info(startTime.get() + " 请求结束");        if (!(result instanceof ModelAndView)) {            String msgText = JSONObject.toJSONString(result);            logger.info("RESPONSE : " + msgText);        }        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));    }    private String getRemoteIp(HttpServletRequest request) {        String remoteIp = null;        String remoteAddr = request.getRemoteAddr();        String forwarded = request.getHeader("X-Forwarded-For");        String realIp = request.getHeader("X-Real-IP");        if (realIp == null) {            if (forwarded == null) {                remoteIp = remoteAddr;            } else {                remoteIp = remoteAddr + "/" + forwarded.split(",")[0];            }        } else {            if (realIp.equals(forwarded)) {                remoteIp = realIp;            } else {                if (forwarded != null) {                    forwarded = forwarded.split(",")[0];                }                remoteIp = realIp + "/" + forwarded;            }        }        return remoteIp;    }    private String getTargetClassName(MethodInvocation invocation) {        String targetClassName = "";        try {            targetClassName = AopTargetUtils.getTarget(invocation.getThis()).getClass().getName();        } catch (Exception e) {            targetClassName = invocation.getThis().getClass().getName();        }        return targetClassName;    }}

    AopTargetUtils:

    public class AopTargetUtils {                    public static Object getTarget(Object proxy) throws Exception {                    if(!AopUtils.isAopProxy(proxy)) {            return proxy;//不是代理对象          }                    if(AopUtils.isJdkDynamicProxy(proxy)) {            return getJdkDynamicProxyTargetObject(proxy);          } else { //cglib              return getCglibProxyTargetObject(proxy);          }                                    }          private static Object getCglibProxyTargetObject(Object proxy) throws Exception {          Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");          h.setAccessible(true);        Object dynamicAdvisedInterceptor = h.get(proxy);                    Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");          advised.setAccessible(true);                    Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();                  return getTarget(target);    }          private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {          Field h = proxy.getClass().getSuperclass().getDeclaredField("h");          h.setAccessible(true);          AopProxy aopProxy = (AopProxy) h.get(proxy);                  Field advised = aopProxy.getClass().getDeclaredField("advised");          advised.setAccessible(true);                    Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();                  return getTarget(target);     }        }

    3.4 实现BeanPostProcessor接口

    作用:筛选出需要生成代理的类,并生成代理类,返回给Spring容器管理。

    public class InteractRecordBeanPostProcessor implements BeanPostProcessor {    private static Logger logger = LoggerFactory.getLogger(InteractRecordBeanPostProcessor.class);    @Autowired    private InteractRecordFactoryPostProcessor interactRecordFactoryPostProcessor;    @Autowired    private ControllerMethodInterceptor controllerMethodInterceptor;    private String BASE_PACKAGES[];//需要拦截的包    private String EXCLUDING[];// 过滤的包    //一层目录匹配    private static final String ONE_REGEX = "[a-zA-Z0-9_]+";    //多层目录匹配    private static final String ALL_REGEX = ".*";    private static final String END_ALL_REGEX = "*";    @PostConstruct    public void init() {        EnableInteractRecord ir = interactRecordFactoryPostProcessor.getEnableInteractRecord();        BASE_PACKAGES = ir.basePackages();        EXCLUDING = ir.exclusions();    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        try {            if (interactRecordFactoryPostProcessor.getEnableInteractRecord() != null) {                // 根据注解配置的包名记录对应的controller层                if (BASE_PACKAGES != null && BASE_PACKAGES.length > 0) {                    Object proxyObj = doEnhanceForController(bean);                    if (proxyObj != null) {                        return proxyObj;                    }                }            }        } catch (Exception e) {            logger.error("postProcessAfterInitialization() Exception ", e);        }        return bean;    }    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        return bean;    }    private Object doEnhanceForController(Object bean) {        String beanPackageName = getBeanPackageName(bean);        if (StringUtils.isNotBlank(beanPackageName)) {            for (String basePackage : BASE_PACKAGES) {                if (matchingPackage(basePackage, beanPackageName)) {                    if (EXCLUDING != null && EXCLUDING.length > 0) {                        for (String excluding : EXCLUDING) {                            if (matchingPackage(excluding, beanPackageName)) {                                return bean;                            }                        }                    }                    Object target = null;                    try {                        target = AopTargetUtils.getTarget(bean);                    } catch (Exception e) {                        logger.error("AopTargetUtils.getTarget() exception", e);                    }                    if (target != null) {                        boolean isController = target.getClass().isAnnotationPresent(Controller.class);                        boolean isRestController = target.getClass().isAnnotationPresent(RestController.class);                        if (isController || isRestController) {                            ProxyFactory proxy = new ProxyFactory();                            proxy.setTarget(bean);                            proxy.addAdvice(controllerMethodInterceptor);                            return proxy.getProxy();                        }                    }                }            }        }        return null;    }    private static boolean matchingPackage(String basePackage, String currentPackage) {        if (StringUtils.isEmpty(basePackage) || StringUtils.isEmpty(currentPackage)) {            return false;        }        if (basePackage.indexOf("*") != -1) {            String patterns[] = StringUtils.split(basePackage, ".");            for (int i = 0; i < patterns.length; i++) {                String patternNode = patterns[i];                if (patternNode.equals("*")) {                    patterns[i] = ONE_REGEX;                }                if (patternNode.equals("**")) {                    if (i == patterns.length - 1) {                        patterns[i] = END_ALL_REGEX;                    } else {                        patterns[i] = ALL_REGEX;                    }                }            }            String basePackageRegex = StringUtils.join(patterns, "\\.");            Pattern r = Pattern.compile(basePackageRegex);            Matcher m = r.matcher(currentPackage);            return m.find();        } else {            return basePackage.equals(currentPackage);        }    }    private String getBeanPackageName(Object bean) {        String beanPackageName = "";        if (bean != null) {            Class<?> beanClass = bean.getClass();            if (beanClass != null) {                Package beanPackage = beanClass.getPackage();                if (beanPackage != null) {                    beanPackageName = beanPackage.getName();                }            }        }        return beanPackageName;    }}

    3.5 启动类配置注解

    @EnableInteractRecord(basePackages = “com.test.test.controller”,exclusions = “com.test.demo.controller”)

    以上即可实现入参、出参日志统一打印,并且可以将特定的controller集中管理,并不进行日志的打印(及不进生成代理类)。

    4.出现的问题(及其解决办法)

    实际开发中,特定不需要打印日志的接口,无法统一到一个包下。大部分需要打印的接口,和不需要打印的接口,大概率会参杂在同一个controller中,根据以上设计思路,无法进行区分。

    解决办法:

    自定义排除入参打印注解

    @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ExcludeReqLog {}

    自定义排除出参打印注解

    @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ExcludeRespLog {}

    增加逻辑

    // 1.在解析requestParam之前进行判断        Method method = invocation.getMethod();        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();        boolean flag = true;        for (Annotation annotation : declaredAnnotations) {            if (annotation instanceof ExcludeReqLog) {                flag = false;            }        }        if (!flag) {            logger.info("该方法已排除,不打印入参");            return;        }// 2.在解析requestResp之前进行判断        Method method = invocation.getMethod();        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();        boolean flag = true;        for (Annotation annotation : declaredAnnotations) {            if (annotation instanceof ExcludeRespLog) {                flag = false;            }        }        if (!flag) {            logger.info("该方法已排除,不打印出参");            return;        }

    使用方法

    // 1.不打印入参    @PostMapping("/uploadImg")    @ExcludeReqLog    public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {        return demoService.uploadIdeaImg(imgFile);    }//2.不打印出参    @PostMapping("/uploadImg")    @ExcludeRespLog     public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {        return demoService.uploadIdeaImg(imgFile);    }

    关于“java如何实现统一打印入参出参等日志”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    java如何实现统一打印入参出参等日志

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    java如何实现统一打印入参出参等日志

    这篇文章主要介绍“java如何实现统一打印入参出参等日志”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java如何实现统一打印入参出参等日志”文章能帮助大家解决问题。1.背景 SpringBoo
    2023-07-05

    java项目实现统一打印入参出参等日志

    这篇文章主要介绍了java项目实现统一打印入参出参等日志方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-13

    如何实现输出执行操作和打印日志的shell脚本

    这篇文章主要为大家展示了“如何实现输出执行操作和打印日志的shell脚本”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何实现输出执行操作和打印日志的shell脚本”这篇文章吧。cat /mnt
    2023-06-09

    java 项目中集成echarts 统计图如何实现打印导出

    这篇文章将为大家详细讲解有关java 项目中集成echarts 统计图如何实现打印导出,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Echarts 由百度研发的开源软件,是一个纯 JS 的图
    2023-06-03

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录