接口服务中的日志

2023-11-07

 

先来看下日志对于一个接口服务的作用:

  •  监控服务的状态,一般程序中会增加一些跟踪或者提示性的日志,用来判断服务执行的详细情况,特别是执行一些复合功能的接口或者叫聚合接口非常有利于我们判断接口的执行情况
  •  安全,用来分析调用者的身份信息,防止接口被非法恶意调用等
  •  性能,可以统计每个接口的访问执行时间来分析系统的瓶颈
  •  异常分析,对于线上的异常,在没有debug的环境下,要想分析问题原因最有价值的就要算异常的堆栈信息

上面的这几点需求,如果接口服务使用的是一些高级产品比如dubbo,其实它已经实现了大部分功能,不需要人为的去全部处理功能。

  •   安全,可利用访问日志的功能来实现,访问日志中详细的记录了客户端调用的时间,身份,调用时的详细参数。  
[DUBBO] [2016-08-27 14:47:06] 10.10.50.20:64948 -> 10.10.50.20:20960
 - ProductFacadeService getDataDictionaryByType(DataDictionaryParamInfo)
 [{"code":0,"type":1}], dubbo version: 2.8.1, current host: 10.10.50.20

        上面的日志我们可以看到如下有用的信息:

         1:调用方IP以及端口信息:10.10.50.20:64948

         2:服务端的IP以及端口信息:10.10.50.20:20960

         3:调用时间:2016-08-27 14:47:06

         4:调用的接口方法:ProductFacadeService getDataDictionaryByType(DataDictionaryParamInfo)
         5:调用的接口方法参数:[{"code":0,"type":1}]

  •   异常分析,会自动将产生的未捕获异常信息记录到日志中,不需要手工的去处理异常的记录。上面访问日志中的参数记录信息也对分析线上问题提供了非常有力的帮助。


其实dubbo自己提供的日志功能已经非常强了,大多数情况下已经够用,但如果想有一些更加强的功能就需要自己想想办法了:

  •   监控服务的状态,这类跟踪信息只能依靠程序逻辑来实现,没有其它捷径
  •   性能,dubbo的访问日志只记录了接口调用的开始时间,没有结束时间,要想统计某个方法或者某个请求的整体执行时间是无能为力的


如果你的服务接口不是dubbo,比如是基于spring mvc 实现的rest api接口,那么就没有上面dubbo帮你做的那些好用的日志功能了。但是spring mvc实现上面的日志需求方法也是很多的,典型的例子可以借助HandlerInterceptor这类拦截器,只需要实现这个接口,然后在实现类中增加日志功能即可:我们可以通过handler这个参数来获取你想要的所有数据,另外还有request以及response两个信息丰富的对象供你使用。

public interface HandlerInterceptor {

    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception;

    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}

 

还有一种是自己实现一个AOP功能去解决我们的需求问题。AOP不是这篇的重点,我贴一张图用来简单展示AOP的作用(上面提到的HandlerInterceptor 也属于AOP的具体应用)。

将某些非业务的功能通过拦截的形式注入到流程中,而非紧密耦合在业务逻辑中,比如下面的记录方法参数的代码就是紧耦合,不推荐:

public ValueResult<ApplyIsvRoleInfo> queryUserApplyIsvInfo(String appKey, String accessToken) {
        log.info("receive queryUserApplyIsvInfo! and the params appKey: [" + appKey + "], accessToken:[" + accessToken + "]");
     ...... do something
}


AOP我这里使用aspectj,当然也可以使用spring自带的。

  •   创建一个用于日志的注解,用来控制记录的具体功能。比如有些方法是不推荐记录返回记录的,像列表查询,也有利于精确的配置日志生效的范围
 @Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {

    /**
     * 是否启动日志功能
     * @return
     */
    boolean enable() default true;

    /**
     * 是否记录返回值
     * 列表数据不推荐记录
     * @return
     */
    boolean isLogResult() default false;

    /**
     * 是否记录方法的参数名称以及值
     * @return
     */
    boolean isLogParameter() default true;

    /**
     * 是否记录执行时间
     * @return
     */
    boolean isLogElapsed() default true;
}
  • 创建一个服务端的日志拦截器,由于我们需要记录方法的执行时间,那么最适合的就是写一个around的方法,在方法执行前后记录时间从而计算出时间差。对于参数,我们也很容易实现,根据反射我们可以轻松得到调用方法的所有参数名称以及参数的具体值,下面是实现这个拦截器用的工具类介绍:

         a:计算时间,这里采用google提供的Stopwatch,这个东西对于从.NET转JAVA的来讲太亲切了

       Stopwatch sw=Stopwatch.createStarted();
        Object result = joinPoint.proceed();
        sw.stop();
        long elapsed=sw.elapsed(TimeUnit.NANOSECONDS);

    
        b:记录参数内容,对于object对象,我们将其序列化成json,可以利用jackson完成,其它的复杂点的就是通过反射来完成参数以及值。记录日志根据注解的配置来判断应该记录哪些日志项。

