今天下午,系统运营群里反映问题。我们查看生产日志排查问题时,发现有大文本日志不停在刷屏,严重干扰了我们对问题的定位。
大日志刷屏现(wèn)象(tí)由来已久,生产环境每天都在打印海量的log,日志文件动辄10G或20G。这些大文本通常是一些api接口签名串、数据加密串、图片文件base64串/hex串、从db或rpc接口拿到的list序列化串。
我们做任何事情,其实是要考虑成本的。生产排障尤其衡量时间成本。长时间定位不到原因,无法及时反馈和修复,小问题也可能会升(yǎn)级(biàn)为生产事故。
因此,我们在日常编码时,不要忽视这些细节。对于记日志,有必要考虑目标对象是否会输出一大段很长很长的文本。如果是,那么,就要做处理。
举个例子,下面两段代码,一个是springmvc接口,程序打印获取到的请求参数。一个是程序在调用外部API时,调用前后打印数据报文。这两块的通信报文均涉及到大字段,就要对其做处理。
@RequestMapping(value = "/common.do", method = RequestMethod.POST) public void doBusiness(@RequestBody String reqJson, HttpServletRequest request, HttpServletResponse response) throws IOException { log.info("商户API前置请求,请求参数={}", StringUtil.abbrLongJSONString(reqJson)); .... }
logger.info("OCR识别请求地址:{},参数:{}", listAuthUrl , StringUtil.abbrLongJSONString(json)); Response response = FastHttpClient. post(). addHeader("Content-Type", "application/json"). body(json). url(listAuthUrl.get(0).getCode()). build(). execute(); String result = response.body().string(); logger.info("OCR识别返回:" + result);
小改动,大受益!
上面reqJson和json都是包含了长文本的json串。那么,对于这种情况,怎么处理呢?
简单粗暴的方式,是直接利用subString截取指定长度的子串。弊端是可能会舍掉必要的信息,反而不利于生产排障。
另一种方式,则是将其中特定的大文本进行截取。如下StringUtil方法可供参考。
/** * 截取json字符串中的大文本 * @param jsonStr 含有特长value的JSON字符串 * @return */ public static String cutLongJSONString(String jsonStr) { String[] split = jsonStr.split("\""); System.out.println(split.length); for (int i = 0; i < split.length; i++) { if (split[i].length() > 100) { split[i] = cutLongString(split[i], 128); } } return String.join("\"", split); } /** * 截取大文本字符串。 * 本方法会包含原串的长度。如果不关注,则可直接使用{@link org.apache.commons.lang3.StringUtils#abbreviate(String, int)}或其重载方法 * @param longString * @param length * @return */ public static String cutLongString(String longString, int length) { if(StringUtils.isBlank(longString)){ return ""; } StringBuilder sb=new StringBuilder(); if (longString.length()>length){ sb.append(longString, 0, length).append("...(length=").append(longString.length()).append(")"); return sb.toString(); } return longString; }