背景
日志内容复杂多样,如何去收集有价值的日志是我们重点关注的。日志的价值其实是取决于业务操作的,不同的业务场景下相同类型的日志的价值会截然不同。
根据以往的业务实践,结合企业级的一些业务需求,我们选定关注以下几类日志。
跟踪日志【trace.log】 Server引擎的调试日志,用于系统维护人员定位系统运行问题使用。
系统日志【system.log】 大粒度的引擎运行的入口、出口的日志,用于调用栈分析,可以进行性能分析使用。
部署日志【deploy.log】 记录系统启动、停止、构件包部署、集群通知等信息的日志。
引擎日志【engine.log】 细粒度的引擎运行日志,可以打印上下文数据,用于定位业务问题。
构件包日志【contribution.log】 构件包记录的业务日志(使用基础构件库的日志输出API写日志)
这里我们专门针对系统日志收集讨论几种收集方案,关注微信公众号Java技术栈,在后台回复:微服务,可以获取我整理的 N 篇 微服务系列教程,都是干货。
方案一:通过日志组件来收集
这里是指通过logback、log4j等日志组件来输出文件,然后再通过文件输出到logstash、kibana等日志组件中,通过这些日志组件来进行可视化统计与分析,这里需要统一关键日志输出格式方便日后统计搜索。
优点
操作简单,收集方便
减少业务依赖
粒度细
缺点
依赖于logstash、kibana
只能满足简单的日志操作,详细点或者个性化需求操作起来比较复杂
方案二:使用aop来拦截controller
拦截controller层,通过controller中的方法名是否包含insert、update、delete等关键字来记录业务日志。
优点
操作简单,收集方便
缺点
只能记录简单日志
不同的人命名习惯不一样,日志可能不准确
方案三:使用注解来,进行稍微精准的业务日志记录
这个方案粒度可大可小,代码侵入性也比较小,可操作性比较强,如果需要获取参数信息或者返回值信息,可以通过注解配置获取到,可以集合fastjson中的jpath来获取参数值下面有几个伪代码供参考
@Slf4j @Aspect public class SysLogAspect { @Around("@annotation(sysLog)") @SneakyThrows public Object around(ProceedingJoinPoint point, SysLog sysLog) { // 根据系统上下文获取相关数据 Operation logVo = SysLogUtils.getOperationModel(); Object obj=null; try{ // 操作方式 logVo.setOperationName(sysLog.value()); // 操作时间 logVo.setOperationTime(new Date()); logVo.setObjectType(sysLog.objectType().name()); logVo.setAppName(StringUtils.defaultString(sysLog.appName(),"TSP")); // 发送异步日志事件 obj = point.proceed(); if(StringUtils.isNotBlank(sysLog.objectIdKey())&&StringUtils.isNotBlank(sysLog.objectNameKey())){ Map<String,Object> params=Maps.newHashMap(); params.put("args",point.getArgs()); params.put("response",obj); logVo.setObjectId(getKeyValue(sysLog.objectIdKey(),params)); logVo.setObjectName(getKeyValue(sysLog.objectNameKey(),params)); } }catch (BusinessException e){ logVo.setException(e.getMessage()); throw new Exception(e); }catch (RuntimeException e){ logVo.setException(e.getMessage()); throw new Exception(e); }catch (Exception e){ logVo.setException(e.getMessage()); throw new Exception(e); }finally { ApplicationContextUtils.publishEvent(new OperationEvent(logVo)); } return obj; } private String getKeyValue(String key,Map<String,Object> params){ try{ Object mm= JSONPath.eval(params,key); if(mm!=null){ return mm.toString(); } }catch (Exception e){ log.error("JSONPath.eval:",e); } return null; } } @SysLog(value = "xxxxxx",objectType = LogObjectType.OFFLINE_THRONG, objectIdKey = "$['args'][0][0]['id']",objectNameKey = "$['response'][0][0]['throngName']")
优点
操作简单
较灵活,粒度可大可小
缺点
有代码侵入
个性化需求不满足
方案四:针对复杂场景或者审计需求手动记录,侵入性强
如果有些业务共用了方法,需要更小的粒度,或者需要记录业务数据变更记录,这时就只能选择侵入式较强的方式来记录日志了,直接在业务代码中记录日志,日志系统新贵 Loki,这个推荐看下。
logClient.logObject(LogObjectType.XXX,id,UserContext.getUserName(),"编辑xxx","xxx变更为xxxxx");
方案五:记录sql日志
则可以使用mybatis拦截器来进行,如果没有使用mybatis,则可以做一个通用的preparestatement以及statement代理。
当然现在已经有很对监控组件可以满足这个需求,比如说jeager、javamelody、druid等。
总结
一般情况下我们会采用多种方式来记录业务日志,这个都是根据具体需求来进行评估用哪几种方式可以更好的达到产品需求。