@Aspect
public class ServicesLoggerInterceptor {

    @Pointcut("execution(* com.chanjet.csp.product.service.service.impl.*ServiceImpl*.*(..))")
    public void pointCut() {
    }

    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Stopwatch sw=Stopwatch.createStarted();
        Object result = joinPoint.proceed();
        sw.stop();

        long elapsed=sw.elapsed(TimeUnit.NANOSECONDS);

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();

        MethodLog methodLog= targetMethod.getAnnotation(MethodLog.class);
        if(null!=methodLog&&methodLog.enable()){
            StringBuilder logLine = new StringBuilder(joinPoint.getSignature().getName());
            if(methodLog.isLogParameter()) {
                Object[] paramValues = joinPoint.getArgs();
                CodeSignature codeSignature= ((CodeSignature) joinPoint.getSignature());
                String[] paramNames = codeSignature.getParameterNames();
                logLine.append("(");
                if (paramNames.length != 0) {
                    AspectLogUtils.logParamValues(logLine, paramNames, paramValues);
                }
                logLine.append(") - started");
            }
            if(methodLog.isLogResult()) {
                logLine.append("  Return Value : ");
                logLine.append(AspectLogUtils.toString(result));
            }
            if(methodLog.isLogElapsed()) {
                logLine.append("  elapsed nanoseconds:" + elapsed);
            }
            AspectLogUtils.getLogger().info(logLine.toString());

        }

        return result;
    }
}


       c:创建一个日志的工具类,用来实现具体的日志记录。

public final  class AspectLogUtils {

    private static Logger logger = LoggerFactory.getLogger(AspectLogUtils.class);

    private AspectLogUtils() {

    }

    public static Logger getLogger() {
        return logger;
    }

    public static void logParamValues(StringBuilder logLine, String[] paramNames, Object[] paramValues) {
        for (int i = 0; i < paramValues.length; i++) {
            logLine.append(paramNames[i]).append("=").append(toString(paramValues[i]));
            if (i < paramValues.length - 1) {
                logLine.append(", ");
            }
        }
    }

    public static String toString(Object object) {
        return JsonUtils.toJsonString(object);
    }
}
  • 在Spring中加载这个拦截器
<bean class="ServicesLoggerInterceptor"></bean>
    <aop:aspectj-autoproxy proxy-target-class="true"/>

INFO  getDataDictionaryByType(dataDictionaryParamInfo={"type":1,"code":0}) - started  elapsed nanoseconds:11491670

 

转载于:https://www.cnblogs.com/ASPNET2008/p/5813518.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

接口服务中的日志 的相关文章

