目录php
日志实际上只是一种按照时间顺序存储记录的数据表或文件
它记录了什么时间发生了什么事情。而对分布式数据系统,在许多方面,这是要解决的问题的真正核心html
tomcat 日志java
日志记录了发生了什么,而每一个表或者索引都是更改历史中的一个投影。因为日志是当即持久化的,发生崩溃时,能够做为恢复其余全部持久化结构的可靠来源linux
机器可识别的日志的概念主要都被局限在数据库的内部。日志做为作数据订阅机制的用法彷佛是偶然出现的。 但这正是支持各类的消息传输、数据流和实时数据处理的理想抽象nginx
分布式系统以日志为中心的方案是来自于一个简单的观察,咱们称之为状态机复制原理(State Machine Replication Principle):git
若是两个相同的、肯定性的进程从同一状态开始,而且以相同的顺序得到相同的输入,那么这两个进程将会生成相同的输出,而且结束在相同的状态github
肯定性(deterministic)
和幂等概念相似,一样的输入,任何状况都获得一样的输出
意味着处理过程是与时间无关的,并且不会让任何其余『带外』输入("out of band" input)影响其处理结果web
举例:
咱们甚至能够记录各个副本执行的机器指令序列的日志 或是 所调用的方法名和参数序列的日志。 只要两个进程用相同的方式处理这些输入,这些副本进程就会保持一致的状态。spring
物理与逻辑日志
对日志用法不一样群体有不一样的说法。数据库工做者一般说成 物理 日志(physical logging)和 逻辑 日志(logical logging)。 物理日志是指记录每一行被改变的内容。逻辑日志记录的不是改变的行而是那些引发行的内容改变的SQL语句(insert、update和delete语句)数据库
状态机器模型
分布式系统文献一般把处理和复制(processing and replication)方案宽泛地分红两种。『状态机器模型』
『主-主模型』(active-active model), 记录输入请求的日志,各个复本处理每一个请求。
『主-备模型』(primary-backup model),即选出一个副本作为leader,让leader按请求到达的顺序处理请求,并输出它请求处理的状态变化日志。 其余的副本按照顺序应用leader的状态变化日志,保持和leader同步,并可以在leader失败的时候接替它成为leader。
变动日志(changelog)101:表与事件的二象性(duality)
变动的日志 和 表之间有着迷人的二象性。 日志相似借贷清单和银行处理流水,而数据库表则是当前帐户的余额。若是有变动日志,你就能够应用这些变动生成数据表并获得当前状态
能够认识到日志是更基本的数据结构:日志除了可用来建立原表,也能够用来建立各种衍生表
表与事件的二象性: 表支持了静态数据,而日志记录了变动。日志的魅力就在于它是变动的 完整 记录,它不只仅包含了表的最终版本的内容, 并且能够用于重建任何存在过其它版本。事实上,日志能够看做是表 每一个 历史状态的一系列备份
表与事件是数据在不一样条件/场景下数据的性质,是对人们对数据的认识、理解或描述方式。
版本控制系统经过日志来完成复制:更新代码便是拉下补丁并应用到你的当前快照中。
这块没有总结出一个完成的定义,仍是把各类实现方式带给你们,让你们来理解看看你们有没有其余的更好的理解和建议
OpenTracing语义标准规范及实现
https://www.jianshu.com/p/a963ad0bbe3e
如何使用 AOP 和自定义注解实现请求方法先后日志打印
https://mp.weixin.qq.com/s/J9eyqIx5Oq-z6mYv8j1zpg
在SpringBoot项目中添加logback的MDC
(Mapped Diagnostic Context,用于打LOG时跟踪一个“会话“、一个”事务“)
https://blog.csdn.net/hongyang321/article/details/78803584
服务端最佳日志实践(v2.0)
https://zhuanlan.zhihu.com/p/27363484
基于elk的业务日志格式设计
https://segmentfault.com/a/1190000008227989
跟踪日志,程序日志,操做日志
http://dev.bingocc.com/dtls/logs/prog.html
{ "type": "trace_id": "span_id": "thread": "timestamp": "source_host": "component": "logs": [ {"LogItem":"LogItem"} ] }
LogItem
{ "level": "thread": "timestamp": "logger": "message": "stack": "tags": { } }
咱们日常使用的 tail -f xxx.log 的形式,来动态观察错误,调试程序
方便查看
使用这类工具依赖能够有web界面来查看log,使用起来更友好,搜索条件,无需熟悉linux命令,便可快速的查询指定时间段的log或是包含指定关键字的log
聚类分析
elk 和 grey log都有一点分析图表和监控报警功能,均可以帮忙实现应用监控指标分析和报警
自定义日志收集处理
服务端接收到格式化的日志log,对log进行业务分析和统计,对程序状态,应用使用,业务状态进行分析,并能对突发状况做出预警和响应紧急措施
一段nginx log示例
-- 01 online-php7-6 2018-04-20T12:00:00+08:00 2018-04-20T12:00:00+08:00 0 web 34055 272408431 read_article {"novel_id":"2460","article_id":"883878","price":"35","consume":0}
各个字段含义
-- version, hostname, occur_time, log_time, type, platform, user_id, action, action_json
version
: 01
hostname
: online-php7-6 hostname
occur_time
: 2018-04-20T12:00:00+08:00
log_time
: 2018-04-20T12:00:00+08:00
platform
: web
user_id
: 367245568
action
: read_article {"novel_id":"1542","article_id":"672651","price":"35","consume":1}
业务含义
根据上述的打印方式能够追踪到app内部每一个模块业务被触发的频次,这只是处理了业务这一范围,其实还能够有不少范围好比:关键业务路径范围
,业务异常警告报错范围
等等,一切围绕应用的日志
当你遇到问题的时候,只能经过debug功能来肯定问题,你应该考虑打日志,良好的系统,是能够经过日志进行问题定为的。
当你碰到if…else 或者 switch这样的分支时,要在分支的首行打印日志,用来肯定进入了哪一个分支
常常以功能为核心进行开发,你应该在提交代码前,能够肯定经过日志能够看到整个流程
必须使用参数化信息的方式:
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
对于debug日志,必须判断是否为debug级别后,才进行使用:
if (logger.isDebugEnabled()) { logger.debug("Processing trade with id: " +id + " symbol: " + symbol); }
使用[]进行参数变量隔离
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
并非全部的service都进行出入口打点记录,单1、简单service是没有意义的(job除外,job须要记录开始和结束,)。
反例(不要这么作):
public List listByBaseType(Integer baseTypeId) { log.info("开始查询基地"); BaseExample ex=new BaseExample(); BaseExample.Criteria ctr = ex.createCriteria(); ctr.andIsDeleteEqualTo(IsDelete.USE.getValue()); Optionals.doIfPresent(baseTypeId, ctr::andBaseTypeIdEqualTo); log.info("查询基地结束"); return baseRepository.selectByExample(ex); }
对于复杂的业务逻辑,须要进行日志打点,以及埋点记录,好比电商系统中的下订单逻辑,以及OrderAction操做(业务状态变动)
重要的状态的变动发送事件并留出监听接口,这个主题下含义是至少要打印log
service为SOA架构,微服务架构,REST接口,那么能够当作是一个外部接口提供方,那么必须记录入参。
调用其余第三方服务时,全部的出参和入参是必需要记录的(由于你很难追溯第三方模块发生的问题)
特别详细的系统运行完成信息,业务代码中,不要使用.(除非有特殊用意,不然请使用DEBUG级别替代)
@Override @Transactional public void createUserAndBindMobile(@NotBlank String mobile, @NotNull User user) throws CreateConflictException{ boolean debug = log.isDebugEnabled(); if(debug){ log.debug("开始建立用户并绑定手机号. args[mobile=[{}],user=[{}]]", mobile, LogObjects.toString(user)); } try { user.setCreateTime(new Date()); user.setUpdateTime(new Date()); userRepository.insertSelective(user); if(debug){ log.debug("建立用户信息成功. insertedUser=[{}]",LogObjects.toString(user)); } UserMobileRelationship relationship = new UserMobileRelationship(); relationship.setMobile(mobile); relationship.setOpenId(user.getOpenId()); relationship.setCreateTime(new Date()); relationship.setUpdateTime(new Date()); userMobileRelationshipRepository.insertOnDuplicateKey(relationship); if(debug){ log.debug("绑定手机成功. relationship=[{}]",LogObjects.toString(relationship)); } log.info("建立用户并绑定手机号. userId=[{}],openId=[{}],mobile=[{}]",user.getId(),user.getOpenId(),mobile); // 若是考虑安全,手机号记得脱敏 }catch(DuplicateKeyException e){ log.info("建立用户并绑定手机号失败,已存在相同的用户. openId=[{}],mobile=[{}]",user.getOpenId(),mobile); throw new CreateConflictException("建立用户发生冲突, openid=[%s]",user.getOpenId()); } }
日志打印通常都是,代码写好后部署上去的,忘记加的须要从新编写而后重启发布,这块其实也有热部署的方案
这块其实更侧重于在线调试,不过都是定位问题,也能够放在日志范畴来讨论,动态替换代码,加入日志打印代码,从新使用classloader加入内存
BTrace
这个偷懒贴了两篇帖子
https://tech.meituan.com/2019/02/28/java-dynamic-trace.html
https://www.ezlippi.com/blog/2018/01/btrace-introduce.html
arthas 一样能够动态打印追踪
使用redefine动态修改代码,加入log打印而后在从新编译会classloader
https://alibaba.github.io/arthas/
https://alibaba.github.io/arthas/redefine.html
Logging 日志记录最佳实践
https://www.oschina.net/question/12_44624
Logging 最佳实践
https://www.cnblogs.com/zhengyun_ustc/archive/2012/12/15/logging_bp.html
OpenTracing语义标准规范及实现
https://www.jianshu.com/p/a963ad0bbe3e
统一日志服务系统架构
http://dev.bingocc.com/dtls/arch.html
原来这才是日志打印的正确姿式
https://blog.csdn.net/u013256816/article/details/94518764
一些设计上的基本常识
https://gitee.com/52itstyle/spring-boot-seckill/blob/master/%E6%9E%B6%E6%9E%84%E4%B9%8B%E8%B7%AF/%E4%B8%80%E4%BA%9B%E8%AE%BE%E8%AE%A1%E4%B8%8A%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%B8%B8%E8%AF%86%20-%20%E6%A2%81%E9%A3%9E.md#
日志:每一个软件工程师都应该知道的有关实时数据的统一抽象 https://github.com/oldratlee/translations/tree/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying