首先往大的来说,日志分2种
①业务日志: 即业务系统需要查看的日志, 常见的比如谁什么时候修改了什么.
②参数日志: 一般是开发人员遇到问题的时候定位用的, 一般不需要再业务系统里展示.
对于业务日志, 我们现在基本确定” 业务日志是业务” 这么个准则, 即业务日志应该跟随着业务表走.
比如你一个订单的操作日志, 那么订单表再哪它就应该在哪, 业务日志应该要跟着你的业务操作同生共死(事务性), 基于上述理念所以业务日志我们不会用table存
对于参数日志, 我觉得这个说是后端开发人员的撕逼生命线毫不为过, 但是同时由于参数日志其实并不属于业务的一部分(完全没有这玩意,业务也是能跑的转,业务系统也不会显示这些信息)
所以很多时候除开发人员之外的其它利益相关方其实并不在意是否有这个参数日志, 甚至不少入门级开发人员也无法理解其重要性.
而且参数日志拥有单位价值低, 但是总量却及其庞大的特点, 也因为这个特点导致数据库那边的人(比如DBA)一般也挺抗拒这个的.
而我们用Table最主要就是解决参数日志的问题.
首先一个大原则是我们希望业务日志和参数日志是能串通起来, 比如你进行了这个业务操作并且有了这个业务日志, 那么我要能回溯到执行这次业务操作的相关参数.
常规的想法是参数日志里存一个业务主键
但是订单表的话你存订单号, 用户表的话你存用户Id, 然后再来个别的业务又要存一个别的主键, 其实这挺不好扩展的, 然后参数日志就会变得乱七八糟, 另外你就算存了订单号你也没法和业务日志能直接的join出来(一般会匹配下2个日志的操作时间人肉来看)
而我们用Application Insights来作为主力监控, 我们发现它能够把一个请求/依赖项/异常等信息串联在一个列表里 参见: 统一的跨组件事务诊断
我们就很好奇它是怎么做到的,然后特地扒了一下它的SDK
发现在不同年代AppInsights通过不同的机制生产了一个在当前请求操作内的Id,然后用于各个操作之间进行关联,分别是通过:
1.早期的AppInsights里(Net 4.6之前)是通过CallContext
2.Net 4.6以后是通过AsyncLocal
3.现在NetCore年代则通过Activity
然后它Id分3个,一个是Id自身,一个是ParentId,一个是RootId
这个属于分布式追踪的内容,里面包含相对较多知识点这里就不展开太多了,具体可以看Github上微软对于Activity的用户手册里有详细描述 Activity User Guide
AppInsights这个算是给了我较大的启示,于是乎我就在想,如果我的业务日志也存下它的那个Id,然后我的参数日志也存这个Id
那么我就拥有了一个和业务无关的统一关联Id(而不是存各个业务表的业务主键),同时我甚至能实现类似它的那个“事务诊断”那样的体验,我通过一个业务日志的数据能迅速关联到我的参数日志的记录
首先对于如何记录参数日志这件事,比较笨的办法可能是如下这样
厉害点的人可能会把这个步骤放到Filter里
但是,拜托,都2021年了,我们来点稍微主流靠谱点的技术吧。
我们是使用了abp的,我觉得里面的Audit(审计日志)特性就蛮不错,我们就是通过这个来记录日志。
参考文档 审计日志
我们只需要重写一下它的 IAuditingStore(里面只有个SaveAsync方法)
然后在需要的地方打上[Audited]即可
Abp的审计日志本质是基于Castle的动态代理(DynamicProxy)来实现了AOP,然后它能获取到一个方法调用的入参/出参/执行时间/异常信息/方法名等各种信息,我们只要重写下告诉ABP怎么存就可以了
所以记录日志的时候只需要打一个特性(而且和Filter不同的是我这个特性可以打在任何基于接口获取的Public的方法里,而不局限于Controller里)
规范业务日志表
为了配套参数日志,我们也规范了业务日志表的存储。
业务日志一般会有2种比较常见的存储模式
①新值旧值得存储
②完全拷贝修改前的记录进行完全存储