随机推荐

  • 【零基础】学python数据结构与算法笔记15-欧几里得、RSA

    文章目录 前言 95 欧几里得算法 96 RSA算法介绍 97 RSA算法测试 98 算法课程总结 总结 前言 学习python数据结构与算法 学习常用的算法 b站学习链接 95 欧几里得算法 求最大公约数 欧几里得算法 gcd a b g
  • OpenWrt下使用docker安装icloudpd实现iPhone照片备份私有云盘nas

    文章目录 一 背景与需求 二 部署方法 2 1 我的环境 2 2 准备工作 2 2 1 检查docker环境是否运行正常 2 2 2 查询宿主系统用户id和组id 2 2 3 规划挂载目录 2 2 4 编写启动参数 2 2 5 解决Open
  • python+selenium 爬取微博(网页版)并解决账号密码登录、短信验证

    使用python selenium 爬取微博 前言 为什么爬网页版微博 为什么使用selenium 怎么模拟微博登录 一 事前准备 二 Selenium安装 关于selenium 安装步骤 三 selenium定位网页元素 基本方法 详细使
  • 启动Nginx出现Failed to start nginx.service:unit not found

    vi etc init d nginx 粘贴下面代码 bin sh nginx this script starts and stops the nginx daemin chkconfig 85 15 description Nginx
  • UE4信息交互-关卡蓝图内的交互

    笔记为 风之神韵 蓝图进阶教学视频中讲解的内容 整理笔记是为了便于学习和查询 UE4版本 4 8 1 一 事件调用函数的方式 1 gt 第一步 打开关卡蓝图 菜单Blueprints gt Open Level Blueprint 如图 2
  • sqlite3编译和使用

    前言 SQLite 是一个 C 语言库 它实现了一个 小型 快速 自包含 高可靠性 功能齐全的SQL 数据库引擎 SQLite 是世界上使用最广泛的数据库引擎 SQLite 内置于所有手机和大多数计算机中 并捆绑在人们每天使用的无数其他应用
  • 判断一个数是不是完全平方数的巧妙方法(不用库函数)

    给定一个 正整数 num 编写一个函数 如果 num 是一个完全平方数 则返回 true 否则返回 false 不要 使用任何内置的库函数 如 sqrt 示例 1 输入 num 16 输出 true 示例 2 输入 num 14 输出 fa
  • Paradigm重金3亿刀下注NFT市场

    隔夜大饼 BTC 破位45k 回踩42k一线 昨天 5号 加密市场传来好消息 NFT界的 淘宝 NFT交易市场opensea完成了3亿美元的C轮巨额融资 投后估值133亿美元 这个穿越了一轮周期的项目成立于2017年底的那一波牛市泡沫期 2
  • Candence中查看MOS管阈值电压Vth、Vgs、Vds、跨导gm、Id等详细MOS参数的方法

    Candence中查看MOS管阈值电压Vth Vgs Vds gm Id等详细MOS参数的方法 ADE仿真结束后 点击工具栏Results Print Transient Operating Points 如果是dc仿真就选DC Opera
  • LinearRegression,RidgeCV,LassoCV,ElasticNetCV各自使用场景

    LinearRegression RidgeCV LassoCV ElasticNetCV各自使用场景 概念 线性回归的目的是要得到输出向量Y和输入特征X之间的线性关系 求出线性回归系数 也就是Y X 其中Y的维度为m x 1 X的维度为
  • Sqlilabs-21

    来到了第 21 关 看着 21 关 我知道 第一阶段快结束了 马上要到 啪 给我认真写博文 这一关卡的题目给内容貌似一点都不符合 还是得从 cookie 下手 但肯定不跟20 关一样 通过提交正常的数据 我们可以看到 COOKIE 长的有点
  • 利用opencv提取纸张边缘

    纸张图像边框提取 摆正以及是否填写检测 JockerWong的博客 CSDN博客
  • Qt打包程序发布(适用小白)

    一 概述 自己用Qt写好程序 只能在自己电脑上运行 Debug调试版本 那如何发布 Release版本 呢 可以在别人电脑上运行呢 这篇文章超详细的介绍如何打包发布 二 环境 用QT 5 9 2 QT 5以上都可以 Enigma Virtu
  • 数据分析系列目录

    数据分析系列目录 统计学元知识 从一到全部 python数据分析复盘 爬虫相关库 Scrapy爬虫实例 南方都市报 python数据分析复盘 数据分析相关库之Numpy python数据分析复盘 数据分析相关库之Pandas python数
  • vue 数据导出

    思路 1 点击导出按钮 调用后台接口获取数据fid 2 调用下载接口 获取文件流 3 把文件流传给封装的下载方法 一 在目录下面准备mixins文件里面建file js文件 blobToExcel res name var blob new
  • 怎么用C++编个网站

    1 前言 用各种语言写过一些网站之后 我不禁开始好奇 C 能不能写网站呢 实际上想到这个问题的时候 我就大致有了答案 本文中我们将尝试用C 写一个简单的网站 探索一些细节性的问题 并报告用C 写网站的体验如何 2 准备工作 无论是之前比较传
  • 现代操作系统原理与实践01:操作系统概述

    目录 1 什么是操作系统 1 1 操作系统的功能 1 1 1 对硬件进行管理和抽象 1 1 2 为应用提供服务和管理 1 3 操作系统的结构 1 4 操作系统的发展趋势 1 5 操作系统面临的挑战 1 5 1 操作系统的可扩展性 1 5 2
  • Cacheable序列化配置

    import org springframework cache CacheManager import org springframework cache annotation CachingConfigurerSupport impor
  • IDEA左侧,按照文件夹排序

    看下图 一开始设置了是没有效果的 设置完要双击项目 把项目重新伸缩下 或者重启
  • 接口服务中的日志

    先来看下日志对于一个接口服务的作用 监控服务的状态 一般程序中会增加一些跟踪或者提示性的日志 用来判断服务执行的详细情况 特别是执行一些复合功能的接口或者叫聚合接口非常有利于我们判断接口的执行情况 安全 用来分析调用者的身份信息 防止接口被