思考总结:搜索团队日志实践
优采云 发布时间: 2022-11-22 18:28思考总结:搜索团队日志实践
背景
在线系统的日志可以记录在线服务的详细信息,包括请求参数、返回结果、处理过程中的错误信息等。这些信息可以帮助我们调试线上问题,根据错误日志进行告警,并将线上日志回放到测试环境,为测试提供数据源。同时,日志中记录的用户行为也可以帮助我们分析用户,生成报告,通过数据挖掘进一步优化系统。为此,我们需要能够有效地利用日志。
日志的高效利用往往包括以下几个方面:
• 日志打印:在线服务模块根据日志规范将日志打印到本地磁盘
• 日志采集
:统一采集
各个服务打印的日志
• 日志聚合:将业务相关的日志聚合在一起
• 日志分析:分析日志、定位线上问题、发出告警、挖掘用户行为、回放日志到测试环境等。
阅文搜索团队在上述方面做了一些工作。整体的日志结构如下图所示:
我们使用filebeat实时采集
各种服务的日志,传输到kafka,通过流计算聚合日志。聚合后的日志一方面写入HDFS作为离线日志供后续数据挖掘使用,另一方面写入ElasticSearch用于实时查询。在此基础上,我们开发了ylog、jceman和watcher来分析、回放和监控日志。
我们将在两篇文章中对上述方面进行扩展。第一篇文章将主要介绍日志打印规范和日志采集
的相关工作。下一篇文章将介绍日志聚合以及我们开发的几个提高日志使用效率的工具。.
日志打印
解析日志一直是个头疼的问题。本质原因是日志打印不规范,过于随意。这使得日志解析代码需要考虑各种情况,代码难以维护。另一方面,当日志格式发生变化时,往往需要修改相应的解析代码,而通常很难及时将日志变化通知到需要解析日志的下游方,而日志往往是下游日志解析出现问题时才发现格式发生了变化。为此,我们制定了一套日志打印规范。
日志规范
日志规范主要包括几个方面:日志分类、记录关键信息、日志格式需要易于解析、日志按时间/大小滚动。
日志级别
结合行业经验和自身情况,我们将日志分为以下四个级别(日志级别从低到高):
• DEBUG日志:记录程序运行的关键过程,用于在线调试问题
• INFO日志:记录接收到的请求上下文、服务返回的响应等关键信息
• WARN日志:请求处理过程中出现错误,但这些错误不会影响后续请求的处理,并会记录相应的错误信息
• ERROR日志:请求处理过程中出现致命错误,导致进程退出,并记录相应的错误信息
除了以上类别,业界常见的其他类别还有TRACE、NOTICE、FATAL。你可以参考这个链接()来了解更多关于日志级别之间的区别。
我们可以通过配置文件来控制最底层的日志打印。系统会打印所有大于或等于配置级别的日志,其他类型低于该级别的日志将不会打印。例如配置为INFO级别时,系统会打印ERROR、WARN和INFO日志,但不会打印DEBUG日志。通常我们配置日志打印级别为INFO online。
关键信息
日志应该只记录关键信息。日志分级后,不同级别的日志应该只记录与该级别相关的关键信息。如果将各种信息写入日志,会增加磁盘写入量(虽然现在磁盘大小往往不是问题,如果服务有其他任务需要读写磁盘,还是会带来磁盘petition),也会增加后续传输日志和解析日志的量。另一方面,过多的无关日志往往会干扰关键信息,影响我们对日志的分析。
根据我们的经验,我们通常会在日志中记录以下信息:
" />
• 服务名称:关联多个日志源时区分不同的日志
• 接收请求时间:用于分析请求的时间分布,从时间维度过滤统计日志,例如:系统承担的QPS、PV等
• 日志级别:INFO、WARN、ERROR、DEBUG
• 线程ID:用于在线问题调试
• 打印日志文件名、函数名、行号:用于在线调试问题
• 请求源的IP和端口:用于跟踪在线请求
• Time spent processing requests:用于统计请求处理时间,发现系统处理瓶颈,分析系统响应时间分布等。
• Requested logid:用于关联不同模块的日志
• 请求的请求和响应:记录服务收到的请求和返回的响应
• 错误信息:对于WARN和ERROR日志,尽可能详细的记录错误信息,方便后续定位问题。
请求的请求数据应尽可能完整地记录下来。一方面有利于后续的日志分析,也有利于复现线上问题,生成回归测试用例。如果响应太大,只记录主要信息,忽略次要信息。比如可以记录返回数据的id,忽略数据的细节。Logid用于关联不同模块的日志。下面将详细介绍Logid。同时,WARN和ERROR日志也要携带logid,将错误信息与对应的请求关联起来。
记录频率
为了方便日志统计,减少日志量,我们规定:
• INFO日志:处理一个请求,打印一个INFO日志,记录请求的信息和返回的响应
• WARN和ERROR日志:根据业务处理逻辑中出现的错误打印相应的日志,没有强制要求
• DEBUG日志:平时关闭DEBUG日志,在线上需要定位问题时动态打开DEBUG日志,问题解决后动态关闭DEBUG日志
每次请求只打印一条INFO日志,方便后续日志统计分析:只需统计INFO日志条数即可计算出服务的QPS,仅分析即可获取请求的所有信息一个日志。DEBUG日志往往会打印很多信息,但在后续的日志分析中不会用到,所以程序正常运行时不需要打印。
易于解析
前面我们提到打印的日志需要易于解析,否则后续的维护成本会很高。这里的“易于解析”除了易于代码解析外,还应该是易于“人眼解析”。毕竟日志的一大用处就是调试线上的问题,而且调试的工作毕竟是人,所以我们要以人为本,让人的眼睛能够快速解析日志,定位问题。当然,easy code parsing 和 human eye parsing 这两个需求往往是冲突的,我们需要在两者之间做一个 trade-off。
无论是便于代码解析还是人眼解析,都需要对日志进行格式化。为了便于人眼解析,我们经常会要求将日志格式化为pretty模式。这里我们选择使用JSON格式化日志,这样既方便代码解析,也便于人眼解析。但是由于一个文件打印多条日志,pretty模式下的json日志(一条记录多行)不利于代码解析。为此,我们要求每个日志(JSON)只打印一行。这也是代码解析的难易程度和人眼解析的难易程度之间的权衡。
前面提到,日志格式的改变可能会导致下游解析工作失败。我们将日志打印成JSON格式后,下游的解析工作就可以轻松实现容错兼容。如果我们需要使用日志的某个字段,只需要判断该字段是否存在,字段类型是否匹配,就可以判断日志格式是否发生了变化。同样,在日志中增加新的字段也不会影响解析工作,我们只需要忽略不熟悉的字段即可。
日志滚动
如果将所有的日志都打印到一个文件中,会导致日志文件过大,不利于后续的日志采集
和删除。为此,我们需要拆分日志。通常有两种拆分方法。一种是根据日志文件的大小进行拆分。当日志大小达到一定阈值时,会打开一个新的文件来记录日志。另一种是按时间拆分,每隔指定时间(每天/每小时)打开一个新文件记录日志。当然,如果日志量很大,也可以结合这两种方式进行拆分,即:先按时间进行拆分,在一段时间内,如果日志量超过了给定的阈值,再进一步按大小拆分。
我们的在线日志是按小时级别分段的,保留最近2天的日志。过期的日志将通过计划的脚本删除。
本地日志与远程日志
上面我们讨论的是在本地打印日志,还有一种方式是直接将日志打印到远程服务器(通过网络传输)。后者需要在服务进程启动的时候创建远程日志服务的链接,当有日志需要打印时,将日志发送到远程服务器(为了不影响在线请求的处理,日志通常是异步发送的)。这个过程通常由底层的日志库进行封装,对上层业务是透明的。远程日志的打印方式存在一些问题。首先,它将在线服务与远程日志服务耦合起来。如果访问远程服务出现问题,可能会影响在线服务的正常运行。其次,如果与远程服务的通信出现问题,日志库还需要缓存未发送成功的日志。为了减少缓存的日志对内存的影响,我们往往需要将日志缓存在磁盘上,甚至在打印完后还要将日志同时打印到本地和远程,这样就增加了设计的复杂度日志库(见下文,实现日志采集需要面对的问题)。
为此,我们选择将日志直接打印到本地,将日志打印和日志采集
解耦,使用专门的工具来采集
日志。
" />
日志采集
日志打印到磁盘后,我们不能直接在在线服务所在的节点上操作日志。有几个考虑因素。第一,直接操作在线节点是非常危险的。如果我们在处理日志时误修改或删除在线文件,将会给在线服务带来灾难性的破坏。问题; 其次,在线分析日志时,往往会占用机器的CPU、内存、磁盘等资源,会挤压在线服务的资源,带来不确定性;最后,现在很多业务都是微服务的,一个请求往往由多个微服务处理,每个微服务往往有多个实例进行冗余。因此,当系统出现问题时,我们很难快速定位到问题服务所在的实例,这就导致我们要登录很多节点去查看日志,这往往是一件非常头疼的事情。为此,我们需要采集
在线服务节点的日志。
如何优雅地采集
日志
很多场景需要对日志进行实时分析,所以日志采集
也需要实时进行。日志的实时采集看似很简单,只要我们不断检查日志是否有更新(类似于Unix的tail命令),然后将更新后的数据通过网络发送即可。但实际上有很多工程细节需要考虑。
• 因为是在线节点日志的采集
,所以最重要的一点是日志采集
不要影响在线服务,只使用较少的资源来采集
日志
• 日志采集需要具备高性能,日志能够尽快传输,保证日志的有效性
• 当采集服务出错异常退出时,如何容错,如何恢复日志传输?
• 如何保证消息传输的正确性:日志的顺序是否有保证?日志是否丢失或重复(至少一次、最多一次、恰好一次)?
• 日志滚动有多种实现方式,每一种都需要优雅地处理
• 如果下游出现问题(来不及处理,甚至直接宕机),无法接收到请求,如何处理?
日志采集
应该使用尽可能少的资源并尽可能高效地传输日志。这两个目标之间存在一定的冲突,需要在技术选择上做出取舍。为此,很多开源方案开始使用一些高性能的编程语言来实现日志采集
。比如用Go语言写的filebeat来代替Java写的Logstash,用C语言写的Fluent Bit来代替Ruby写的Fluentd。我们可以通过多个线程(进程)来传输日志,但是这样可能会增加CPU的压力。为了降低CPU占用率,我们在检测日志文件是否有更新的时候不要过于激进,但是过于保守的检测可能会造成日志传输延迟。为了提高传输效率,减少带宽,我们可以将多条日志批量合并,压缩后传输。但批处理后可能会影响日志传输的有效性,压缩可能会造成CPU消耗。
为了保证日志发生错误后能够继续传输,我们需要为日志传输进度建立一个检查点,并持久化该检查点,这样日志才不会丢失。如果每次传输日志时都更新check point并刷入磁盘,会对磁盘造成压力,影响性能。但是,如果两个校验点之间刷新校验点的时间比较长,在这段时间内出现错误后,恢复日志传输时可能会出现日志重复。为了保证日志的顺序,我们可能还需要对日志进行编号,考虑在多线程/进程传输时如何保证编号的顺序。
另外,在日志滚动的时候,有的计划是写一个新的文件,有的计划是继续写原来的文件,只是周期性地把原来的文件mv成另一个名字的文件。当下游忙于处理时,上游日志采集模块是否可以主动发现,或者下游模块需要通知上游,然后降低传输速率。如果下游宕机,日志采集模块也需要在适当的时间间隔重试并报警。
以上问题都是日志采集需要考虑的,所以要实现一个看似简单的日志采集,实际上会涉及到很多工程决策。幸运的是,有许多开源解决方案可供选择。其中,成熟的开源解决方案往往会配置上述问题,让用户根据自己的业务场景和资源情况优化日志采集。
选择
我们调查了多个开源日志采集
工具,例如 Logstash、Filebeat、Fluentd、Fluentd Bit 和 Flume。对于Logstash,它的性能不是很好,而且很重,所以我们先排除它。Flume是基于java的,比较重。同时它的社区活跃度不是很高,所以我们不考虑。Fluentd是基于Ruby的,性能不如Fluent Bit,所以不考虑。Fluent Bit 和 Filebeat 都是比较轻量级的日志采集
工具。两者都具有良好的社区活动、性能和成熟度。它们还支持日志输入和输出到各种中间件/数据库。
考虑到我们使用Elastic Stack的相关组件比较多,包括Elastic Search、Kibana等,而Filebeat也是Elastic Stack的一部分,所以最终选择了Filebeat作为日志采集工具。Filebeat也可以实现上面提到的“优雅采集日志”的各个技术点并进行配置。限于篇幅,这里不再展开。详见相关文件()。
日志去哪儿了
从各个服务节点采集日志后,应该传输到哪里?通常,根据使用情况,我们经常需要将日志传输到不同的下游模块。比如我们需要对日志进行批量离线分析,我们可以将它们传输到HDFS,如果我们需要检索日志,我们可以将它们传输到Elastic Search,等等。但是我们不应该让服务节点直接同步日志文件给多个下游模块,因为这样会带来很多问题。首先,这样会抢占在线服务更多的资源(一条日志被多个拉日志进程多次读取和传输)。其次,我们在使用日志之前,通常需要对日志进行ETL操作。如果日志直接传输到多个下游,不同的下游模块执行不同的ETL,会导致数据不一致。最后,多个日志采集进程独立采集,会给日志管理带来混乱。
为此,我们将日志采集
并传输到Kafka,然后通过流式计算对日志进行统一的ETL,下游模块从Kafka订阅日志。这样保证了在线服务节点只传输一条日志,降低了在线服务节点的资源压力。另一方面,日志也可以统一维护,使用统一的规则进行清理,保证数据在下游各个模块中的一致性。
总结
在线日志是非常重要的资产,可以帮助我们定位在线问题,监控在线服务,分析用户行为,生成报告,帮助我们优化系统。本文从日志打印和日志采集两个方面介绍阅文搜索团队的一些实践经验。在后续的文章中,我们将从日志聚合和日志分析工具两个方面进一步探讨。
关于作者
刘思伟
目前就职于阅文集团智能业务中心,智能业务后台技术负责人
总结:人人都能学会,不写代码的爬虫概述篇
爬虫这个词并不陌生,但在很多人眼里,却是技术爱好者的专属。其实,爬虫是一项人人都可以掌握的技能。
你可能有这些需求:
如何从公开信息中寻找赚钱点子?
想采集
一大堆表情包,又想躺着让电脑自动采集
下载。
在58同城一个一个找同城的租房信息太麻烦了,要电脑帮我统计一下。
如何一键统计知乎大V的所有文章标题。
……等等,这些爬行动物可以轻松帮你搞定。
爬行动物究竟能做什么?简而言之,它可以捕获您可以看到的所有信息。
今天不写代码,只用一个爬虫软件——优采云
collector,可以满足我们所有的需求。
因为优采云
采集器是付费软件,我们可以使用它的变体——优采云
。
我也准备了免费版的优采云
软件。公众号回复[优采云
]获取软件,下载到本地,解压即可。
优采云
的界面是这样的,看似普通,但是用起来就能体会到它强大的功能。
" />
在进行爬取之前,我们首先要了解一些网页的基础知识。
一个网站由多个网页组成,网页一般会展示我们想要的信息。
我们以便图网为例,
在浏览器中打开网站,点击键盘上的F12,会看到这样的界面,第一个宇航员图片的代码对应右边区域。
代码中有图片链接,下面所有图片对应一个区域。我们所要做的就是复制这些图片链接并保存。
当然,你也可以不吃不喝不睡,手动复制,优采云
会帮我们完成。
如果你打开58同城、知乎等网站,你会发现其他网站也有这种结构。
我们只需给 优采云
URL,告诉它我们想要的信息,它就会为我们完成工作。
优采云
中要处理三个任务,采集
URL-采集
数据-发布数据。
一个网站有很多网页,我们找到采集
url的规律,写好规则后,就可以得到该网站的所有网页。这是 优采云
的第一份工作——采集
URL。
拿到URL后,我们会进入各个网页,然后我们会去寻找数据的规则,写规则,对应优采云
的第二个任务——采集
数据。
数据获取成功后,我们可以将数据保存到电脑上的Excel,远程数据库,或者直接发布到wordpress网站。
接下来我们要学习的其实是如何编写采集规则。通过学习,人人都可以成为爬虫。
" />
想要找到好的副业,最重要的还是要提升自己的核心竞争力,不断学习新的技能来拓宽自己的护城河。
无论是你的工作还是副业,需要定期处理的数据都可以交给爬虫。
-结尾-
喜欢就关注吧
关于作者:
我是田同学,一名程序员。我从程序员开始,但不局限于代码,分享个人成长&赚钱
欢迎加我微信,交个朋友
以前的文章
你点的每一个赞,我都当成一个赞