千家信息网

定位bug打日志的方式有哪些

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍"定位bug打日志的方式有哪些",在日常操作中,相信很多人在定位bug打日志的方式有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"定位bug打日志的
千家信息网最后更新 2026年11月01日定位bug打日志的方式有哪些

这篇文章主要介绍"定位bug打日志的方式有哪些",在日常操作中,相信很多人在定位bug打日志的方式有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"定位bug打日志的方式有哪些"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

请统一日志格式

日志格式最好是统一的,即方便查看定位问题又方便统计收集。我一般喜欢定义一个LogObject对象,里面定义日志的各个字段。例如:

import com.fasterxml.jackson.annotation.JsonInclude;import com.fasterxml.jackson.annotation.JsonInclude.Include;import com.fasterxml.jackson.annotation.JsonProperty;public class LogObject {@JsonProperty(index = 1)private String eventName;@JsonProperty(index = 2)private String traceId;    @JsonProperty(index = 3)private String msg;    @JsonProperty(index = 4)private long costTime;    @JsonProperty(index = 6)private Integer userId;    @JsonProperty(index = 7)private Object others;    @JsonProperty(index = 8)private Object request;    @JsonProperty(index = 9)private Object response;public String getEventName() {return eventName;    }    public LogObject setEventName(String eventName) {this.eventName = eventName;return this;    }    public Object getRequest() {return request;    }    public LogObject setRequest(Object request) {this.request = request;return this;    }    public Object getResponse() {return response;    }    public LogObject setResponse(Object response) {this.response = response;return this;    }    public String getMsg() {return msg;    }    public LogObject setMsg(String msg) {this.msg = msg;return this;    }public long getCostTime() {return costTime;    }    public LogObject setCostTime(long costTime) {this.costTime = costTime;return this;    }public Integer getUserId() {return userId;    }    public LogObject setUserId(Integer userId) {this.userId = userId;return this;    }    public Object getOthers() {return others;    }    public LogObject setOthers(Object others) {this.others = others;return this;    }    public String getTraceId() {return traceId;    }    public LogObject setTraceId(String traceId) {this.traceId = traceId;return this;    }
  • traceId: 调用链id

  • eventName: 事件名称,一般就是业务方法名称

  • userId: C端用户id

  • msg: 结果消息

  • costTime: 接口响应时间

  • request: 接口请求入参

  • response: 接口返回值

  • others: 其他业务参数

使用链式的风格,方便设置字段的值:

long endTime = System.currentTimeMillis();LogObject logObject = new LogObject();logObject.setEventName(methodName).setMsg(msg).setTraceId(traceId).setUserId(backendId).setRequest(liveRoomPushOrderReqDto).setResponse(response).setCostTime((endTime - beginTime));LOGGER.info(JSON.toJSONString(logObject));

当然最好还是封装出一个工具类出来,例如叫:LogTemplate,作为一个统一的入口。
另外可以使用JsonProperty注解,指定字段的顺序,例如通过index=1,将eventName放置在最前面。

@JsonProperty(index = 1)private String eventName;
将request和response放置在一起

将请求和返回值,放置在同一条日志里,有个好处,就是非常方便查看上下文日志。
如果打印成两条,返回值那条可能被冲到很后面,而且也得再做一次grep操作,影响效率。
具体的日志如下:

{   "eventName":"createOrder",   "traceId":"createOrder_1574923602015",   "msg":"success",   "costTime":317,   "request":{      "uId":111111111,      "skuList":[         {"skuId":22222222,"buyNum":1,"buyPrice":8800,         }      ]   },   "response":{      "code":0,      "message":"操作成功",      "data":{         "bigOrderId":"BIG2019",         "m2LOrderIds":{"MID2019":{               "22222222":"LIT2019"}         }      }   }}

为了能拼成一条,有两种方案,一种是比较low的,直接在代码里使用try catch finally,例如:

@PostMapping(value = "/createOrder")public JsonResult createOrder(@RequestBody Object request) throws Exception {    String methodName = "/createOrder";    Integer backendId = null;    String msg = "success";    long beginTime = System.currentTimeMillis();    String traceId = "createOrder_"+beginTime;    JsonResult response = null;try {        OrderCreateRsp orderCreateRsp = orderOperateService.createOrder(request, traceId);        response = JsonResult.success(orderCreateRsp);    }    catch (Exception e) {        msg = e.getMessage();LOGGER.error(methodName+",userId:"+backendId+",request:"+ JsonHelper.toJson(request),e);        throw new BizException(0,"下单失败");    }    finally {        long endTime = System.currentTimeMillis();        LogObject logObject = new LogObject();        logObject.setEventName(methodName) .setMsg(msg) .setTraceId(traceId) .setUserId(backendId) .setRequest(request) .setResponse(response) .setCostTime((endTime - beginTime));LOGGER.info(JSON.toJSONString(logObject));    }        return response;}

这种方案呢,有个缺点,就是每个业务方法都得处理日志,更好的方案是使用aop加thread local的方式,将请求统一拦截且将返回值和请求参数串起来,这个网络上的方案很多,这里就不阐述了。

对于对性能要求比较高的应用,反而推荐第一种方案,因为使用aop,有一些性能损耗。像我之前在唯品会参与的商品聚合服务,用的就是第一种方案,毕竟每一秒要处理上百万的请求。

日志里加入traceId

如果应用中已经使用了统一调用链监控方案,且能根据调用链id查询接口情况的,可以不用在代码里手动加入traceId。
如果应用还没接入调用链系统,建议加一下traceId,尤其是针对聚合服务,需要调用中台各种微服务接口的。像聚合层下单业务,需要调用的微服务就有如下这么些:

  • 营销系统

  • 订单系统

  • 支付系统
    下单业务调用这些接口的时候,如果没有使用traceId进行跟踪的话,当下单失败的时候,到底是哪个微服务接口失败了,就比较难找。下面以小程序端,调用聚合层下单接口的例子作为展示:
    营销系统:

{   "eventName":"pms/getInfo",   "traceId":"createOrder_1575270928956",   "msg":"success",   "costTime":2,   "userId":1111111111,   "request":{      "userId":1111111111,      "skuList":[         {"skuId":2222,"skuPrice":65900,"buyNum":1,"activityType":0,"activityId":0,         }      ],   },   "response":{      "result":1,      "msg":"success",      "data":{         "realPayFee":100,      }   }}

订单系统:

{   "eventName":"orderservice/createOrder",   "traceId":"createOrder_1575270928956",   "msg":"success",   "costTime":29,   "userId":null,   "request":{      "skuList":[         {"skuId":2222,"buyNum":1,"buyPrice":65900,         }      ],   },   "response":{      "result":"200",      "msg":"调用成功",      "data":{         "bigOrderId":"BIG2019",         "m2LOrderIds":{"MID2019":{               "88258135":"LIT2019"}         }      }   }}

支付系统:

{   "eventName":"payservice/pay",   "traceId":"createOrder_1575270928956",   "msg":"success",   "costTime":301,   "request":{      "orderId":"BIG2019",      "paySubject":"测试",      "totalFee":65900,   },   "response":{      "requestId":"test",      "code":0,      "message":"操作成功",      "data":{         "payId":123,         "orderId":"BIG2019",         "tradeType":"JSAPI",         "perpayId":"test",         "nonceStr":"test",         "appId":"test",         "signType":"MD5",         "sign":"test",         "timeStamp":"1575270929"  }   }}

可以看到聚合层需要调用营销、订单和支付三个应用的接口,调用的过程中,使用traceId为createOrder_1575270928956的串了起来,这样我们只需要grep这个traceId就可以把所有相关的调用和上下文找出来。
traceId如何生成呢,一种简单的做法是,使用System.currentTimeMillis() 加上业务接口名字,如:

 long beginTime = System.currentTimeMillis(); String traceId = "createOrder_"+beginTime;

加traceId会侵入到业务方法里,比如说:

public void createOrder(Object obj) {  long beginTime = System.currentTimeMillis();   String traceId = "createOrder_"+beginTime;   pmsService.getInfo(obj,traceId);   orderService.createOrder(obj,traceId);   payService.getPrepayId(obj,traceId);}

像pmsService这些内部的service方法,都需要加一个traceId字段,目前我觉得还好,要是觉得入侵了,也可以考虑thread local的方式,处理请求的时候,为当前线程存储一下traceId,然后在业务方法里,再从当前线程里拿出来,避免接口方法里的traceId满天飞。

到此,关于"定位bug打日志的方式有哪些"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

日志 接口 业务 方案 方法 系统 方式 定位 服务 统一 字段 就是 学习 应用 成功 时候 订单 处理 支付 营销 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 圆梦互联网科技 疫情对互联网科技公司影响 狮子座互联网科技有限公司 美国互联网黑科技 唐信互联网科技 大连 深圳天众互联网科技公司 火传媒上海互联网科技 互联网科技咨询 四个字互联网科技公司取名 青海省互联网新科技 创业天下互联网科技有限公司 亳州好赞互联网科技有限公司 科技类公司包括互联网吗 互联网在线教育科技创业 锡山区互联网智能科技产品供应 梅州GM互联网络科技有限公司 四川互联网展示科技馆设计 卡尔迅互联网科技有限公司 神州科技互联网有限公司 互联网科技包装股 互联网巨头科技公司 互联网科技金融 互联网未来科技 世界互联网领先科技成果 天扬科技互联网广告 厦门比较好的科技互联网公司 米聚互联网科技南京有限公司排面 互联网企业科技类企业如何做账 三亚科技创新互联网基地 银川新华互联网科技学校招聘
0