
不忘初心 方得始终
背景与趋势这部分首先介绍了数字化下系统运维对工程师挑战(包括一专多能、需要更多观测分析),然后介绍了可观测的定义(更早、更全面数据的采集与观测),再到可观测系统的挑战(各种监控系统并存孤立以及门槛高),并总结了传统告警运维系统的六大痛点。详细内容可以参考这篇的背景部分,这里不再赘述。被观测系统的复杂性:各种孤立并存的监控系统:传统监控运维系统的痛点:方案基础本节介绍了整体方案的基础前提:一个统一的可观测平台。以便在这个基础上再去解决告警风暴、监控质量差、通知单一重复等问题。典型的可观测平台解决方案基于上一节的背景和趋势介绍,我们可以看到一个典型的可观测平台应该具备这样的特性:对包括日志、时序、链路、跟踪和事件的统一的存储、处理、分析、展示以及响应的基础中台能力。支撑上层业务方(开发运营、监控、安全、用户运营)的观测分析需求(与定制能力)。同时可以对接上下游系统的生态支持。数据统一接入与查询分析这样的可观测平台,具备将各种异构的数据(日志、指标、时序、事件等)进行统一存储和管理的能力:基于统一的存储,我们可以构建统一的查询和分析语法,从而一套语法适配不同的数据,并且让不同的数据之间进行联合查询也变成了可能。如图,可以以标准SQL为基础,进行了部分DSL扩展和 SQL 函数扩展,并融合了PromQL,从而让不同类型的数据查询和分析变得统一。例如下面的例子:我们可以通过标准 SQL 语句对日志进行查询分析通过 PromQL 扩展的 SQL 函数对指标数据进行分析再通过嵌套查询,对指标数据的分析结果进行再聚合还可以进一步通过机器学习函数,给查询和分析赋予 AI 的能力告警统一监测与管理通知在云原生可观测性平台上我们就提供了一套现代化的智能运维告警系统。新版告警提供对日志、时序等各类数据的告警监控,亦可接受三方告警,对告警进行降噪、事件管理、通知管理等可以看到新版告警由4个模块组成:告警监控:对存储在平台中的各类数据进行监控,产生高质量的告警。开放告警:接收平台外的现有十几种监控系统的告警(例如Zabbix、Grafana告警、Promethues告警等)告警管理:对上述两种方式的告警进行集中管理:包括路由合并、降噪抑制、事务管理等。通知管理:对告警进行人性化通知多渠道通知,包括动态分派、告警升级、节假日与值班组等。统一的可观测态势大盘在此基础上我们就可以得到基于业务的统一的告警态势大盘:智能监控有了一个可观测基础平台,现代化告警系统就可以从两个环节对告警降噪进行治理:首先是告警监控环节,也就是告警产生的源头进行降噪。其次是告警管理环节,也就是对来源前一个环节(告警监控+开放告警接收的三方告警),进行全局基于业务的降噪。传统告警监控的困难和挑战这里我们详细解读一下传统告警监控的挑战:监控对象和监控指标急剧增加。如果要全方位的覆盖这些监控对象和指标,需要配置大量的监控规则,并且它们的阈值也可能是各不相同的,因此会有很大的工作量。监控规则无法自适应:基于人为定义的阈值,很大程度上依赖于人的经验,随着系统的演化和业务的发展,这些规则往往不能很好地适应,由此不可避免地导致漏报、误报等问题。无法做到数据的自适应,因此需要人为介入,不断调整阈值。例如下图:上面是一个指标,有规则性的毛刺。如果通过阈值来判断是否需要告警,当一个毛刺点异常的时候,可能由于不满足阈值,导致告警漏报。下面是另一个指标,可能随着系统的进化,新版本发布之后,该指标的值会发生一个陡增。此时如果是固定阈值告警的话,会将陡增之后的所有数据都认为是异常点,导致告警频繁触发。此时需要人为介入去调整阈值。监控规则泛化能力弱:不同的业务、甚至同一业务的不同版本,指标的规律性、阈值都有可能是不同的。因此我们需要为不同的业务、不同的版本去做监控规则的适配。例如下图,虽然两个指标整体上有着比较相似的波动规律,但是由于它们的取值范围、以及局部的抖动情况会有差异,因此需要分别去做监控。智能监控1:智能巡检基于上述问题,智能巡检可以一定程度的优化,其具备以下几个优势:智能前置:现在有很多系统是在告警触发后,进行智能的管理,但是这无法避免告警误报、漏报等问题。智能巡检可以将 AI 的能力前置到监控层,从而在源头上避免潜在的告警问题,挖掘出真正有效的数据价值。监控自适应:可以基于历史数据自动学习和进化,进行动态的阈值判断,从而让告警更加精准。另外对数据的学习也是实时的,可以更加快速地发现异常问题。动态反馈:除了自动学习之外,还可以通过用户的反馈,对告警进行确认或者误报标记,将 AI 能力与人的经验相结合,相辅相成,进一步完善模型,减少误报。在一些数据波动比较大,指标没有固定阈值的场景下(例如用户访问量、外卖订单量等),智能巡检的优势可以得到很好的体现。例如下图,指标本身呈现出周期性地波动,假如一个新版本上线了之后,由于bug导致网络流量异常抖动。如果基于固定阈值来判断,此时处于指标值的上下界范围内,就很难发现问题;但是基于智能巡检,就可以很容易地判定这是一个异常点。实现思路智能巡检的基本思路如下:我们采用无监督学习算法,自动识别实体的数据特征,根据数据特征选取不同的算法组合,针对数据流实时建模,完成异常任务检测。并根据用户的打标信息(对告警进行确认或者误报反馈),训练监督模型,对算法进行不断优化,从而提高监控的准确率。目前异常检测我们使用了两种算法,它们的比较如下:流式图算法流式分解算法适用场景适用于一般性时间序列的异常检测场景,包括:机器级别的监控指标的异常巡检,例如CPU占用率、内存利用率、硬盘读写速率等。业务指标的异常巡检,例如QPS、流量、成功率、延时等。黄金指标的异常巡检。适用于对具有周期性的数据序列进行巡检,且要求数据的周期性较为明显。例如游戏的访问量、客户的订单量。相关论文Time-Series Event Prediction with Evolutionary State GraphRobustSTL: A Robust Seasonal-Trend Decomposition Algorithm for Long Time Series智能监控2:增强型规则引擎监控对于时序类异常检测,智能巡检可以比较好的解决。但对于以下类型的监控,还需要使用增强型规则引擎来减少噪音:经典类监控:确定性的经典规则(例如基于关键字异常的监控)、需要支持多目标、自动恢复等经典功能的监控(例如某台机器宕机监控以及上线的恢复通知),并在此基础上使用黑白名单、机器学习算法降噪的监控。业务类监控:基于业务的定制化程度高的监控,例如大量异常非常见客户国家的访问,某些非VIP客户的购买异常记录等。安全类监控:基于安全规则的监控(例如海量数据库拖库监控)、配置合规审计监控(例如创建了公网IP的ECS网络主机实例需要告警)等。使用基于统一的查询分析引擎以及扩展的告警引擎(包括多源协同、数据感知和灵活规则等),可以很好的覆盖这类场景并降低噪音:智能告警管理智能告警管理从以下出发点进行降噪:智能监控环节可以较好的降低噪音,但来源于三方的告警一般是未经过这样的处理,因此需要在智能告警管理环节进行降噪。智能告警监控环节在宏观方面还是可能也产生较多告警(例如多个业务并行),告警智能管理也可以进一步合并或协同降噪。全局角度,客户还需要借助智能管理完成在分派(值班)、升级、通知渠道、节假日感知层面的进一步降噪优化。我们可以通过告警智能管理来解决上述问题,如图所示:智能降噪机制告警智能降噪包含以下几种机制:智能合并可以有效的将重复的大量的告警合并,减少告警风暴。例如某服务的2个主机从20:00和20:01分别每分钟不停触发高CPU告警,收到2小时125条告警,经过降噪仅发送4次。关联抑制可以有效的将许多关联的告警自动抑制,减少告警风暴,例如某K8S集群发生OOM后,自动发送一条告警,而该集群上海量的上层应用告警会被自动抑制:灵活静默可以根据配置在特定时间、节假日等屏蔽掉特定的告警通知,减少告警风暴的干扰:动态分派动态分派保证了有效触达外,也可以通过以下方式降噪:多渠道:支持短信、语音、邮件、钉钉、企业微信、飞书、Slack等多种通知渠道,同时还支持通过自定义 Webhook 进行扩展。可以根据告警的严重度和特定场景动态以合适的渠道通知到人。动态通知:可以根据告警属性动态分派通知,将正确的告警在正确时间给正确的人,有效减少无效通知。例如:测试环境的告警,通过短信通知到张三,并且只在工作时间通知;而生产环境的告警,通过电话通知到张三和李四,并且无论何时,都要进行通知。通知升级:长时间未解决的告警要进行升级,而不是持续的无效重复通知。例如某告警触发后,通过短信通知到了某员工,但是该问题长时间未被处理,导致告警一直没有恢复,此时需要通知升级,通过语音的方式通知到上一层负责的领导。值班管理许多运维团队由多人负责告警的处理,可以通过值班管理按需分派通知,减少对非值班的同学的告警噪音影响。值班管理在常规功能外提供了灵活的机制进一步提高降噪效果:节假日感知(例如法定节假日与自定义感知换班)工作时间感知(例如自定义上班时间才换班)代班与以上或细腻度支持(例如特定时间段的换班)总结本议题介绍了在一个现代化的可观测平台上告警降噪的整体方案与技术实践,平台提供了包括日志、时序、链路、事件等统一的数据接入与存储、提供了统一的查询分析语法,并在此基础上分别从智能告警监控(包括智能巡检与加强引擎)与智能告警管理(包括智能降噪、动态分派与值班管理等)环节展开介绍了如何解决传统告警的告警风暴、监控质量低、不人性化等典型问题。参考SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/slsSLS告警文档:https://help.aliyun.com/document_detail/209951.htmlSLS告警学习路径:https://help.aliyun.com/learn/markets/aliyuningpath/log/alert?spm=a2c4g.11186623.0.0.d0342e58Vd1gnOSLS智能巡检:https://help.aliyun.com/document_detail/253411.htmlPPT下载: https://github.com/wjo1212顺便打个广告,欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持:
背景相信大家已经知道了Log4J的核弹级漏洞,影响了非常多的Java 类产品:Apache Log4j 2.x < 2.15.0-rc2;可能的受影响应用及组件(包括但不限于)如下:Apache SolrApache FlinkApache DruidApache Struts2srping-boot-strater-log4j2ElasticsearchflumedubboRedislogstashkafka更多组件可参考:https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/usages?p=1该漏洞利用Log4j提供的lookup功能允许开发者通过一些协议去读取相应环境中的配置。但在实现的过程中,并未对输入进行严格的判断,从而造成漏洞的发生。简单来说,就是在使用Log4j打印日志时,如果发现日志内容中包含关键词 ${,那么这个里面包含的内容会当做变量来进行替换,导致攻击者可以任意执行命令。详细漏洞披露可查看: https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3201攻击检测通过使用阿里云SLS服务,只需两步即可完成攻击检测:1. 将Java程序日志接入SLS首先需要将业务日志接入SLS,如果已经接入了的可跳过。SLS支持非常便捷的接入方式,这里推荐使用文件采集Java程序的日志,具体接入方法可以参考:数据采集概述:https://help.aliyun.com/document_detail/28981.html使用极简模式采集日志:https://help.aliyun.com/document_detail/137903.html在日志接入后,我们就可以在SLS控制台配置关键词告警。2. 配置关键字监控该漏洞被利用时会产生相应的日志,通过检测以下关键字,即可识别:"jndi:ldap://" or "jndi:rmi" or "javax.naming.CommunicationException" or "javax.naming.NamingException: problem generating object using object factory" or "Error looking up JNDI resource"然后点击查询/分析,(如果有攻击发生,会如下图):再点击右上角的“另存为告警 -> 新版告警”:配置告警规则如下:通知里可以配置语音、钉钉等渠道,如下图所示:如果日志中有关键字出现,则会在发送语音和钉钉通知。修复建议目前漏洞 POC 已被公开,官方已发布安全版本(RC2),建议使用 Java 开发语言的系统尽快确认是否使用 Apache Log4j 2 插件。禁止使用 log4j 服务器外连,尽快升级 Apache Log4j2 到最新的 log4j-2.15.0-rc2 版本,地址: https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2总结该漏洞影响极大,修复本身也比较花费过程,许多服务都发布公告需要一段时间,在这之前,强烈推荐使用阿里云SLS对该漏洞进行预警。进一步参考阿里云SLS(日志服务):https://www.aliyun.com/product/slsSLS日志采集文档:https://help.aliyun.com/document_detail/28981.htmlSLS告警文档:https://help.aliyun.com/document_detail/209951.html欢迎扫群加入阿里云-日志服务(SLS)技术交流,获得第一手资料与支持
1. 背景与趋势本节介绍可观测性平台与低代码技术的相应背景和相关发展趋势。1.1. 数字化对工程师的挑战随着云计算与云原生技术的大量应用,研发运维类工程师总体上花费在部署环境上的时间相对其他工作有所减少。而数字化时代的趋势下,为了更好的减少业务损失或发展业务,工程师需要对系统进行观测分析的时间也逐步增加。从软件研发的生命周期看,有大量的工作需要做,不同人员参与的协同会大大降低响应速度,越来越多的公司要求一专多能。开发、测试、运维融合逐步成为趋势,开发人员逐步开始承担测试的工作、部分的运维甚至运营的工作。1.2. 趋势1:工作模式的统一Ops(运营)工作中很重要一环就是观测与分析,不同角色关注不同的观测源,进行相应的处理动作,例如开发运营维护(DevOps)关注稳定性、安全运营(SecOps)关注风险控制、商务运营(BizOps)关注业务健康增长等,但是他们的工作模式基本一致,可以统一为两个阶段:对观测源进行数据处理(数据采集、数据存储、分析加工)到分析判断(信息展示与响应动作)。1.3. 趋势2:数据存储的统一不同运营角色的数据源都需要采集与存储,而他们有很大程度上的重叠,例如安全运营与商务运营人员关注的用户操作与行为日志,同样也是开发运营维护人员所需要关注的。这就好比智能汽车的发展一样,从一开始的孤立的各个数据监测系统(行车记录仪、车载录音、胎压监测、倒车雷达、ETC等)到现在逐步趋于统一化:统一采集、存储与分析的总线系统实现了接口统一、数据互通互联。1.4. 趋势3:算力带来实时洞察传统监控一般以一个白盒方式监控系统,专注发现核心指标异常,例如500错误,客户订单成功率等。一般这种问题发生时,准取性极高(例如大量500错误,大量订单失败,一定表示SLA有问题),一般也都比较严重。因为是黑盒,进一步排错和修复时间和成本极大,往往给开发运维人员带来极大压力。根据海恩法则(Heinrich's Law),每一起严重事故背后,必然有29次轻微事故和300起未遂先兆以及1000起事故隐患。如果提前处理那些不那么严重的问题、先兆或者隐患,其实是可以避免后续的严重事故的,也就避免了其带来的巨大压力和损失。可观测性是对传统监控的升级,其要求进行白盒化监控,对各种可能的隐患、先兆、不严重问题进行监测、跟踪处理。且不再只是在发布后,而是在开发、测试阶段就进行。因此对比两者,可以发现,传统监控主要由SRE人员从系统外部进行监控,关注指标,发现问题(Know What);而可观测性由DevOps人员从系统内部进行监控,关注指标、日志和跟踪等数据各种数据,发现问题并挖掘原因(Know Why)。可观测性需要采集更多的数据,对数据进行更多的预处理,而现在算力的增强(更丰富的机器资源,加上更好的大规模数据处理的能力),让实时观测落地变得更容易与流行。1.5. 趋势4:业务与技术越成熟越驱动低(无)代码技术/平台落地在疫情下大量轻应用需求带动下,低代码(Low Code)或无代码(No Code)技术以及其衍生出的低代码应用平台(LCAP:Low Code Applicaiton Platform)开始流行。其核心理念就是使用低代码甚至无代码就可以完成某项之前需要代码才能完成的工作,除了常规应用软件开发,也可应用于数据分析、流程管理、工业领域等复杂业务系统的建设。从实际落地角度看,低(无)代码技术的应用与技术成熟度、业务模式成熟度有很大关系,例如云计算简化了软件架构、前端组件化让多端开发更容易,从而催生了许多通用轻应用领域的低(无)代码应用。而某些特定行业的应用或典型数据流程审批系统的业务模式也越来越成熟,使得低(无)代码也有了相应的用武之地。新一代可观测平台,因为在技术挑战和业务模式上还在逐步完善中,使用LCAP协同开发还无成熟落地,但低(无)代码技术已经开始被应用辅助提升用户体验。1.6. 典型的可观测平台解决方案基于前述一些趋势,我们可以看到一个典型的可观测平台应该具备这样的特性:对包括日志、时序、链路、跟踪和事件的统一的存储、处理、分析、展示以及响应的基础中台能力。支撑上层业务方(开发运营、监控、安全、用户运营)的观测分析需求(与定制能力)。同时可以对接上下游系统的生态支持。1.7. 开发运维智能化、无代码化发展趋势可观测平台也是智能运维平台(AIOps)的载体,可以看到在算力算法和低(无)代码技术下逐步提升效率,让机器代替部分人力,将智能自动化水平逐步提高。以下是各个层次的智能化、低(无)代码化发展水平:2. 各环节的场景与挑战下面我们从可观测平台支持的各个环节介绍低(无)代码技术的应用场景与相应挑战。2.1. 可观测平台下配置部署时的低(无)代码层次运营模式下的各个环节使用低(无)代码技术可以分成三个层次:纯代码、低代码到无代码。纯代码层次下,各个环节都存一定的编码任务,例如采集时需要在代码中写入数据以便被采集,或集成方需要编写程序来拉取特定数据;存储下需要部署加工程序或在流计算框架下编写代码完成存储预处理工作;分析阶段需要调用不同接口分析并关联协同;展示阶段需要在可视化系统中编写提取数据并展示价值信息;动作阶段需要集成代码与三方系统交互,完成特定流程逻辑。低代码层次下,做到了组件化封装大部分编码工作,避免代码编写或通过DSL方式简化编码工作。无代码层次下,大大简化或自动化了编码工作,通过内置组件、图形化操控、智能提示等完成各环节工作。2.2. 数据采集的低(无)代码场景数据采集需要解决各类数据源的数据摄入,这一阶段的低(无)代码采集的主要场景如图,可简单分为:以低(无)代码方式接入任意源的任意类型数据。不修改程序以无/低代码接入程序中相关数据。大部分场景行业已经有一些不错的解决方案,但依然存在的一些挑战,典型的如下:无代码采集技术成熟度的问题,例如Java代码侵入、JMX采集、JS在终端浏览器注入(RUM)以及eBPF技术等,如何包装适配性,以及在实时密集采集时,控制对目标系统的稳定性与性能影响,以便在核心生产环境中大规模部署。非结构化的日志的格式解析,目前典型使用正则等方式,如何保持灵活性由减低门槛。如何降低编排复杂度,让采集变得更加智能,例如云资产如何自动发现与适配对应云产品日志等。2.3. 数据存储加工的低(无)代码场景数据在入库存储前需要进行规整加工,典型场景如图,支持以低(无)代码方式对无结构、半结构化数据进行各种形式的分派、加工、归并与富化等。目前典型做法是使用ETL工具或流计算等编码形式完成,但如何以低(无)代码实现的主要挑战是:如何灵活适配以上各种场景,又具备一定扩展性?架构方面又如何实现自动化扩容与缩容?2.4. 数据分析的低(无)代码场景如背景中介绍的各种运营模式下,分析阶段的场景主要是对数据进行各类分析计算(或动态加工),对包括时序、日志、Trace、事件等数据进行查询、聚集、转换、关联、智能算法等低(无)代码的计算命令。该场景下的主要挑战包括:不同存储的查询分析语法各不相同,如何降低使用门槛?以开源为例,有ES(Lucence、DSL)、 Prometheus (PromQL)、OpenTelemetry(API)、Presto(SQL)、ClickHouse(类SQL)等。多种系统的数据之间如何实现低(无)代码的关联协同?2.5. 数据展示的低(无)代码场景数据展示阶段的低(无)代码场景比较直观,不需要写代码就可以构建包括各类图表的可视化大盘,包括各类图表;也包括写很少代码就可以定制支持其他交互分析场景的可视化模块。该阶段目前有很多成熟的方案已经可以做到,但依然存在一些挑战,包括:数据与可视化之间如何进一步简化关联?多端可视化如何自动化适配?(目前LCAP在软件开发阶段已经较好的解决了Form场景,但数据图表展示场景依然有一定配置复杂度)。仪表盘外的可视化定制如何支持低(无)代码扩展?2.6. 操作响应的低(无)代码场景操作响应阶段的场景可以理解为从数据中挖掘更重要的信息进行相应的动作,典型的可观测平台下是告警事件的监测、管理以及行动(如图)。支持的场景大部分涉及比较复杂的告警监测与恢复(包括智能机器学习巡检)、智能降噪(包括抑制、静默、事务合并等)。而行动响应步骤的场景又有所不同:开发运维(DevOps)时强调通知的触达与管理,包括了分派、故障升级、值班敏感、各类通知渠道调度、通知内容编排等,智能运维(AIOos)时强调告警处理,包括了上下文富化、关联、人工标注、根因分析、以及止血自动化等。安全运营(SecOps)时强调安全编排(SOAR)的支持,包括与威胁情报联动、故障流程处理、二次审批与风控介入自动化等。主要的挑战是:如何以低(无)代码方式满足上诉场景,并支持灵活扩展?如何在可观测平台下以统一模式方式支持各类场景?3. 最佳实践与Python技术的应用采集阶段的低代码实践比较多与生态建设以及各类具体技术相关,这里针对除采集阶段外的各个阶段的低(无)代码技术实践进行简单介绍与探讨。3.1. 数据存储加工的低(无)代码实践这里介绍一种PaaS形态的数据加工的实践方式,架构层面如下:采用队列如Kafka、阿里云LogHub、AWS Kinesis等对数据进行流式读取消费。调度系统基于K8S构建,对数据流量与计算消耗,动态缩容扩容。而低(无)代码的方式以Python子集语法方式提供用户侧的输入与实际逻辑的映射。这种方法在具体实践时,有一些特色如下:提供了200+封装算子,与Flink、Python原生、ESLogstash的Ruby方案对比,代码更少。低于20行代码即可满足大部分用户的加工需求。以子集方式提供保证了低门槛与灵活性,也进一步保护了计算环境,同时也支持通过代码优化裁剪、部分重写等方式进行进一步性能优化可能。如下是一个对数据进行字段设置、丢弃、判断处理场景的样例:3.2. 数据分析的低(无)代码实践针对对应环节描述的挑战,该阶段的实践方案采用如下架构:一套存储,多种索引方式,支撑随机查询(日志)、列存(日志分析)、行存(时序分析)等场景。以一套统一语法方式,SQL为中心覆盖所有分析能力(Query、PromQL均作为SQL一部分)其低(无)代码的特点体现在:•统一使用SQL = Search + PromQL + SQL92作为平台核心接口。•提供Query Builder可视化功能,进一步降低分析语法构建的门槛。例如以下查询分析语句展示了,如何使用一套SQL完成对日志、时序、机器学习算法等一起使用并协同的方式:3.3. 数据展示的低(无)代码实践数据展示针对前述挑战,低(无)代码的主要实践是:依托统一查询分析接口语法映射数据,这就解决了数据协同的问题。提供开箱机用的各种图表(如图),在画布基础上支持所见即所得(这种方式目前在行业方案中也比较普遍)。、3.4. 操作响应的低(无)代码实践针对前述各种复杂业务的操作响应的需求与挑战,尤其是灵活性方面,在在架构上将复杂操作(对告警的监控、管控、通知等)以Python子集包装,后台引擎使用此输入执行,逻辑上支持相关语法安全检查与实际逻辑映射。在低(无)代码场景(例如告警监控、管理逻辑)落地时,提供可视化界面(GUI)交互配置编排,其并自动映射为Python子集。如下是一个告警通知逻辑编排的例子:通知展示模板也需要用户自行编排,可以使用Python子集作为低代码灵活配置(当然也可以辅以可视化界面进行所见即所得的构建),如图:在生态集成时,需要API交互操作时,上述场景也都可以通用以低代码(如SQL、Python语法子集)方式接受输入。4. 总结我们总体上介绍了可观测平台下低代码技术技术实践的背景趋势、各环节的应用场景、挑战,以及相应的一些最佳实践的做法。可以看到,新时代可观测平台已经在采用低(无)代码技术来优化各个环节的体验与工程效率,并在持续不断完善中。参考什么是可观测性:https://www.sumologic.com/glossary/observability/ 与 https://www.splunk.com/en_us/data-insider/what-is-observability.html低代码技术定义:https://kissflow.com/low-code/low-code-overview/低代码完全指南:https://retool.com/blog/what-is-low-code/录播视频:https://www.bilibili.com/video/BV1pL4y1q7KQPPT下载: https://github.com/wjo1212顺便打个广告,欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持:SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/sls
日志服务告警是一站式的告警监控、降噪、事务管理、通知分派的智能运维平台。新发布日志服务告警学习路径图,汇集目前50+最佳实践的文档、文章和视频 (持续更新),为您展示告警功能的操作指引文档,帮助您快速了解日志服务告警。视频与文档结合,全方位提升您的产品使用及文档阅读体验。https://help.aliyun.com/learn/markets/aliyuningpath/log/alert欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持(含直播培训)后续系列直播与培训视频会同步到B站,敬请留意
人体的8种感觉我们都知道人体神经系统非常复杂,由各个末梢信号后经由脊髓、大脑中枢处理后获得如下众所周知的5种感觉:视觉——眼睛、听觉——耳朵、触觉——皮肤、嗅觉——鼻、味觉——口除此之外,人体其实还有另外3种重要的感觉,分别是:内脏觉——内脏、本体觉——关节肌肉、前庭觉——前庭神经核其中内脏觉获取来内脏壁信号,产生内脏相关的感觉例如饥饿、饱腹、尿急等。本体觉获取关节肌肉等信号,感知当前身体处于一个什么样的姿势和运动状态。前庭觉则通过各方信号,获得平衡方向感,并过滤信号以便集中精神。不用说,任何一个感觉出了问题,都会造成非常严重的后果。人们健康快乐的生活离不开这些神经系统的正常工作。企业IT系统的感觉系统企业IT系统同样存在各种感觉——对计算、网络、存储、安全、管控等系统的监控运维。数字化信息化的今天,企业组织能否健康、稳定、持续的发展也离不开其IT系统的神经系统——监控运维系统(更宽泛的说法叫可观测性系统)的正常工作。但不同于人类物种经过几百万年的统一演化为基本一致性,企业IT系统的告警监控系统(可观测性系统),还存在非常大的多样性,例如如下使用开源方式对容器化部署形态的IT系统的监控存在多种方案组成:传统告警系统大量碎片化、无体系的神经孤岛,带来了非常多的痛点,包括重复建设、监控智能差、告警风暴、触发不人性化、无法闭环等。SLS告警为企业IT系统提供智能神经中枢作为新一代的云原生可观测平台,SLS支持多种数据源的一体化接入、一站式的存储、加工、分析、可视化、监控、投递、三方对接,为企业IT系统的使用者(包括开发运维、监控人员、商务、安全运营人员等)提供了最快、最高效的观测体验。作为SLS的一个子系统,SLS告警为IT系统重现构建了智能神经中枢。目前已经大量被阿里云平台的企业用户使用,每天从海量的数据中监控识别产生告警、管理处理并通知与响应,可证明的灵活适配与稳定可靠性。使用SLS告警可以给企业IT系统的神经中枢提升易用弹性、可靠性以及功能灵活性,并降低成本、告警噪音以及减少损失。下面从几个侧面了解一下SLS告警是如何为企业IT系统提供智能的神经中枢的。接入三方告警——兼听则明类似于人体的多种感觉从多方获取信号设置告警(例如听到天气预报说明天会下大雨,而出门带好雨伞),SLS告警也支持从其他多种流行监控系统中直接接受告警,并进行智能处理与响应。支持的三方系统包括Promethues告警、Grafana告警、Zabbix告警等。统一信号分析——智能告警监控SLS作为可观测平台,已经接收了IT系统的大量信号(日志、时序、跟踪等),可以直接使用SLS告警从中挖掘出大量信息(告警),SLS提供了一套查询分析语法,全面覆盖现在流行的时序、分析型语法,兼顾强大灵活与易上手。不仅如此,还支持多种数据的协同关联,就像闭上眼睛,拿一个水果,也可以通过触觉、嗅觉、听觉等判断出这是一个杯子、还是一个苹果等。智慧信号处理——ML算法就像人的脊髓、大脑等会自动参与信号处理得到更高级的感觉信息一样,从海量的数据中产生信息包括告警,离不开机器学习的支持,SLS提供多种丰富流行与顶级算法,更精准高效的产生告警。千里眼——全局监控人的感觉系统是有距离限制的,触觉限制了手能够到的地方,视觉可达数公里。但IT企业系统可能分布在各个国家、城市系统或各个隔离的账号体系中,SLS告警支持跨库、跨地域、跨账号的对数据进行远程监控,就像千里眼一样。知识库——内置规则库人类善于学习积累来扩展感觉系统,一个小朋友在摸过看过或碰触装了热水的玻璃杯后会记住这个感觉,下次再看到冒着热水的玻璃杯就会得到一样的信息。SLS告警的内置告警规则库,提供数百个这样的知识,开箱机用。告警降噪——IT系统的前庭觉一个男生在玩《王者荣耀》时,或一个新青年在看《光荣与梦想》时,通常会自动忽略周围的大部分信息,这其实是前庭觉在起作用。在企业监控告警中,也需要通用的机制:在收到一个主机宕机的严重告警时,IT运维同学加紧迁移系统中,希望自动忽略该主机上一连串的其他告警的。亦或者在晚上休息期间,非严重告警,要被自动忽略掉。SLS告警的管理功能(告警策略、行动策略等),可以提供静默、合并、合并降噪等功能,也支持根据节假日、工作时段等灵活调整通知策略等。告警态势大盘——IT系统的本体觉类似于本体觉让你闭上眼睛也能感知自己是站着还是躺着,处于什么姿势一样。SLS告警的多张告警大盘让使用者轻松掌握目前整体告警态势与进展。记事本——事务管理人的神经系统不能一下次并行处理多个事务(例如很难一边写作业、一边玩游戏),也不容易记住很多事情。IT系统运维人员面对许多并行告警时,同样如此,SLS的告警事务管理提供了这样的便利,就像记事本一样帮助他们管理好各个告警的状态,及时有效的跟进或处理。神经系统响应——行动管理一如人体对于感觉会做出反应(如看到危险,要躲避一样),SLS告警提供多种通知与响应形式,包括各种渠道:也企业组织下还支持贴心的功能如:值班表(轮岗代班等)、告警升级等。进一步参考论坛分享视频:https://developer.aliyun.com/live/246820SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/slsSLS新版告警文档首页:https://help.aliyun.com/document_detail/207609.html欢迎钉钉扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持更多SLS的系列直播与培训视频会同步到微信公众号与B站,敬请留意
前言本篇是SLS新版告警系列宣传与培训的第三篇,后续我们会推出20+系列直播与实战培训视频,敬请关注。系列目录(持续更新)一站式云原生智能告警运维平台——SLS新版告警发布!这才是可观测告警运维平台——20个SLS告警运维场景可观测告警运维系统调研——SLS告警与多款方案对比(本篇)1. 什么是SLS告警运维系统1.1. SLS(日志服务)是什么SLS是阿里云上云原生观测分析平台,为Log/Metric/Trace等数据提供大规模、低成本、实时平台化服务。目前对内已经是“阿里巴巴 + 蚂蚁金服”系统的数据总线,数年稳定支撑双十一、双十二、新春红包活动。对外则已经服务阿里云几十万企业客户。1.2. SLS新版告警——一站式智能告警运维系统SLS新版告警在中国站等发布公测(国际站预计4月发布),新版在SLS云原生可观测性平台上提供了一站式智能运维告警系统。新版告警提供对日志、时序等各类数据的告警监控,亦可接受三方告警,对告警进行降噪、事件管理、通知管理等,新增40+功能场景,充分考虑研发、运维、安全以及运营人员的告警监控运维需求。1.3. 优势使用SLS新版告警,可以有效缓解典型告警运维系统的痛点,和其他自建、商业化或云厂商提供的方案比,具备如下5大优势:2. 与自建方案对比2.1. 与ELK X-Pack 告警 (Watcher/KibanaAlert)对比2.1.1. 简介自建方案ELK表示使用开源方案ElasticSearch + Logstash + Kibana组合,但是其不包括告警功能,需要额外购买X-Pack商业包,会拥有2个告警功能,一个是ElasticSearch附带的Watcher,一个是Kibana 7.x+新增的Alert功能,注意:这两个告警功能互相独立,并不能协同和关联。2.1.2. 评估对比2.2. 与Prometheus & Loki(含AlertManager)告警对比2.2.1. 简介自建方案Prometheus&Loki表示使用开源方案Prometheus + Loki + AlertManager组合,其中Prometheus Alert针对时序进行告警监控,Loki针对日志进行告警监控,两者共同将告警发送给Alert Manager进行告警管理。2.2.2. 评估对比2.3. 与InfluxDB 2.0 告警(含Kapacitor) 告警对比2.3.1. 简介自建方案InfluxDB表示使用开源方案InfluxDB OSS 2.0 + kapacitor组合搭建告警监控系统,如果需要集群部署功能,还需要购买InfluxDB商业版本。注意,该方案只能支持针对时序数据的告警监控。2.3.2. 评估对比3. 与其他云厂商方案对比3.1. 与AWSCloudWatch 告警 +SNS+SSM对比3.1.1. 简介AWS告警监控方案,需要依赖AWS CloudWatch告警加上多个其他AWS服务达到告警监控和管理的能力。一般使用CloudWatch Alarm + SNS + System Manager OpsCenter组合的方式完成对日志、时序的监控管理。CloudWatch Logs支持日志的采集,但实际监控告警时,需要先转换成时序才行。3.1.2. 评估对比3.2. 与AzureMonitor 告警对比3.2.1. 简介Azure Monitor支持完整的基于时序与日志的监控,并很好集成了上下游方案提供完整的告警监控与告警管理通知功能。3.2.2. 评估对比4. 进一步参考4.1. 各方参考ELK:ElasticSearch Watcher: https://www.elastic.co/guide/en/kibana/current/watcher-ui.htmlELK X-Pack Kibana Alert: https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.htmlPrometheus & Lock:Prometheus Alert: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/Prometheus AlertManager: https://prometheus.io/docs/alerting/latest/alertmanager/Loki Alert: https://grafana.com/docs/loki/latest/alerting/InfluxDB: InfluxDB Alert: https://docs.influxdata.com/influxdb/v2.0/monitor-alert/Kapacitor: https://docs.influxdata.com/kapacitor/v1.5/working/alerts/AWS:Cloudwatch Alarm: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.htmlCloudwatch 定价: https://aws.amazon.com/cn/cloudwatch/pricing/SNS A2P: https://docs.aws.amazon.com/sns/latest/dg/sns-user-notifications.htmlSNS 定价: https://aws.amazon.com/cn/sns/pricing/SSM OpsCenter: https://docs.aws.amazon.com/systems-manager/latest/userguide/OpsCenter.htmlSSM 定价:https://aws.amazon.com/cn/systems-manager/pricing/Azure:Azure Monitor Alert概念: https://docs.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-overviewAzure Monitor Alert指南: https://docs.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-logAzure Kusto: https://docs.microsoft.com/en-us/azure/data-explorer/kusto/concepts/Azure Monitor定价: https://azure.microsoft.com/en-us/pricing/details/monitor/4.1. SLS相关SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/slsSLS新版告警文档首页:https://help.aliyun.com/document_detail/207609.html欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持(含直播培训)后续系列直播与培训视频会同步到B站,敬请留意
前言本篇是SLS新版告警系列宣传与培训的第二篇,后续我们会推出20+系列直播与实战培训视频,敬请关注。系列目录(持续更新)一站式云原生智能告警运维平台——SLS新版告警发布!这才是可观测告警运维平台——20个SLS告警运维场景(本篇)可观测告警运维系统调研——SLS告警与多款方案对比1. 新版SLS告警介绍1.1. 常规告警运维系统的痛点可观测性对于告警监控运维系统是有很高的要求的,但现状却不容乐观,我们可以看到常规监控运维系统存在如下6大痛点:具体展开细化如下:1.2. 新版SLS新版告警发布SLS新版告警在中国站等发布公测(国际站预计4月发布),新版在SLS云原生可观测性平台上提供了一站式智能运维告警系统。新版告警提供对日志、时序等各类数据的告警监控,亦可接受三方告警,对告警进行降噪、事件管理、通知管理等,新增40+功能场景,充分考虑研发、运维、安全以及运营人员的告警监控运维需求。1.3. 五大优势使用SLS新版告警,可以有效缓解前面提到的告警运维系统的痛点,和其他自建、商业化或云厂商提供的方案比,具备如下5大优势:2. 20类功能场景展示场景样例1:日志、时序、跟踪一套查询分析语法对日志、指标、跟踪等数据,提供一套通用语法,SLS告警监控的支持大规模日志/时序/跟踪等实时监控,而查询统计语法也是使用通用统一的SQL(并扩展)的方式提供。也就是SQL = Search + PromQL + SQL92。例如对特定机器是否在线监控,可以使用SQL、PromQL、或者两者子查询协同、甚至多层嵌套使用机器学习的算法来找出异常。场景样例2:丰富的机器学习算法支持SLS的机器学习算法是直接在SQL扩展方式提供,覆盖了以下4个场景:场景样例3:全局监控不需要同步数据到本地,即可跨库、跨区域、甚至跨账号的监控并告警。场景样例4:多数据源协同支持多个(可不同)数据源之间的协同场景样例5:多目标监控与黑白名单一条规则可以同时监控日志/时序库中的多个逻辑目标,可分开告警,且支持自定义白名单或黑名单(只针对多目标监控时自动专注或排除)。场景样例6: 动态设置告警严重度支持根据检测值动态设置告警的严重度。场景样例7:内置告警监控规则库内置各场景下告警规则库 (500+),开箱即用,且持续增加中。场景样例8:告警静默可以基于告警来源规则的属性(区域、项目、规则名等),也可以基于告警自身的属性(状态、标签、标注、严重度等)抑制告警。场景样例9:告警抑制一个告警可以抑制(忽略)其他的告警,可有效阻止因为一次严重告警触发的告警风暴。场景样例10:路由合并降噪支持将告警归类分派到多个合并集合中,进一步的降噪控制(去重、合并等)后,合并发送(通过行动策略)。在合并集合中,多个告警重复发送会自动去重,在集合首次、变化时发送,或不变时延迟发送。场景样例11:事件(Incident)阶段管理自动给告警创建事件(Incident),可对其确认、解决、忽略、设置处理人、注释等操作。场景样例12:告警态势大盘提供多张告警态势大盘:监控规则中心、告警链路中心、告警排错中心、监控规则执行报表等。场景样例13:通知渠道分派可以自由按照告警的属性,将对告警分派给不同人和渠道。场景样例14: 日历与工作时段感知可以自定义日历的时区、工作时段、自动同步节假日,亦可重置。通知渠道自动感知。案例: 发生告警时,工作日发送短信、钉钉、邮件;法定假日(如国庆长假)和周末时仅发送邮件。案例: 发生严重告警时,工作时段发短信、钉钉、邮件;非工作时段额外打电话。场景样例15:告警通知升级在事件(Incident)一直处于特定未完成状态时可触发通知升级(例如直接打电话、或通知组长等)。场景样例16:独立的接收人/组管理支持独立的接收人、组的管理。场景样例17:值班组/表-轮岗支持创建值班组,选择特定的时间段、对象(可以是组)、轮岗、交班方式后,自动排班。可预览最终排班效果:亦可按细腻度分排班:可以针对特定不连续的时间段排班场景样例18:值班组/表-代班值班人员临时不能值班时,可由他人代班,自动替代原有值班人员值班,可预览效果。场景案例19:内置渠道扩展原生内置支持语音(电话)、短信、邮件、钉钉(群机器人)、阿里云管理员消息中心等短信、语音(电话)支持国际手机号。支持Webhook灵活定制,可支持企业微信、Slack、飞书等通知渠道。提供内容模板定制格式、甚至可以引用变量场景案例20:渠道额度控制支持设置默认、特定人、特定组的短信、邮件、语言的渠道额度,且支持隔离控制。3. 进一步参考SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/slsSLS新版告警文档首页:https://help.aliyun.com/document_detail/207609.html欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持(含直播培训)后续系列直播与培训视频会同步到B站,敬请留意
前言本篇是SLS新版告警系列宣传与培训的第一篇,后续我们会推出20+系列直播与实战培训视频,敬请关注。系列目录(持续更新)一站式云原生智能告警运维平台——SLS新版告警发布!(本篇)这才是可观测告警运维平台——20个SLS告警运维场景可观测告警运维系统调研——SLS告警与多款方案对比1. 云原生观测告警1.1. 业务发展对开发运维的挑战现代业务发展对开发运维提出了新的挑战,具体如下:1.1.1. 业务:稳定性要求越来越高参考AIOps的目标与挑战,随着越来越多的业务云化数字化,例如今年开始大热的在线教育,任何一个稳定性、可靠性等异常都将给业务带来巨大的损失。要求SLA(服务可靠性)越高越好、MTTR(问题平均修复时间)和Cost(成本)越低越好。在各大云厂商,也指定了非常多的稳定性制度和要求,例如1-5-10(1分钟发现问题,5分钟定位问题,10分钟解决问题)准则。1.1.2. 系统:复杂性越来越高随着开发模式(敏捷开发、DevOps)、系统架构(分层、微服务)、部署模式(容器化、云原生)、和基础设施(多云、混合云)的快速演变,系统变得原来越复杂。当系统出现问题时,如何发现问题、排查定位原因、解决问题就越来越困难。从监控运维的角度,系统的可观测性也逐步成为是一个基本要求。1.1.3. 工程师:职责越来越大因为前述原因,系统从研发集成到上线前后的各个阶段,有大量的工作需要做,不同人员参与的协同会大大降低响应速度,越来越多的公司要求一专多能。开发、测试、运维融合逐步成为趋势,开发人员逐步开始承担测试的工作、部分的运维甚至运营的工作。随着业务数字化时代的到来,可预见到运营角色更深入的与开发、运维角色融合也是一个趋势,也就是说开发工程师未来投入到运营(Ops)的时间也会逐步增加。1.2. 什么是可观测性传统监控一般以一个白盒方式监控系统,专注发现核心指标异常,例如500错误,客户订单成功率等。一般这种问题发生时,准取性极高(例如大量500错误,大量订单失败,一定表示SLA有问题),一般也都比较严重。因为是黑盒,进一步排错和修复时间和成本极大,往往给开发运维人员带来极大压力。根据海恩法则(Heinrich's Law),每一起严重事故背后,必然有29次轻微事故和300起未遂先兆以及1000起事故隐患。如果提前处理那些不那么严重的问题、先兆或者隐患,其实是可以避免后续的严重事故的,也就避免了其带来的巨大压力和损失。可观测性是对传统监控的升级,其要求进行白盒化监控,对各种可能的隐患、先兆、不严重问题进行监测、跟踪处理。且不再只是在发布后,而是在开发、测试阶段就进行。因此对比两者,可以发现,传统监控主要由SRE人员从系统外部进行监控,关注指标,发现问题(Know What);而可观测性由DevOps人员从系统内部进行监控,关注指标、日志和跟踪等数据各种数据,发现问题并挖掘原因(Know Why)。1.3. 可观测性的挑战根据AIOps平台方案选择,可知各种监控数据(指标、日志、跟踪等)的中台都有各种方案,同样的监控系统也有非常多的选择。主要挑战就是:数据覆盖不完整、存在数据孤岛(无法关联协同)使用门槛高,不人性化1.4 告警运维系统的痛点可观测性对于告警监控运维系统是有很高的要求的,但现状却不容乐观,我们可以看到常规监控运维系统存在如下6大痛点:具体展开细化如下:2. 什么是SLS告警运维系统2.1. SLS(日志服务)是什么SLS是阿里云上云原生观测分析平台,为Log/Metric/Trace等数据提供大规模、低成本、实时平台化服务。目前对内已经是“阿里巴巴 + 蚂蚁金服”系统的数据总线,数年稳定支撑双十一、双十二、新春红包活动。对外则已经服务阿里云几十万企业客户。2.2. SLS新版告警——一站式智能告警运维系统SLS新版告警在中国站等发布公测(国际站预计4月发布),新版在SLS云原生可观测性平台上提供了一站式智能运维告警系统。新版告警提供对日志、时序等各类数据的告警监控,亦可接受三方告警,对告警进行降噪、事件管理、通知管理等,新增40+功能场景,充分考虑研发、运维、安全以及运营人员的告警监控运维需求。可以看到新版告警由4个模块组成:告警监控、告警管理、通知(行动)管理以及即将发布的开放告警组成。下面逐步介绍各个模块的作用。2.3. 优势使用SLS新版告警,可以有效缓解前面提到的告警运维系统的痛点,和其他自建、商业化或云厂商提供的方案比,具备如下5大优势:2.4. 告警监控概述通过告警监控规则配置,定期检查评估,查询统计源日志、时序存储,按照监控编排逻辑,评估结果,并触发告警或恢复通知,最终发送给告警策略。告警监控提供的功能可以分为如下3类:基础能力其中值得强调的是SLS告警监控的基础能力支持大规模日志/时序/跟踪等实时监控,而查询统计语法也是使用通用统一的SQL(并扩展)的方式提供。也就是SQL = Search + PromQL + SQL92。例如对特定机器是否在线监控,可以使用SQL、PromQL、或者两者子查询协同、甚至多层嵌套使用机器学习的算法来找出异常。其中机器学习算法是直接在SQL扩展方式提供,覆盖了以下4个场景:2.5. 告警管理每一个告警监控规则会将触发的告警(含恢复通知)发送给一个预先配置的告警策略,通过告警策略配置,对所有接受到的告警进行路由分派、抑制、去重、静默、合并操作,后再分派给特定行动策略。通过告警中心控制台可以管理告警的状态(包括设置处理人),和查看告警链路与规则态势。告警管理提供的功能也可以分为3类,如下:2.6. 行动(通知)管理每一个告警策略根据配置分派合并后将每个告警合并集合发送给特定的行动策略。由行动策略根据配置动态分派给特定通知渠道通知到特定的人/组/值班组,也支持告警未及时处理下的通知升级。行动(通知)管理提供的功能也可以分为3类,如下:3. 进一步参考SLS(日志服务)云原生观测分析平台:https://www.aliyun.com/product/slsSLS新版告警文档首页:https://help.aliyun.com/document_detail/207609.html欢迎扫群加入阿里云-日志服务(SLS)技术交流, 获得第一手资料与支持(含直播培训)后续系列直播与培训视频会同步到B站,敬请留意
引言本文介绍我在PyCon2020年底时分享的议题内容,结尾有录播的视频和PPT下载链接。Python强类型检查的历史、背景Pyton语言与类型限定我们都知道Python是一门强类型的动态类型语言,具体如下特性:可以动态构造脚本执行、修改函数、对象类型结构、变量类型但不允许类型不匹配的操作这里举了2个例子,第一个例子体现动态性:用字符串直接执行代码,动态构建了一个函数并执行,甚至给函数挂载新的名字。第二个例子体现强类型性:变量都有类型信息,不同类型无适配操作时不允许操作,例如整数和字符串不允许相加。 动态语言 vs 类型提示因为Python的强类型挂载,但是又不像C++/Java有强类型定义信息(C++11开始也有auto自动推导了),比较容易出错。因此Python也提供了类型标注功能,有了类型标注提示后,就可以在编码时即发现错误。例如下面一个有标注,一个没有标注的例子;可以看到有标注的例子,IDE/工具都会在编码时即发现潜在与标注不匹配的执行甚至逻辑错误并提示,而无标注时,工具的支持就大打折扣了。函数标注(PEP 3107)标注功能是通过PEP 3107就添加进来了,以函数的标注为例,实际上对函数参数、返回值标注,是放在add.__annotations__变量里面。如图,IDE、工具可以在代码层面静态检查并提示错误,信息非常直观。强类型检查的优势与现状通过上面的例子就可以发现,强类型检查可以给Python带来不少好处,这方面总结的文章不少,这里总结一下如下:易读:比docstring更易理解接口协议,也更易使用三方库(IDE工具支持)排错:编码、编译期间即可发现错误(IDE工具支持)。重构:接口范围明确,更易于理解和放心重构。性能:(可能)可以做到静态编译优化。那么实际落地的情况如何呢?可以看到还是乐观的:生态-IDE:三方类型检查工具比较全面(PyCharm、VSCode、VIM等)。生态-库工具:三方库有官方或大厂支持且很流行。官方/mypy-9.5K star、Facebook/pyre-5K Star,Google/pytype-3K star等。成熟度:静态检查已被广泛验证有效性,尤其大型工程(dropbox迁移400万行到静态标注)。行业:主流编程语言以静态强类型检查为主(C++、Java、Go等);其他动态语言的静态类型扩展迅猛发展(如TypeScript)。Python强类型检查的策略既然强类型有这么多好处,并且已经越来越被接受,是不是意味着Python会往强类型语言转型呢?可以确认不会发生(至少在Python3上),因为Guido很早就在正式文档中阐述了这方面的策略:Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention. ——Guido in PEP 484可以看到Python的策略是保留了类型检查向后兼容的能力,不会转型为真正的强类型检查语言,但是Python依然会对强类型检查做持续的支持,通过如下金字塔结构来完成,最终达到即享受强类型检查的好处,又保持向后兼容和动态类型语言的灵活性,根据后续的介绍,可以看到,这方面Python还是付出了不少的努力。Python类型系统到Py3.7(2018.6)才基本成型具体来看下Python做了哪些事情来支持强类型检查,如下是一个非常长的(14个)PEP(Python改进建议)的列表和落地情况(图中时间是文档时间+1年左右是实际落地时间),可以看到2006年Python就引入了一些基础支持,并且在PEP 483开始快速迭代(图中橙色是比较重要的迭代),并且到了Python3.7才真正算是成型。Python强类型检查常用场景实践(22个)下面开始浏览一下Python强类型检查的能力在各个主要场景下的具体最佳实践典型类型标注场景1:复杂数据结构的标注(使用别名)(PEP 484)当被标注的类型比较复杂时,可以使用类型别名来简化类型提示标注。(这里直接使用了内置类型[]做标注(PEP 585))这个例子中servers是一个比较复杂的结构,它的每个成员可以用一个变量(别名)接住后再继续定义,最后用于标注。场景2:变参类型标注(PEP 484)Python的变参也支持标注,如下案例解释了如何对变参、命名变参直接标注其值的类型:场景3:类别选择 Union vs. TypeVar(PEP 484)Python的动态灵活性,允许一个变量的类型可以是多个选择(例如一个add函数允许传入int、long、str类型的变量等),在这种情况下可以Union和TypeVar来定义。两者区别如下:使用Union定义可能的类型选择: x、y、返回值的类型同一时刻可以不同使用TypeVar定义一组类型:x、y、返回值的类型同一时刻必须相同以下举例说明了两者区别:场景4:函数类型(PEP 484)高阶函数可以接收或返回函数,因此函数和变量一样,也需要类型标注,具体形式可以如下:Callable[[参数1类型, 参数2类型, …], 返回类型]例如下面这个高阶函数的参数标注:场景5:泛型类型(PEP 484)容器下的泛型:TypeVar泛型在C++/Java中非常普通,Python中也存在这样的情况,例如参数需要是一个列表,但是里面元素类型可以不定。这种情况,可以使用泛型TypeVar。例如下面这个函数接受一个泛型列表,并返回第一个元素。在标注后,错误的使用情况也可以被检查出来。当泛型是多个可选类型时,TypeVar也支持,如下例子:类下的泛型:Generic泛型场景比较丰富,类也有泛化需求,例如一个类需要定义泛型时,比较好的方式是,使用Generic并继承它,参考如下案例:以上就各种情况的标注做了大概的介绍,可以看到Python对各种类型的标注都可以很好支持,下面具体讲解一下标注的方式。标注方式场景6:使用注释(Comments)进行标注(Annotation)(PEP 484)可以看到,目前为止的标注都是以修改代码才能完成的,这样某种程度上是有一定风险的,因为修改了代码,就意味着可能引入了BUG,哪怕风险不高。这个问题实际PEP已经考虑到了,这并不是Python添加强类型检查的唯一方式。除了上面的代码方式标注,还可以用注释进行标注。形式如下:# type: xxxx 进行标注注意其中函数的标注方式:(参数类型列表) -> 返回值类型这里举一个例子说明:场景7:将标注放在单独文件(PEP 484)添加注释某种程度上不算修改源码,但还是会修改源代码文件(依然引入了一些风险,并且可能源代码不一定有权限去添加注释),因此PEP 484还引入独立的文件方式来解决这个问题:具体的方法是,使用一个独立的Stub文件(.pyi)与源文件相同,并放在同一个文件夹即可。而Pyi的语法是将源文件的一些类、函数、变量等都打上标注(不需要具体实现)。例如:这样的方法有优点也有缺点:优点:不需要修改源代码(减少引入BUG可能)Pyi可以使用最新的语法(源文件可以是低版本)测试友好不拥有的三方库,也可以补充标注信息。缺点:代码重复写了一遍头(工作量)打包变得复杂高级类型标注场景8:前置引用(PEP 484)强类型检查实际是是非常复杂的(参考C++模板技术与泛型编程),在进行类型标注时,实际的扩展的情况要复杂的多,下面开始讲解一些高阶的强类型标注的场景。首先看一个经典案例是一个二叉树节点,在进行类型标注是,就需要引用自己,改如何解决?这种情况下就需要使用前置引用,方式是用字符串代替类型标注。参考下面例子:场景9:函数标注扩展overload(PEP 484)我们都知道Python函数语法本身不支持重载(实际通过库可以扩展),一般一个函数可以接受各种情况的参数,并返回特定的类型。例如一个add,传递int就返回int,传递bytes/str就返回str,这种情况下应该如何标注呢?可以使用typing.overload进行参数返回值描述。参考下面的案例,但需要注意:必须有一个无修饰版本做真正实现。这里仅仅用于静态类型检查,如果需要真正的重载,可以使用functools.singleddispatch等有时可以用TypeVar代替,显得更简洁,但请注意和上述overload有一个重要区别,就是入参与返回参数的映射关系丢失了。场景10:协变(covariant)与逆变(contravariant) PEP 484在泛型编程中,还有一个比较常见的概念就是协变与逆变(Java泛型编程中常常遇到),如下定义:协变:让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值);例如:老鹰列表赋值给鸟列表逆变:让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确。例如:鸟列表赋值给老鹰列表这种情况在Python强类型标注也一样存在,例如下面是一个协变的例子,注意使用了参数covariant=True:场景11:静态duck typing(PEP 544)Python支持OOP,也支持duck typing,在强类型检查中,duck typing也一样适用,例如一个函数close_resource接受一个参数,要求这个参数必须提供一个特定的方法.close()。这种需求在强类型检查时,也可以支持,我们称之为静态duck typing,相关方案由PEP 544支持。如下是一个具体案例:通过继承typing.Protocol,构建了一个IResource的静态类型,再使用这个IResource进行标注,之后就可以实现对所有参数是否具备这个IResource定义的方法进行校验。这就是static duck typing。某种程度上,可以静态duck typing类似于Java的接口概念。可以有实现,可以被继承可以继承多个接口,构建一个新接口(参考后面的例子)但实际类型检查时,不要求被标注对象继承这个IResource,只需要对象的所有成员方法signature匹配Protocol即可。如下是一个多继承的例子:场景12:泛型静态duck typing(PEP 544)通用的,泛化在静态duck typing也存在,例如约束的方法中的某个参数的类型,需要是泛化的。这种情况,typing.Protocol也是支持。和前面一样,只需要使用TypeVar定义个泛型类型,再传递给Protocol即可。参考下面样例,这里定义了一个标准可迭代对象的泛型静态duck typing。同样的,也支持泛型编程中的协变或逆变:场景13:运行时化duck typing(PEP 544)静态duck typing,顾名思义,静态时(编码时)检查,运行时不检查。但有时,也存在运行时使用isinstance检查的需求,这种情况下,这种需求也是支持。只需要使用runtime_checkable装饰,即可让Protocol可以运行时访问,例如isinstance检查。参考如下例子:有用的标注库场景14:dataclasses(PEP 557)Python对强类型编程的支持不限于标注类型的支持,也提供了许多基于强类型标注上结合Python自身灵活性扩展的非常强大库来增加工程效率,这里我们介绍一个相关的非常有用的库dataclasses。这个库顾名思义,支持数据类的快速构建,可以把dataclass理解为一个加强版的namedtuple,是一个支持带默认值的可变的命名元祖:甚至还支持延迟初始化,如下的变量c是基于a和b的值,动态初始化的。也支持复杂的默认值,例如下面的mylist不传入时,会使用空list初始化。dataclasss也提供了与tuple、dict互转方法,如下:场景15:字面量类型(PEP 586)Python较新的发布,继续发挥其灵活性的特点,不限于借鉴其他语言的强类型机制,还发扬光大,添加许多更灵活有用的功能,例如这个字面量类型。有了这个功能后,可以做到不修改类型为enum的情况下,限定传递参数。语法形式:Literal[字面量1, 字面量2, …]例如下面例子,使用Literal,限定了打开文件的mode字符串的可枚举值,编码期间就可以检查非法的参数:场景16:typing.Final(PEP 591)通用的,在Python 3.8版本中引入一个扩展的标注typeing.Final,用于标注变量,这个内置标注非常有用,可以理解为实现了C++语法const的静态检查作用。指定变量被初始化后无法再被修改、类变量无法被子类修改。声明为Final的类成员的变量,未初始化的,必须在__init__里面初始化。如下案例具体说明:场景17:typing.final(PEP 591)另一个被引入的就是小写的typing.final,用于标注类,可以理解为实现了Java语法final的静态检查作用。虽然Python本身就可以扩展实现运行时final的作用,但是这里实现了检查期的final,这个官方版本可以说非常的有用。如下,除了可以修饰类(不能被继承),甚至可以修饰类的方法(不能被重写)。优化、控制与扩展场景18:延迟类型提示执行(PEP 563)我们知道,Python标注执行期间不做强类型检查,但实际上标注依然会占用资源(空间和时间),下面开始我们看一些性能优化的场景。例如下面的是一个极端例子,标注初始化指向了一些代码逻辑,初始化时,需要额外的资源消耗。为了优化,可以开启延迟类型提示执行,只需要从__future__导入annotations即可,这时会跳过标注的代码逻辑,直接转化为字符串。参考下面案例:场景19:静态检查时与运行时区分(PEP 484)可以看到类型检查需要导入一些库,也就增加了运行时的开销,因此Python引入检查时的概念,允许在检查时才导入特定库,而在实际运行时不导入。但是注意这种情况下,相关标注只能用注释或字符串(前置)方式标注。例如下面的例子,可以即保持静态检查,又不会因为引入特定标注库对性能和资源产生冲击:场景20:关闭静态类型检查(PEP 484)特定情况下,我们也需要关闭静态检查(例如测试或开发中间时),这种情况只需要使用typing.no_type_check修饰函数或类来关闭。但是如果希望关闭对一个装饰器的静态检查的话,需要使用typing.no_type_check_decorator修饰装饰器来关闭。场景21:新的Hook方法(PEP 560)因为typing中常常需要使用[]进行传递类型,在定义自己的类型时,可能需要做一些高级定制,因此Python-Core中引入了新的Hook方法__class_getitem__,在传入类型时会自动调用。这样的修改,简化typing的实现方式。场景22:类型标注(PEP 593)Python再一次发挥了灵活性特点,并大步向前。这个是Python3.9才引入的新特性,已经延伸了强类型检查的范围,扩展到的运行时数据逻辑性的问题,将以前一些三方数据标注库从运行时引入到了检查期。通过使用typing.Annotated,可以进一步增加额外附属信息,方便静态检查或运行时做更进一步的检查。例如:分数(值从1-100的整数类型)又例如:包含最多10个元素的元组列表结语以上可以看到Python对于强类型检查还是符合其核心精神(灵活性与实用性),一方面还是非常完善的,且大踏步的往前延伸,另一方面,也又一次的让Python的深入掌握的门槛进一步增加(进入了强类型编程、泛型编程领域,甚至动态扩展的场景)。工具参考如下列前面提到的一些三方工具,供进一步参考。typeshed: Python内置标准和三方库的pyi集合repo(PyCharm、mypy、pytype已包含)mypy: 官方标准静态类型检查工具。pyre:Facebook开源的静态类型检查工具。pytype:google开源的Python静态代码扫描工具(不依赖标注)。pyannotate:dropbox开源的自动给Python添加类型标注的工具。pydantic:一个基于标注的Python数据校验与配置管理库。什么时候需要或不需要类型检查?了解背景并讲解具体场景前,我先看一下具体的使用策略:什么时候需要或不需要使用Python类型检查?以下情况建议采用:提供SDK、库/接口给其他人时。比docstring更清晰、主流IDE支持提示和校验。代码行数越多,价值越大。规范化编码,通过工具可以辅助发现潜在BUG。需要写UT(单元测试)的地方,就需要类型检查 (by Bernat Gabor)以下情况需要一定考量再决定:原型(Prototype)或验证性质项目(POC)的代码,可以先不引入。大量旧有代码,需要逐步阶段性引入(参考Dropbox经验)。不熟悉Python和类型提供功能用法时,可以先不引入。其他录播视频:https://www.bilibili.com/video/av458282564/PPT下载: https://github.com/wjo1212/ChinaPyCon2020顺便打个广告,欢迎扫群加入阿里云-日志服务(SLS)技术交流钉钉群, 获得第一手资料与支持:
从被动审计到主动监控——SLS日志审计-内置告警规则发布! 背景介绍 自从日志服务(SLS)日志审计功能发布以来,已经陆续有许多重要客户使用来审计云上系统的行为、网络、操作日志等等。目前SLS覆盖几十种以上云产品日志接入,而日志审计在此基础上更是针对审计需求,支持了一键式、多账号、跨区域、自动发现实例的中心采集、存储并提供上层多种审计方式的功能。 主动监测 通常在审计场景下,不同客户也因为组织发展阶段、人员形态、业务需求等有不同层次的审计诉求(如下)。表面上看,大部分客户对于日志审计的基础诉求是采集、存储、自动配置即可,也就是将日志审计作为一个兜底的、事后被动可溯源的场景使用。但实际上在安全问题发生后,排查的成本是相当高的(参考FireEye M-Trends 2018报告),如果可以进行更主动的监控、告警、分析、建模、智能,显然安全防护效果会更佳。例如云账号Root账号是否允许主账号秘钥在使用?需要时刻监测所有的访问日志并检查是否有违规,并及时报告,而不是可能秘钥已经泄露才发现有违规行为。但因为这方面要求对于系统建设要求较高,且投入人力、组织建设、持续运营方面较大,往往较大型公司或组织会做较多关注,大部分公司不具备这方面的条件(而非需求)。 日志服务(SLS)可以在以上各个层次都可以较好支持客户诉求,降低门槛和减少落地时间,这次的发布中的内置告警规则功能,则是进一步增强用户这方面的能力和降低使用门槛,让更多客户在几乎不增加费用的情况下,享受主动监测带来的好处。 功能介绍 SLS日志审计新发布了内置告警规则,具备如下功能和优势: 近百个规则,一键式开启 支持白名单、多实例自定义参数 独立用户、用户组 自由渠道管理 独立告警态势大盘 该功能与通用告警一样,仅针对短信、语音按条收费外,不会产生其他额外费用。 覆盖区域与站点 公有云、金融云、国际站等同步发布,覆盖目前已支持的所有区域。详情参考产品覆盖文档。 功能概览 进入日志审计配置完成后,可以看到新增的审计告警菜单与导航栏。 内置告警规则 其中告警规则覆盖了近百种的告警规则(持续增加中),主要是CIS规则和云安全最佳实践两大方面。其中CIS规则覆盖了账户安全、权限管理、存储、主机、数据库、网络、日志等各个方面的合规建议,而云安全最佳实践提供CIS的更完善的补充。SLS日志审计的内置规则,将这些文案性质的建议或偏配置的静态规范,变成了可监测确认的、自动化规则。 一键式开启与参数定制 告警中心的告警规则页面提供了一键式开启的功能,根据标签过滤器,选择特定的规则,例如CIS基准,多选后,在工具栏即可点击开启(也可单独操作)。 大部分规则自带默认值,并且支持自定义,例如规则Root子账号无MFA登录,可以针对特定可信子账号关闭检查,点击白名单按钮,添加特定主账号ID和子账号用户名即可: 也可以针对特定规则,修改器默认参数,例如规则Root账户连续登录告警,可以配置次数阈值以及发生时问题的严重度: 对于特定规则,如规则RDS慢SQL检测,可以针对特定业务的数据库,设置不同参数(数据库名、阈值、严重度等),添加多个告警实例来分别监测: 告警大盘 内置告警提供了全新的告警大盘,覆盖规则中心、链路视图和排错中心等。例如通过规则中心,可以知道目前有多少告警规则被触发,具体触发的事件如何;通过链路视图,可以知道有多少告警通知事件,经过降噪后分别在多少渠道最终通知出来。 动态告警策略 日志审计内置的告警基于新版告警引擎,支持动态告警路由功能,例如可以根据告警的特性(如来源账号、云产品、或事件严重度)来分派到特定的渠道,而渠道也可以引用独立配置的用户、用户组、内容模板等。 静默支持 可以对特定告警临时关闭,也可以对特定事件进行临时静默,例如某个时间段,大家在休假,特定不严重的告警(如告警严重度不到高的)都可以忽略。那么只需要在告警策略中,修改日志审计对应的静默策略,添加一条这样的规则即可: 独立用户、用户组、内容模板 独立的用户、用户组和内容模板使得告警更加容易管理,独立用户、用户组都支持批量导入,且支持以前做了优化: 用户:支持临时禁用,方便特定用户离职或休假时使用;或禁用特定渠道(如电话、语言),方便特定人员出差或手机不可用时使用;单个用户支持多个邮箱等。 用户组:支持临时禁用,与用户解耦(一个用户可以属于多个组)。 参考 日志审计的内置告警规则库会持续更新,后续也会有更多的最佳实践介绍发布。更多信息请参考日志审计的告警文档: 日志审计文档(含使用前需知) 日志审计告警文档(目录) SLS计费说明(含告警通知费用详情) 也欢迎钉钉扫码加入SLS技术交流群,观察本功能宣传直播或视频,并获得更及时的客户支持、资讯发布、直播分享和技术交流:
介绍 新冠病毒疫情分析App 新冠病毒疫情分析App是基于阿里云日志服务中台,提供的一站式的数据处理可视化分析系统。借助它,可以在全球范围内了解各省份、市区的全面一手疫情信息和防疫新闻动态。目前该能力全面开放给政府、社区、第三方平台和开放者进行广泛应用,完全免费开放。 关于日志服务 阿里云的日志服务(log service)是针对日志类数据的一站式服务,无需开发就能快捷完成海量日志数据的采集、消费、投递以及查询分析等功能,提升运维、运营效率。日志服务主要包括 实时采集与消费、数据投递、查询与实时分析 等功能,适用于从实时监控到数据仓库的各种开发、运维、运营与安全场景: 作为日志分析中台,日志服务提供了一站式的数据采集、加工、查询分析、AI计算、可视化,并支持互联互通。 亮点优势 亮点1:多份数据源、实时同步并形成可视化平台: 覆盖全球、省份、市区疫情信息,患者行程轨迹、路径信息;新闻公告等; 注意:上图中各种数据源表示日志服务允许客户自行接入其他的各种数据源,目前App提供的数据来源于央视新闻、人民日报、各省市卫健委公告(实际信息以官方为准)。 亮点2:内置多份数据大盘并支持自定义 提供全球态势、省市态势、确诊者踪迹态势、相关公告新闻等;支持交互式查询分析,自定义报表,深钻与告警等; 全球态势 省市态势 感染者行程信息 新闻公告信息 参考: 日志服务交互式分析 如何自定义仪表盘 如何订阅仪表盘 如何自定义告警 亮点3:数据平台开放,互联互通 在互联互通方面,日志服务是开放的,可以和大量其他环境的系统、三方应用或开源进行对接。提供易扩展的数据分析、存储、可视化平台能力,如DataV、Blink、OSS、流计算、Grafana、SOC等等; 参考: 数据加工系列视频 如何嵌入日志服务仪表盘到自己的控制台。 如何接入或消费日志服务数据 亮点4:完全免费 疫情服务的App、以及相关资源数据,包括仪表盘、告警等功能完全免费。 如何使用 登录阿里云日志服务控制台,可以看到疫情分析的应用: 点击进入进行首次配置(一次性,后续数据会自动同步),之后即可直接使用日志服务提供的多份数据大盘,开启交互式分析、可视化之旅。 相关链接 应用帮助:https://help.aliyun.com/document_detail/151625.htm 了解更多:https://sls.console.aliyun.com/intro/index.html#/本应用相关直播视频:https://yq.aliyun.com/live/1933欢迎加入日志服务官方技术交流群,了解更多最新信息:
直播预告(2020年2月11日上午11点):https://yq.aliyun.com/live/1933 使用日志服务提供的疫情分析App,免费开通与使用,实时同步数据,支持更灵活的自定义交互式分析、可视化、仪表盘构建、钉钉告警、订阅等功能(完全免费)直接登陆日志服务,首页应用专区,即可开通并使用(2月11日发布):https://sls.console.aliyun.com/ 并支持深钻的各个省市的具体信息,进行关联对比: 也可以访问日志服务提供的只读的Demo链接(手机可以打开,并支持分享)https://1340796328858956.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/demo/slsconsole/?redirect=true&type=2020(如果要分享,请使用以上链接,跳转后的链接无法分享)也可以收集扫码查看(如下二维码可以分享): 注意: 以上Demo链接只能查看,如需进行交互式分析、自定义大盘、告警、订阅等功能,推荐使用日志服务的疫情分析App(完全免费) 技术支持由阿里云日志服务提供技术支持, 扫码了解更多
目前新型肺炎疫情扩散比较快,为了让大家能够较为实时看到最新的情况,我们使用日志服务技术构建了一张新型肺炎疫情实时动态大盘 (手机可打开) https://1340796328858956.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/demo/slsconsole/?redirect=true&type=2020 数据来源: 央视新闻、人民日报、各省市卫健委公告等,仅供参考,以官方最新数据为准
引言 本文介绍我在PyCon2019上海站的议题内容,结尾有PPT下载链接。根据Gartner的报告,AIOps将在未来5-10年落地开花,并集中统一各种Ops平台(Dev、IT、Net、Sec),本议题介绍AIOps的核心作用、相关工程难点(数据采集、数据中台、智能算法、自动化等)与开源方案选择,适当介绍了Python在其中的主要作用,覆盖开源方案有:Kafka、Elastic Stack (Beats, Elasticsearch,Kibana)、K8S、Prometheus、Grafana、Thanos, Tick stack (Telegraf,InfluxDB,Chronograf,Kapacitor)、Ansible、OpenTelemetry、Skywalking、Druid、Clickhouse等。 一. 关于AIOps IT运维目标 AIOps并不是蹭热点,而是以实实在在解决IT运维的痛点或提高效率为目标。一直以来IT运维存在以下3个核心指标/目的: 1. MTTR的降低 MTTR(Mean Time To Repair)平均修复时间,是一个衡量系统宕机时间的指标,IT运维人员以降低此目标为第一要务,越低越好。 2. Cost的降低 公司每年需要在IT上投入很多钱,包括硬件、软件、服务、人员等,通过IT运维希望将资源效率提高到最高,形成持续的成本优化。另一方面,宕机也会带来业务损失(例如电商一时不能用,客户就无法下单),因此此指标也与MTTR和SL相关,MTTR越长,SL越低,成本也越高。 3. Service Level的提高 SLA表示客户与服务商之间服务可用性的承诺,一般以服务可用性用时长为维度,例如99.99%可用,表示一个周期(例如一个月)宕机的总体时间不超过0.01% * 365天 < 4.5分钟。有时也表示API错误率占比。 IT运维挑战 但是IT运维所面临的挑战也呈现越来越高的趋势,大概分成两类原因: 1. IT系统复杂度越来越⾼ 目标系统越来越复杂,快速定位问题难度越来越高,具体细分为:架构演变复杂化和数据孤岛越来越多。 架构演变复杂化随着云计算的普及,许多公司存在云上、云下业务,甚至多云策略(海外业务用AWS,亚太用阿里云);SaaS的普及(这点在海外非常普遍),容器化与微服务架构的流行,使得一套系统的部署非常复杂。某一个环节出错,可能落点也都有可能。 数据孤岛越来越多各种数据存储于各个系统之中,在大数据下呈现4V特点(容量Volume、速度Velocity、种类Variety和价值Value),很难集中采集与处理,一旦发生问题,很难有效检查具体数据信息。 2. IT系统成本越来越⾼ 祸不单行,修复的IT问题的成本也越来越高,具体细分为三类原因: 业务中断成本信息化越来越发达的今天,一些流行产品动辄上亿PV、千万UV。例如一些电商、服务系统,一旦临时不可用,造成的损失就是客户无法下单、转投竞品处购买,设想一下,双十一当天每秒的交易额可知成本之高,对于金融、公共服务类的系统,则会造成更大的损失也有可能,基本都会成为新闻报道。 缺少持续改进另一方面,普遍存在的现象是运维人员的日常工作大部分时间都在忙于救⽕,自然缺少持续改进的时间和机会,包括工程流程上梳理漏洞,编写引入自动化工具,客户培训等 学习速度跟不上这里特别强调这点,是因为其实人始终是一个非常重要的原因,业务增长的速度往往超乎人的想象(参考风口论),某个业务在一年内提高5倍、10倍甚至100倍都是有可能的,但人的学习成长速度往往很难匹配上。 AIOps基本概念 虽然Ops的概念很宽泛,但一般AIOps表示Artificial Intelligence for IT Operations,可以理解为组合了大数据、机器学习、分析来帮助IT运维实现其目标(例如发现、预测、修复问题)。 而Gartner报告中的一张图可以更具体的解释AIOps对IT运维的改进: 通过历史、实时流式数据的导入,结合大数据+机器学习在IT运维的三个方面(检测、管理、自动化)中的4类场景(历史分析、异常检测、性能分析、关联与上下文等)进行增强。 1. 大数据促进平台融合 可以看到AIOps平台要求采集各种数据(包括日志、指标、网络数据、API、文本、社会媒体信息等),用于分析、训练API、关联分析等以达到效果。 如前述IT运维挑战所说,完整、实时地采集以上数据是很不容易,且这类数据又被各种角色的人所关心,包括不限于: IT运维人员 开发人员 数据工程师 安全运维人员 合规审计人员 商务分析师 相关管理者、决策者 因此Gartner认为AIOps会从功能(Feature)演逐渐变成平台并落地,且预测到2022年年,40%企业会使⽤用AIOps。 2. 机器学习促进ITOps的主要方式 机器智能主要在如下4个场景下帮助ITOps完成目标: 增强描述性 通过算法、可视化与统计分析等方式,对海量数据(例如日志、告警)进行降噪、去重,增强其描述性。 增强排错 自动识别数据模式(例如日志模型),自动识别关键实体并关联事件。 增强预测能力 使用经典算法,基于模式预测。 增强辅助根因分析 通过关联、知识图谱获得可能原因。 总而言之,AIOps的最终目标时促进IT运维的目标达成,逐步释放IT运维人员的效率束缚,理想的未来,甚至被认为是服务Level-0(第一道关)的存在: 二. 工程难点 以下将AIOps系统拆解,并逐个从数据采集、数据中台、智能算法、自动化等角度分析其工程难点。 基本架构 简单描述,一个典型的AIOps的系统可以划分为如下层次: 更进一步,借鉴热门AIOps创业公司Moogsoft的一张图: 自上而下的划分,可以看到如下子系统: 场景应⽤:基于AIOps的通用性,场景不仅可以是运维、也可以是审计、DevOps、商务分析等。 智能监测系统:AIOps引擎,处理大数据并使用AI、分析从4个方面增强IT运维。 自动化系统:自动处理工单、协同沟通、定位问题的编排系统,自动治愈修复也在其中。 工单知识库:工单系统是重要的输入源,也是历史类似问题处理的知识库。 数据湖:不同于传统数据库或数据仓库,这里是一个无结构模型依赖、实时提供服务的数据湖。 监控生态系统:各类监控类系统,输出的数据,也包括告警。 数据源: 底层各种数据源系统,被监控的系统都在这里,例如主机、服务、云平台等。 数据采集 没有大数据,AIOps就如巧妇难为无米之炊,实时、有效、完整地数据采集是AIOps的基础前提。 1. 数据的摄取挑战 如前面IT运维的挑战类似,数据采集是很困难的,随着技术架构的演变,数据有3V((大容量、常变化、高速增长))的特点、以各种格式(Log、Tracking、Event;Metrics、IoT data;⽹网络数据)存在于各种来源( SaaS、多云、容器器、微服务、主机、应⽤用等)中。 2. 数据类型比较 各种类型的数据之间也有各自的特点,如下: 可见Tracking数据价值大,但采集难度较大;日志类的价值普遍更高;指标类数据简单,但数据量也可能非常大(IoT场景下);文本内的价值能力最大,但加工难度最大。 另一方面,数据之间也有一定的重叠(数据量仅供参考): 某种程度上,在一些产品中,tracking、指标类数据被认为是一种结构化的日志。 数据中台的处理 没有数据中台,AIOps如空中楼阁,我们看下数据中台具体是什么,又要做什么: 1. 海量多样数据的存储/索引 数据中台要能够有效存储和索引各类数据(时序指标数据、⽂文本数据、⽇日志、⽹网络数据、Tracking等)。这里有效指的是,针对前述数据类型的特点,有针对性的提高存储、检索效率(例如时序数据的索引与日志类的索引是有所不同的)。 2. 各种分析的⽀支持 还需要支持流式(或微批实时)分析处理,例如实时统计PV或异常告警。另一方面,也需要支持多维度的实时关联统计与分析,且支持交互式add-hoc方式。 3. 数据治理的支持 数据治理的能力也很重要,包括如下两个方面,数据加⼯:支持通⽤数据模型,且支持对多维机器数据、半结构化数据进行灵活的规整或与各种第三⽅数据关联(富化)等。数据⽣命周期管理:随时间流式,更老的数据需要降维、聚集或归档等,需要有这方面的支持。 机器学习的挑战 如前述,机器学习具体在如下几个场景发挥作用: 但算法落地面对一些直接的挑战: 数据不全,质量量欠佳:数据不全将导致算法模型变形,例如只用小猫图片训练,算法永远无法知道老虎长什么样。好在越来越多的团队意识到数据全面性的价值。 团队缺少懂的⼈:算法有一定的数学门槛,但这方面的专业懂的人才相对还是少一些。好在高薪的岗位吸引着越来越多的新鲜血液进入这个领域。 ⼯具不好⽤:算法工具目前越来越易用,不需要是计算机博士就可以做AI工作,但依然存在一定门槛。 工程化不易:目前AI被吐槽的地方就是难的推理结论不准,简单的推理结论显然。 外部数据集成与自动化 AIOps的最后一块重要系统是外部数据集成与自动化,这也是为什么一些大厂逐步在补齐方面的短板。这部分主要包括工单系统、CMDB(资产管理)的集成;Run Book自动化、告警与应用的编排能力。 三. 开源方案选择 这里介绍一些特定场景下特定的平台搭建的开源选择及策略: 日志类数据⽅方案 指标类时序数据⽅方案 其他OLAP选择 AI增强⽅方案 1. 数据源与监控 这里以容器化架构为例,自下而上可以看到多个层次的开源方案选择。 底层物理虚拟机层的监控由传统优势方案占主导,例如Zabbix、statsd、collectd、Nagios、fluentd等。 往上的容器类监控是Prometheus + Grafana + Thanos的领地。 上层应用的性能、日志类监控的选择是Elastic栈、TICK栈和Open Telemetry类方案。 2. 监控⽅案作为中台的能力比较 这里选择上面三类监控方案作为中台进行能力比较,维度参考前述挑战中的方面: 其中Prometheus和Tick方案对于指标类支持很好,而Elastic对于日志类支持更好。但流式分析方面,Prometheus和Elastic方案并无直接支持。而统计关联分析方面,Elastic较强,其他一般。在数据治理方面的支持,三个方面也都差强人意。总体而言,一个结论是:没有银弹。 以下大致介绍下相关方案的特点与优缺点。 3. Prometheus栈方案 3.1 指标类数据监控 - Prometheus Prometheus作为K8S监控标配(继K8S后第2个CNCF项⽬目),其有如下特点: 多维数据模型 + PromQL。 汇总性数据+Label过滤。 可从160+源渠道提取指标数据。 主动拉去模式(可由gateway被动)。 自动发现。 主要用于短期指标。 支持20+外部存储用于长期存储。 3.2 通⽤用指标类可视化 - Grafana 作为通⽤型指标类可视化⽅案,Grafana已然是Phrometheus的连体婴儿了,其有如下特点: 近70数据源(支持混合)。 新推简单⽇志方案:Promtail+Loki,目前还非常初级。 ⾃由报表定制与构建。 30+ 可视化插件。 ⽀持查询原始指标。 3.3 Prometheus的扩展 - Thanos 试图解决Prometheus的几个核心问题而生的方案,其有如下特点: 全兼容Prometheus,提供全局视图+HA。 扩展⾼可用。 Sidecar + Query节点。 ⻓时间备份与归档。 压缩与下采样(Down Sampling)。 4. Open Telemetry方案 是目前CNCF蓝图下统⼀Metric、Tracking的新标准(统一了Open Tracing和Open Census),目前还处于开发阶段,其主要是客户端标准。 Open Telemetry - SkyWalking SkyWalking作为国内流行Tracking方案,已经是Apache孵化项目,其有如下特点:• 性能影响较小(<10%)• Cloud Native容器化⽀持好• 支持存储到ES/TiDB、MySQL等 5. TICK栈方案 作为InfluxDB家的全家桶,其表示Telegraf + InfluxDB + Chronograf + Kapacitor。此方案具备如下一些特点: InfluxDB:⾼性能的时序数据库。与ElasticSearch比较报告号称有8X写⼊速度,少4X磁盘占用,以及3~7X响应速度。 Telegraf:数据采集客户端,支持200+数据渠道。 Chronograf:可视化方案,其没有Grafana强⼤和灵活。 此方案一个缺点是开源免费版本缺少集群、安全、管理等功能。 6. Elastic栈方案 作为Elastic家的全家桶,在日志与文本领域非常流行,常用BELK表示,意为Beats + Elasticsearch + Logstash + Kibana。此方案有如下特点: 接⼊层通常还会搭配Kafka使用。 重要企业级组件都在商业组件X-Pack中,包括安全、机器学习、SQL、监控、告警、Transform等 其附带了⼀个简单的开源免费的APM方案。 典型Elasticsearch部署方式是搭配Kafka使用,并在整个接入过程中使用Logstash(或Ingest Node)完成数据的转换(ETL),引⼊队列,主要是解决丢数据问题,但相应的部署、维护复杂度也较为复杂。 6.1 Elasticsearch核⼼能⼒ 作为Elastic核心数据库,Elasticsearch具有如下特点: 简单、易扩展、功能集丰富、生态活跃。 NoSQL、schema灵活。 全文索引查询强、过滤快、聚集功能强⼤。 不支持外部关联,有SQL适配器器(有限支持)。 但其缺点也较为明显: 企业特性需要商业License(x-pack)。 内存管理理挑战较大,复杂类统计DSL易失控。 超过百TB规模后运维成本⾼(非线性增长)。 存储压缩效率偏低。 6.2 Kibana核⼼能⼒ Kibana作为Elastic专属可视化方案,近几年迭代很快,其具有如下特点: 支持交互式查询控制台、类tail-f功能。 支持完整报表中⼼与交互功能。 提供⾼级图表功能:地图、关系图等。 支持时序数据。 支持机器学习(收费)。 提供Canvas⾃由编辑能力(支持暗黑主题,但挂接数据方式有限)。 6.3 Logstash核⼼能⼒ 作为ELK的主要接入与数据治理核心组件,Logstash具备如下特点: 插件化灵活:输⼊入/过滤/输出 200+插件 配置统⼀一管理理 数据传输可靠性 但其缺点也较为明显:占用资源较大,性能较差。 6.4 Beats核⼼能⼒ 为解决Logstash的性能问题,引入了Beats,提供8类轻量级Beats,具备如下特点: 配置统⼀管理,部分逻辑插件化 集成50+内置⽣态模块(⽇志与指标) ⽀持容器类部署场景 7. 其他OLAP选择 除了前述三种主要开源监控类方案外,这里也介绍一下其他部分流行OLAP的开源方案作为中台选择参考。 7.1 Druid Druid作为Apache项目,有如下特点: 性能优越: PB级别规模。 亚秒级OLAP系统。 实时写入与查询。 组件⻆色较多,搭建较为复杂。 Json-QL(有SQL适配器器)。 不⽀持外Join、窗口等。 7.2 ClickHouse 作为新起之秀,ClickHouse以其独门武功的卓越性能吸引了一大批用户,其具有如下特点: 性能优越: 10亿+条规模⽐比商业软件快5倍 ⽐比MySQL快⼏几百倍 稳定可靠,⾮非Hadoop体系, 类SQL功能 缺点: 聚合结果要在⼀一台机器器内存内 缺少完整更更新删除操作 ⽀支持操作系统有限 8. AI与自动化 关于AI方面以Python框架为主,而自动化方面选择较多,这里也列举一些Python的方案如下: 数据治理:Python ETL、PySpark、Flink/Blink-Python 机器学习:Airflow(编排)+ 如下机器学习框架 自动化:Ansible、Puppet等 以下列举一下具体的AI算法在场景下应用实例: 8.1 AI增强 - 降噪去重与模式识别 这里主要体现在机器数据的聚类(模式识别),例如对海量日志进行模式聚类(从65万条日志,聚类出50条日志模式),以下是一些方案样例: 8.2 AI增强 - 异常值(Outlier)与异常识别(Anomaly) 传统Ops平台普遍存在告警疲劳,因为传统基于阈值方式的告警并不能很好解决问题: 阈值难以合理,例如CPU高于90%在某些情况下合理,某些又不正常。 基于经验的有效阈值会⾮常复杂:例如外网非工作时间频繁登录失败是异常,但公司内工作时间相对容忍度较高。 有效阈值维护成本较⾼:经常因情况变化要调整。 过滤后的告警数量依然较多。 比较好的方法策略是以历史数据作为基准,对周期性或断层数据异常进⾏识别: 使⽤基于统计的动态阈值。 使⽤模式识别正常行为与异常行为。 如下是一些方案样例: 8.3 AI增强 - 预测 对周期性趋势性数据进行预测,一些方案样例: 8.4 AI增强 - 根因分析 关联事件上下⽂文与相关链路路指标,提供根因辅助,一些方案样例: 四. 结语 开源⽅案选择策略 使用开源方案时需要考虑几点: 1. 没有银弹,没有one for all 没有一套方案完美解决所有问题,需要权衡选择合适的方案组合,但也不要万花筒。 2. 开源 ≠免费 开源的东西不等于免费,因为如下几点原因: 学习、迁移、维护升级、稳定性、License、潜在坑(例如这个长长的breaking change清单)等。 某些开源软件的重要扩展是需要额外费用的(例如InfluxDB的集群功能)。 需要结合团队、技术需求、⽅案选择做细致评估。商业软件或SaaS方案(简化Ops平台⾃自身运维成本,例如阿里云日志服务)也可作为选项。 其他 PPT下载: https://github.com/wjo1212/ChinaPyCon2019另外,我们一直在提供并发展云上AISecDevOps平台,欢迎扫群加入日志服务技术交流钉钉群, 获得第一手资料与支持:
阿里云日志服务作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。目前发布日志审计服务功能,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。 背景 日志审计是法律刚性需求 无论海内外,尤其国内自从2017年《网络安全法》还是今年《等保2.0》的即将与12月实施,企业落实日志审计变得越迫切. 日志审计是客户安全合规依赖基础 很多企业自身有很成熟的法规条例以及合规审计团队,针对账号设备操作,网络行为进行审计,也需要对日志审计。一方面客户有成熟的内部合规团队,可以直接消费原生各类日志;客户也可以直接使用日志审计服务提供的审计支持,直接构建并输出合规审计信息;如果客户有安全中心(SOC)可以直接消费日志审计中日志,也可以直接使用阿里云安全中心。 日志审计是安全防护的重要一环 根据FileEye M-Trends 2018报告,企业安全防护管理能力薄弱,亚太地区尤甚间,企业组织的攻击从发生到发现所需时长平均101天,而亚太平均需要498天;其中发现后的验证平均需要58天。很显然,需要长期、可靠、无篡改的日志记录与审计支持下,来持续缩短这个时间。 日志服务与日志审计服务App 日志服务与审计场景 SLS作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。一直很好的支持日志服务场景相关场景:DevOps、运营、安全、审计. 典型日志审计场景 根据我们支持客户的反馈来看,日志审计可以分成如下4层需求,越往上越高级。 说明 基础需求是大部分中小企业客户需要的自动化采集存储日志的功能,他们主要的诉求是满足等保2.0最低的需求,并脱离手工去维护。 高级需求的是跨国公司、大公司以及部分中型客户,他们往往多个部门之间独立结算,并且在使用阿里云的账号上各自隔离。但是在审计的时候,却需要自动化的统一采集相关日志,他们的主要诉求除了上述外,额外需要能够中心化的采集日志、并支持多个账号的简单管理。这部分公司往往有自己的审计系统,因此对日志审计的需求是能够实时、简单的对接。 再高一级的需求是有专门合规团队的大公司,他们需要针对日志进行监控、告警和分析的需求,一部分客户可以直接采集同步数据到期专门的审计系统中去操作. 一部分尤其是计划在云上搭建一套新的审计系统的客户, 可以直接使用日志服务提供的审计支持(查询, 分析, 告警, 可视化等)进行审计操作. 最顶上的客户往往是拥有专业成熟审计合规团队的大公司, 一般其都拥有自己的一套SOC或审计系统, 核心需求是对接数据进行统一操作. 针对以上4类客户, 日志服务的日志审计服务都可以比较好的满足. 日志审计服务App 日志审计服务以日志服务的App形式存在,目前App免费,数据存储、读写流量等按标准按量收费。提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。 产品技术功能与优势 优势 完整数据采集:产品覆盖所有审计相关日志自动化采集(可接入20+产品) 配置简单与自动化:跨多主账号、自动实时发现新资源并实时采集。一键式配置(一期支持6个)。 丰富建模与分析功能:借助SLS查询分析、加工、报表、告警、导出等功能,完整支持审计场景下分析告警对接需求 低成本存储:利用对象存储、冷备等介质保证低成本 丰富生态:与开源、阿里云大数据、第三方SOC软件无缝对接,充分发挥数据价值 功能 区域与产品覆盖 发布区域 登录入口: 中国站(目前邀测阶段,申请入口,需登录 国际站(即将发布) 数据区域: 数据采集区域目前支持公共云所有区域 同步到中心化目标区域,目前支持北京、上海、杭州、深圳等区域以及新加坡 覆盖产品支持接入支持如下产品(一键式接入的为蓝色): 快速开始 1. 开通 首先开通日志服务的日志审计功能. 2. 首次配置 进入App后的首次配置页面, 选择中心化存储的区域, App会在保存后自动构建一个固定模式名字的项目. 选择开启相关的产品, 并参考页面提示授权, 之后保存. 3. 多账户配置 在多账户配置的全局配种, 配置其他多个主账号, 将其日志也统一收集到当前中心化项目中. 4. 统一查询与内置报表查看 在左侧菜单中选择某一类数据进行跨账号的统一搜索, 也支持多个产品之间的协同查询与分析以及内置报表. 5. 其他审计操作 可以在日志服务首页直接定位到中心项目, 进行扩展的审计操作. 针对数据交互式分析, 请参考SQL统计功能. 构建自定义报表, 请参考仪表盘操作 订阅报表并定期发送, 请参考报表订阅 指定规则并自定义告警, 请参考告警操作 清洗数据做统一分析或输出. 请参考数据加工 通过接口对接SOC或其他系统, 请参考数据投递 更多参考 产品发布页宣传。 产品直播 日志服务手册与Demo 扫群加入日志服务技术支持钉钉群, 获得第一手资料与支持:
概述 使用全局富化函数做富化时, 需要传递一个字典或者表格结构做富化. 参考构建字典与表格做数据富化的各种途径比较. 本篇介绍从使用资源函数res_log_logstore_pull从其他logstore拉取数据的做富化的详细实践.关于res_log_logstore_pull的参数说明, 参考这里. 该语法目前支持两种模式去logstore拉取数据,一种是拉取指定时间间隔内的logstore 的数据内容,另外一种是不设置结束时间,持续的拉取目标logstore内容 背景 这里我们有两个logstore,一个是存储个人信息的source_logstore,一个是酒店存储客人入住信息的target_logstore ,我们现在将酒店的入住信息拿来做富化。注意: 这里采用pull_log接口拉取数据, 富化的logstore并不依赖索引. 个人信息 source_logstore topic:xxx city:xxx cid:12345 name:maki topic:xxx city:xxx cid:12346 name:vicky topic:xxx city:xxx cid:12347 name:mary 酒店入住信息logstore time:1567038284 status:check in cid:12345 name:maki room_number:1111 time:1567038284 status:check in cid:12346 name:vicky room_number:2222 time:1567038500 status:check in cid:12347 name:mary room_number:3333 time:1567038500 status:leave cid:12345 name:maki room_number:1111 基本语法 res_log_logstore_pull( endpoint, ak_id, ak_secret, project, logstore, fields, from_time=None, to_time=None, fetch_include_data=None, fetch_exclude_data=None, primary_keys=None, delete_data=None, refresh_interval_max=60, fetch_interval=2): 具体的参数说明请参考res_log_logstore_pull,需要注意的地方是,res_log_logstore_pull 是一个单独的语法,只负责从目标logstore 拉取数据下来,本身自己并没有做任何富化的操作,所以请不要单独使用res_log_logstore_pull语法,结合e_table_map和e_search_table_map语句一起使用才是有意义的,本篇也会结合e_table_map和e_search_map_table的使用给出一些例子进行演示。 场景1: 获取指定时间内所有的数据 注意: 这里的时间是日志获取时间. DSL编排语法 res_log_logstore_pull(..., ["cid","name","room_number"],from_time=1567038284,to_time=1567038500) 获取到的数据 #这里我们的语法中 field 填入了 cid,name,room_number 三个字段,,并且指定了时间范围,将会获取这个时间范围内的logstore的所有数据的这三个字段的值 cid:12345 name:maki room_number:1111 cid:12346 name:vicky room_number:2222 cid:12347 name:mary room_number:3333 cid:12345 name:maki room_number:1111 场景2: 设置黑白名单参数来过滤拉取的数据 1.DSL 编排语法(只设置白名单) # 设置白名单,只有 room_number 值等于 1111的的数据会被拉去下来 res_log_logstore_pull(..., ["cid","name","room_number","status"],from_time=1567038284,to_time=1567038500,fetch_include_data="room_number:1111") 获取到的数据 # 设置了 ferch_include_data 白名单,只有包含 room_numver:1111的数据会被拉去下来,其他数据不会被拉取。 status: check in cid:12345 name:maki room_number:1111 status:leave cid:12345 name:maki room_number:1111 2.DSL 编排语法(只设置黑名单) res_log_logstore_pull(..., ["cid","name","room_number","status"],from_time=1567038284,to_time=1567038500,fetch_exclude_data="room_number:1111") 获取到的数据 # 设置黑名单 fetch_exclude_data 当数据包含 room_number:1111的时候丢弃这条数据。 status:check in cid:12346 name:vicky room_number:2222 status:check in cid:12347 name:mary room_number:3333 3.DSL编排语法(同时设置黑白名单) res_log_logstore_pull(..., ["cid","name","room_number","status"],from_time=1567038284,to_time=1567038500,fetch_exclude_data="status:leave",fetch_include_data="status:check in") 获取到的数据 # 黑白名单同时存在的情况下,优先进行黑名单数据的匹配,这里我们填入的是 status:leave的值,当数据包含status:leave的值时候,数据会被直接丢弃,而后匹配白名单,白名单我们填入的是 status:check in 当数据包含 status: check in 的值时候,该数据才会被拉取下来. status:check in cid:12345 name:maki room_number:1111 status:check in cid:12346 name:vicky room_number:2222 status:check in cid:12347 name:mary room_number:3333 场景3: 开通持续拉取目标logstore 数据 DSL编排语法 如果目标logstore 的数据是持续写入,我们需要持续的去拉取时候,设置 to_time 参数为None 就可以,同时可以设置fetch_interval 设置拉取的时间间隔,和refresh_interval_max 当拉取遇到错误的时候退火重试的最大时间间隔 res_log_logstore_pull(..., ["cid","name","room_number","status"],from_time=1567038284,to_time=None,fetch_interval=15,refresh_interval_max=60) # 需要注意的是,在持续拉取的过程中,如果遇到错误,服务器会一直退火重试,直到成功为止,不会停止数据加工进程。 场景4: 开启主键维护拉取的目标logstore数据(暂时不推荐) 注意事项 目前该功能仅限使用所有数据存储在logstore的同一个shard中,所以暂时不推荐使用该功能。 背景 以我们的个人信息logstore 和 酒店信息logstore的数据进行举例,因为logstore和数据库不同,logstore中的数据只能写入,无法删除,所以有的时候我们希望匹配的时候将已经删除的数据不要进行匹配,这时候就需要开启主键维护功能。 需求演示 现在我们想拉取酒店信息logstore中,所有入住还没有离开的客人信息,当status=leave的时候,表示客人已经离开酒店,所以不需要将该信息进行拉取。 DSL编排语法 res_log_logstore_pull(..., ["cid","name","room_number","status","time"],from_time=1567038284,to_time=None,primary_keys="cid",delete_data="status:leave") 得到的数据 ## 可以看到 name为maki 的客人的最后更新status为leave ,已经离开酒店,所以并没有将 maki的数据拉取下来, time:1567038284 status:check in cid:12346 name:vicky room_number:2222 time:1567038500 status:check in cid:12347 name:mary room_number:3333 注意 需要注意的是 primary_keys 目前只支持设置单字符串,这个需要设置logstore数据中 值为唯一的字段,比如样例中的cid , 类似数据库的唯一主键,并且当设置primary_keys的时候,delete_data 也必须不为 None,这样才有意义。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
本实践案例主要是从字符串专题进行展开,将从多方面讲解如何使用不同方案字符串,来解决用户需求。 一个快速案例: 解析URL中参数键值对 以下是一个url的数据,本文将会使用两种方案来展开这条日志内容: request: https://yz.m.sm.cn/s?ver=3.2.3&app_type=supplier&os=Android8.1.0 需求 1、对以上日志进行解析出proto, domain, param等内容2、对param中的键值对值做展开操作 原始日志 比如在控制台上收集到的日志为: __source__: 10.43.112.168 __tag__:__client_ip__: 12.120.75.130 __tag__:__receive_time__: 1563517113 __topic__: request: https://yz.m.sm.cn/video/getlist/s?ver=3.2.3&app_type=supplier&os=Android8.1.0 LOG DSL编排 Grok模式+KV方式 1、首先是使用grok模式对字段request内容进行解析(也可使用正则,参考grok函数和grok模式参考) e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) 预览处理日志: uri_domain: yz.m.sm.cn uri_param: /video/getlist/s?ver=3.2.3&app_type=supplier&os=Android8.1.0 uri_proto: https 2、其次在使用grok模式对字段uri_param解析 e_regex('uri_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}")) 预览处理日志为: uri_path: /video/getlist/s uri_query: ver=3.2.3&app_type=supplier&os=Android8.1.0 3、第三步具体操作如下: e_kv("uri_query") 预览处理后日志: app_type: supplier os: Android8.1.0 ver: 3.2.3 4、综上,LOG DSL规则可以如一下形式 # 初步处理解析request内容 e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) # 其次处理解析uri_param e_regex('uri_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}")) # 展开kv形式 e_kv("uri_query") 预览处理后日志: __source__: 10.43.112.168 __tag__:__client_ip__: 12.120.75.130 __tag__:__receive_time__: 1563517113 __topic__: request: https://yz.m.sm.cn/video/getlist/s?ver=3.2.3&app_type=supplier&os=Android8.1.0 uri_domain: yz.m.sm.cn uri_path: /video/getlist/s uri_proto: https uri_query: ver=3.2.3&app_type=supplier&os=Android8.1.0 app_type: supplier os: Android8.1.0 ver: 3.2.3 注意:假如只需要第二个需求可以直接对字段request使用e_kv函数,就能展开kv数据(具体参考e_kv函数)。如下: e_kv("request") 预览处理后日志: __source__: 10.43.112.168 __tag__:__client_ip__: 12.120.75.130 __tag__:__receive_time__: 1563517113 __topic__: request: https://yz.m.sm.cn/video/getlist/s?ver=3.2.3&app_type=supplier&os=Android8.1.0 app_type: supplier os: Android8.1.0 ver: 3.2.3 其他方案 都是以url: https://yz.m.sm.cn/video/getlist/s?ver=3.2.3&app_type=supplier&os=Android8.1.0为例, 如果要提取其中动态字段ver, app_type和os等, 其实还有多种方案, 如下: 使用正则 e_regex("url", r"\b(\w+)=([^=&]+)", {r"\1": r"\2"}) 使用e_kv_delmit函数 e_kv_delimit("url", pair_sep=r"?&") 综上可知,对于大部分url函数形式,都可使用以上几种方式进行展开。但是针对以上url形式,其实使用e_kv函数已经足够,清晰明了,而且形式简单。 方案比较 方案 关键字提取 值提取 关键字加工 值加工 e_kv 使用特定正则 支持默认的字符集+特定分隔符或者带"分隔 支持前后缀 支持文本escape e_kv_delimit 使用特定正则 通过分隔符 支持前后缀 默认没有 e_regex 自定义正则+默认字符集过滤 完全自定义 自由(例如重复, 中间加, 或者把value也放入key的都支持) 自由(如左) 关键字提取 e_kv、e_kv_delimit,e_regex在使用的关键字提取的时候都遵循字段名提取值约束,以下两个示例都可以使用这三种方法进行提取: 1、案例1 以k1: q=asd&a=1&b=2&__1__=3为例,如果要对以上日志格式做关键字和值提取的话,三种方案如下 # 默认以特定字符集提取关键字 e_kv("k1") # 以&分隔键值后, 用&分隔提取出关键字 e_kv_delimit("k1", pair_sep=r"&") # 自行指定字符集提取关键字和值 e_regex("k1",r"(\w+)=([a-zA-Z0-9]+)",{r"\1": r"\2"}) 经过以上DSL编排过后的日志格式为: k1: q=asd&a=1&b=2 q: asd a: 1 b: 2 注意到, 并没有提取出关键字__1__因为其不符合字段名提取值约束. 2、案例2以content:k1=v1&k2=v2?k3:v3为例, 有点特殊需要特定正则提取关键字,三种方案如下: e_kv("content",sep="(?:=|:)") e_kv_delimit("content",pair_sep=r"&?",kv_sep="(?:=|:)") e_regex("content",r"([a-zA-Z0-9]+)[=|:]([a-zA-Z0-9]+)",{r"\1": r"\2"}) 注意: 给参数pari_sep、kv_sep或者sep传递字符集的时候, 需要使用正则的不捕获分组, 形式如(?:字符集)。 经过DSL编排之后的日志格式是: content:k1=v1&k2=v2?k3:v3 k1: v1 k2: v2 k3: v3 3、案例3以下格式的字符串比较复杂,使用e_regex提取更方便。 content :"ak_id:"LTAiscW,"ak_key:"rsd7r8f 如果要提取的字符串的关键字前有"需要使用e_regex来提取。 e_regex("str",r'(\w+):(\"\w+)',{r"\1":r"\2"}) 经过DSL编排语法后,日志为: content :"ak_id:"LTAiscW,"ak_key:"rsd7r8f ak_id: LTAiscW ak_key: rsd7r8f 值提取 动态键值对之间以及关键字与值之间有明确标识如: 1、日志格式为a=b或a="cxxx"形式的推荐用e_kv函数: content1: k="helloworld",the change world, k2="good" 在这种情况下使用e_kv函数就可以,提取内容不包括 the change world这几个词 e_kv("content1") # e_kv_delimit函数写法,特别注意k2前有空格,所以e_kv_delimit函数的pair_sep参数需要使用`,\s`才能正常解析,否则解析不出来k2 e_kv_delimit("content1",kv_sep="=", pair_sep=",\s") # e_regex函数写法 e_regex("str",r"(\w+)=(\"\w+)",{r"\1": r"\2"}) 提取后的日志为: content1: k="helloworld",the change world, k2="good" k1: helloworld k2: good 但是对于不同的场景,以上三种有不同的合适使用场景。比如: 2、而以下形式的带"的日志格式content:k1="v1=1"&k2=v2?k3=v3,如果使用e_kv函数提取会比较容易,如下: e_kv("content",sep="=", quote="'") 处理后日志为: content: k1='v1=1'&k2=v2?k3=v3 k1: v1=1 k2:v2 k3:v3 而使用e_kv_delimit函数做提取,使用的规则e_kv_delimit("ctx", pair_sep=r"&?", kv_sep="="),只能解析出k2: v2和k3: v3, 因为其中第一个提取的键值对中关键字是k1="v1,不符合关键字的规范会被丢弃。 3、比如以下分隔符的键值对中值包含了特殊字符但没有用特定字符括起来的时候,适合e_kv_delimit函数的数据形式. 例如: content: 老鼠吃大米,油|小鸡吃虫子,稻子|小猫吃鱼,老鼠| 此时使用e_kv_delimit函数比较合适。 e_kv_delimit("content", pair_sep="|", kv_sep="吃") 处理后日志为 content: 老鼠吃大米,油|小鸡吃虫子,稻子|小猫吃鱼,老鼠| 小猫: 鱼,老鼠 小鸡: 虫子,稻子 老鼠: 大米,油 而使用e_kv解析会无法解析完整 e_kv("f1", sep="吃") 处理后日志为 content: 老鼠吃大米,油|小鸡吃虫子,稻子|小猫吃鱼,老鼠| 小猫: 鱼 小鸡: 虫子 老鼠: 大米 关键字加工 1、e_kv和e_kv_delimit函数都可以通过prefix="", suffix=""对关键字和值进行加工,示例如下:原始日志: k1: q=asd&a=1&b=2 加工编排 e_kv("k1", sep="=", quote='"', prefix="start_", suffix="_end") e_kv_delimit("k1", pair_sep=r"&", kv_sep="=", prefix="start_", suffix="_end") e_regex("k1",r"(\w+)=([a-zA-Z0-9]+)",{r"start_\1_end": r"\2"}) 加工后的数据都是关键字加工形式,如下: k1: q=asd&a=1&b=2 start_q_end: asd start_a_end: 1 start_b_end: 2 e_regex对关键字加工的能力更强, 例如将关键字重复(仅用于举例): e_regex("k1",r"(\w+)=([a-zA-Z0-9]+)",{r"\1_\1": r"\2"}) 加工后的数据都是关键字加工形式,如下: k1: q=asd&a=1&b=2 q_q: asd a_a: 1 a_a: 2 值加工 1、日志格式为k1:"v1\"abc",值内容有双引号的情况,只有e_kv可以正常提取出,并且使用escape参数。其他两种比较难实现 """ 这里的\只是普通的符号,不是转义符 """ content2: k1:"v1\"abc", k2:"v2", k3: "v3" 使用e_kv规则为: e_kv("content2",sep=":", quote='"') 提取后的日志为: content2: k1:"v1\"abc", k2:"v2", k3: "v3" k1: v1\ k2: v2 k3: v3 e_kv提供对于\字符的转义支持, 通过参数escape打开, 如下: e_kv("content2",sep=":", quote='"',escape=True) 提取后的日志为: content2: k1:"v1\"abc", k2:"v2", k3: "v3" k1: v1"abc k2: v2 k3: v3 2、日志格式为a='k1=k2\';k2=k3'特殊形式的日志,只有e_kv正常提取出,其他两种比较难以实现 data: i=c10 a='k1=k2\';k2=k3' 默认情况下e_kv函数的escape=False,结果为: e_kv("data", quote="'") 提取后的日志为: a: k1=k2\ i: c10 k2: k3 e_kv提供对于\字符的转义支持, 通过参数escape打开, 如下: e_kv("data", quote="'", escape=True) 提取后的日志为: data: i=c10 a='k1=k2\';k2=k3' i: c10 a: k1=k2';k2=k3 综上,对于e_regex,e_kv_delimit函数,提取出以上形式的日志格式比较困难。值加工推荐使用e_kv函数。 4、键值的复杂加工 content: 老鼠吃大米|小鸡吃虫子|小猫吃鱼| e_regex("content", r"\b([\u4e00-\u9fa5\u0800-\u4e00\w]+)吃([^\|]+)", {r"\1": r"\2被\1吃"}) 处理后日志为 content: 老鼠吃大米|小鸡吃虫子|小猫吃鱼| 小猫: 鱼被小猫吃 小鸡: 虫子被小鸡吃 老鼠: 大米被老鼠吃 结论 大部分键值对的提取使用e_kv并配置特定参数就可以很好满足, 尤其是带括字符和反斜杠需要提取并转义时. 其他复杂或高级的场景时, 可以用e_regex来提取. 某些特定场景下的键值对, 使用e_kv_delemit会跟简单. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 日志服务数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 完整DSL语法介绍与参考PDF下载(持续更新) 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
分享介绍日志服务数据加工功能提供托管的自动伸缩的实时可靠数据加工服务,本次系列培训从实战案例角度完整介绍数据加工的主要场景. 包括功能语法、数据分发、结构与非结构化数据清洗、外部资源关联、并发可靠性与排错等,解决实日志数据在接入、分析、投递、对接时存在各种数据加工需求与问题。 第一讲——数据加工介绍与实战 直播时间: 8月7日 19:30-20:30讲解人: 丁来强 (成喆)直播介绍: : 介绍数据加工的各种痛点, 场景以及解决方案. 介绍与演示日志服务数据加工的功能, 概念, 原理, 控制台操作, 以及SLB数据加工实战等视频回顾: https://yq.aliyun.com/live/1339PPT下载: https://yq.aliyun.com/attachment/download/?id=6958 第二讲——DSL核心语法介绍 直播时间: 8月8日 19:30-20:30讲解人: 丁来强 (成喆)直播介绍: : 介绍与演示日志服务数据加工的核心与发的框架. 深入浅出的覆盖基础语法, 数据结构, 30+全局函数概述, 200+表达式函数概述, 以及类SLS/Lucene的查询字符串语法视频回顾: https://yq.aliyun.com/live/1340PPT下载: https://yq.aliyun.com/attachment/download/?id=6959 第三讲——DSL语法实践 直播时间: 8月13日 19:30-20:30讲解人: 丁来强 (成喆)直播介绍: : 逐步深入介绍日志服务的事件字段提取模式, 正则表达式实践, 400+GROK模式概览, 灵活强大的JMES语法. 以及ETL核心场景最佳实践: 函数调用、事件判断、日期时间处理等视频回顾: https://yq.aliyun.com/live/1341PPT下载: https://yq.aliyun.com/attachment/download/?id=6960 第四讲——动态数据分发汇集实践 直播时间: 8月14日 19:30-20:30 讲解人: 唐恺 (风毅)直播介绍: : 介绍与演示数据加工对于跨账号多目标logstore数据分发. 以及跨账号多源logstore数据汇总的场景视频回顾: https://yq.aliyun.com/live/1342PPT下载: https://yq.aliyun.com/attachment/download/?id=6961 第五讲——非结构化数据解析实践 直播时间: 8月20日 19:30-20:30讲解人: 唐恺 (风毅)直播介绍: : 通过多个实战案例介绍日志服务数据加工如何全面灵活的支持非结构化数据的解析. 包括: 解析标准syslog协议框架、使用正则表达式与grok解析Ngnix日志、动态键值对KV加工, 特定格式的、特定格式文本的数据加工等视频回顾: https://yq.aliyun.com/live/1343PPT下载: https://yq.aliyun.com/attachment/download/?id=6962 第六讲——结构化化数据解析实践 直播时间: 8月21日 19:30-20:30讲解人: 唐恺 (风毅)直播介绍: : 通过多个实战案例介绍日志服务数据加工如何全面灵活的支持非结构化数据的解析. 包括:复杂JSON格式加工、多子键为数组的复杂JSON、多层数组对象嵌套的复杂JSON, 解析CSV格式日志等视频回顾: https://yq.aliyun.com/live/1344PPT下载: https://yq.aliyun.com/attachment/download/?id=6963 第七讲——数据映射富化实践 直播时间: 8月28日 19:30-20:30讲解人: 唐恺 (风毅)直播介绍: : 通过多个实战案例介绍日志服务数据加工对于数据富化的支持. 介绍如何从RDS-MySQL、Logstore、OSS等外部资源自动化加载数据支持各种维表更新与数据富化场景。也覆盖特定复杂逻辑映射的实战内容。视频回顾: https://yq.aliyun.com/live/1349PPT下载: https://yq.aliyun.com/attachment/download/?id=6964 第八讲——可靠性性与排错实践 直播时间: 8月29日 19:30-20:30讲解人: 丁来强 (成喆)直播介绍: : 深入浅出介绍介绍如何构建健壮性数据加工任务. 从异常错误监控, 到异常错误排查框架流程, 通过实战覆盖数据加工各个阶段的错误排查手段, 包括: 初始化错误排查, 读取错误, 加工错误, 输出错误等视频回顾: https://yq.aliyun.com/live/1350PPT下载: https://yq.aliyun.com/attachment/download/?id=6965 进一步技术交流 加入日志服务云栖技术交流群: 阿里云开发者群:
通用错误排查步骤数据加工任务启动后,加工结果根据路由规则发送到对应的Logstore。如果加工任务失败, 目标logstore没有日志产生或者加工延迟过大等异常,可以通过如下方式排查:1) 确认目标logstore是否有写入检查目标logstore最近是否有日志写入. 正确的方法是如下操作目标Logstore的写入预览:注意通过logstore的查询的方式可能并不准确, 因为:数据加工是基于日志接收时间进行加工, 可能正在加工历史日志, 当前查询的时间范围与日志的写入时间并不一定一致.日志服务在写入历史日志时的索引到可以查询有需要一定延迟(通常几分钟). 数据加工如果正在写入历史日志, 那么查询界面立刻查询可能查询不到.2) 查看加工任务状态查看当前任务的进度, 尤其是是否已经启动, 参考 任务管理。对于时间区间固定的任务,加工到结束位置之后会自动结束。查看对应任务的消费组, 检查其是否状态是否开启并有所更新.参考错误日志查看方式, 查看是否有异常发生. 在参考错误排查进一步找到原因并解决.3) 查看源logstore是否有数据产生通过查看加工任务, 确认当前加工任务的时间范围内, 源logstore是否存在日志.如果是时间配置没有设置结束时间, 检查源Logstore是否有新日志产生,如果没有新日志产生, 且特定时间范围内没有日志且没有历史日志存在,加工无法进行。如果选择的是历史时间, 确认历史特定时间范围内, 源logstore是否有日志.正确的检查方法如下:点击加工任务的修改规则, 选择对应的时间范围来查看是否有原始日志.4) 查看加工规则是否异常检查加工规则代码中是否存在异常, 例如:修改了日志的时间,导致在当前时间范围内查询不到。或者加工规则在特定条件下丢弃了日志.例如如下代码会丢弃所有字段name不存在或者为空的日志, 而前置逻辑中是构建字段name, 如果前置逻辑存在问题, 导致字段name没有正确构建的话, 所有日志都不会产出.# ....前置逻辑. # .... 构建字段name... e_keep(e_search('name: "?"'))如果存在从第三方拉取数据做富化的逻辑, 需要确认是否第三方的数据过大, 数据加工任务正在处于初始化状态, 导致迟迟未能开始消费数据.例如如下:e_dict_map(res_rds_mysql(..database="userinfo", table="user"), "username", ["city", "school", "age"])正确的检查方法如下:点击加工任务的修改规则, 选择对应的时间范围, 并点击预览来查看结果.如果能够重现, 可以通过注释掉特定语句重试预览来调试.5) 确认shard数量是否符合预期如果发现加工速度过慢, 可以考虑是否源logstore与目标logstore的规划不符合性能预期, 建议参考性能优化指南来增加源logstore或目标logstore的shard数量.错误日志查看方式错误日志可以在如下几个地方查询:1)在Logstore internal-etl-log 中加工任务产生的日志会存储在internal-etl-log 这个Logstore中(该Logstore在执行数据加工任务后由系统自动生成)internal-etl-log属于专属logstore, 不会收取任何费用, 用户无法修改其配置, 也无法写入其他数据,该Logstore在执行数据加工任务后由系统自动生成.internal-etl-log中每个日志事件的__topic__字段显示了加工任务的状态,可通过该字段判断对应的加工任务是否产生了错误。具体的错误信息在每一个日志事件的message和reason字段查看,如下图所示2)在数据加工诊断仪表盘的异常详情处进入诊断仪表盘的方式参考数据加工诊断仪表盘 具体的错误信息在异常详情的reason列,如下图所示3)预览阶段错误日志会直接显示在控制台预览阶段只是模拟加工规则的操作,以及预览预期的加工结果,并不会真的对源logstore和目标logstore进行修改,因此预览阶段遇到的各类错误都不会对源日志事件产生影响(不会丢弃、修改日志等)。预览限制预览阶段的数据加工相较于真实的ETL加工任务而言,存在一些限制:1)不能发现源logstore秘钥权限的部分缺失预览阶段不会创建消费组进行消费,因此消费组权限没有进行检查。2)不能发现加工规则中输出目标的名称错误预览阶段不会对写入目标做真正的写入操作,因此不会检查配置项中是否配置了相关的输出目标。3)不能发现输出目标的配置错误这里的配置错误包含输出目标的 project/logstore/秘钥权限等信息配置错误。因为预览阶段不会对写入目标做真正的写入操作,因此不会检查输出目标的配置信息是否正确。4)不能完整覆盖所有数据预览阶段默认只从源logstore中拉取1000条数据进行加工,不会覆盖所有数据。如果拉取的1000条数据在加工后没有加工结果产生(数据都被加工规则过滤掉了),则会持续拉取数据五分钟,直到有加工结果产生为止。错误排查概述当发生错误时, 分析错误产生在数据加工任务的哪个环节,能帮助用户更高效地定位错误位置。根据数据加工原理, 数据加工任务的主要四个环节如下图所示以上四个环节的每一个环节都有可能产生错误,其原因、影响和排查方式各不相同。启动加工引擎该环节产生错误主要是由于在启动加工引擎过程, 检测到用户编写的LOG DSL规则存在错误,导致加工引擎内部的安全检查(security check)不通过。在该阶段产生错误时,加工任务会一直重试,直到被手动停止或重试成功。如果重试成功(修改为正确的加工规则),加工任务会继续正常工作,不会产生日志的丢失或冗余(重复)。该环节的错误排查指南参考加工引擎启动错误排查指南。读取源logstore数据该环节产生的错误主要是由于对源logstore的访问异常。访问异常的原因可能是源logstore信息配置错误、网络错误、源logstore信息发生变化等。在该阶段产生错误时,加工任务会一直重试,直到被手动停止或重试成功。如果重试成功,加工任务会继续正常工作,不会产生日志的丢失。如果是已经读取部分数据后才报错,会保存断点并一直重试,重试成功后,从断点处继续读取,不会有数据的丢失与重复. 如果重试过程中停止, 不会有数据丢失与重复.该环节的错误排查指南参考源logstore日志数据读取错误排查指南。加工日志事件该环节产生错误主要是由于在数据加工过程中, 部分(也可能是全部)日志事件不适配加工规则,从而引发的错误。在该阶段产生错误时,不适配加工规则的日志事件会丢弃(跳过),加工后的输出结果中将不包含这些日志事件。该环节的错误排查指南参考日志事件加工错误排查指南。输出到目标logstore(s)该环节产生错误主要是由于对目标logstore的访问异常。访问异常的原因可能是由于目标logstore信息配置错误、网络错误、目标logstore信息发生变化等原因。在该阶段产生错误时,加工任务会一直重试,直到被手动停止或重试成功。如果重试成功,加工任务会继续正常工作,不会产生日志的丢失。如果是已经输出部分数据后才报错(例如配置了多个目标, 一个目标成功, 另外一个目标失败),会保存断点并一直重试,重试成功后,不会有数据的丢失与冗余. 但如果这时停止加工任务, 再重启加工时, 会从断点继续, 不会有数据丢失, 但可能会有数据的冗余(重复输出)。该环节的错误排查指南参考目标logstore输出错误排查指南。加工引擎启动错误排查指南数据加工任务的第一个环节便是启动加工引擎。该环节产生错误主要是由于在启动加工引擎过程, 检测到用户编写的LOG DSL规则存在错误,导致加工引擎内部的security check不通过。错误日志在加工引擎启动过程中,如果检测到用户编写的LOG DSL规则存在错误,会报如下形式的错误:{ "errorMessage": "ETL config doesn't pass security check, detail: XXXXXX" }错误日志可以在数据加工诊断报表的异常详情中或者Logstore: internal-etl-log中查看。错误影响在加工引擎启动阶段产生错误时,加工任务会一直重试,直到被手动停止或重试成功。重试成功后(修正加工规则后),加工任务会继续正常工作,不会产生日志的丢失或冗余(重复)。常见错误排查1. LOG DSL基本语法错误用户编写LOG DSL规则过程中,可能会因为粗心,错写了不符合LOG DSL语法的加工规则,如多或者少括号,逗号写成冒号等。错误日志{ "errorMessage": "ETL config doesn't pass security check, detail: invalid syntax" } { "errorMessage": "ETL config doesn't pass security check, detail: unexpected EOF while parsing" } ...错误排查方法可根据错误日志中的traceback信息定位具体的语法错误位置。如下图所示,根据错误日志中的traceback信息可以看到这里的LOG DSL语法错误是将e_set("test", v("status"))错写为了e_set("test": v("status"))。2. 非法使用运算符LOG DSL中所有的操作都需要通过LOG DSL提供的函数来完成。数值运算、大小比较等操作都需要通过op_类的函数来完成,而不能直接使用+ - * / > <等运算符。错误日志{ "errorMessage": "ETL config doesn't pass security check, detail: invalid type detected: <class `_ast.BinOp`> " }错误排查方法检查LOG DSL规则,确保所有的操作(运算、大小比较等)都使用了LOG DSL提供的函数来完成,没有使用LOG DSL提供的功能以外的非法运算符。错误及解决样例e_set("b", v("a") - 10) # 错误 e_set("b", op_sub(v("a"), 10)) # 正确 e_set("b", v("a") >= v("c")) # 错误 e_set("b", op_ge(v("a"), v("c"))) # 正确3. 函数参数类型传递错误如果传递给函数的参数类型和函数接收的参数类型不一致,会产生错误。错误日志{ "errorMessage": "ETL config doesn't pass security check, detail: invalid call in detected: function_name" }错误排查方法可根据错误日志中的traceback信息定位产生错误的函数。如下图所示,根据错误日志中的traceback信息可以看到这里的产生错误的函数是dt_totimestap。于是可以去检查规则中的调用dt_totimestap的地方,看是否传递了正确类型的参数给该函数。错误及解决样例#错误样例 e_set("time1", "2019-06-03 2:41:26") e_set("time2", dt_totimestap(v("time1"))) #正确样例 e_set("time1", "2019-06-03 2:41:26") e_set("time2", dt_totimestap(dt_parse(v("time1")))) #正确样例 e_set("time1", "2019-06-03 2:41:26") e_set("time2", dt_parsetimestamp(v("time1")))dt_totimestap接收的参数类型是日期时间对象,此处v("time1")是字符串类型,因此此处传递了错误类型的参数给日期时间函数,引发报错。可以使用dt_parse函数来将字符串转化为日期时间对象,再传递给dt_totimestamp。也可以使用dt_parsetimestap函数来代替dt_totimestamp, 该函数可以接收字符串类型。4. 非法使用变量定义赋值LOG DSL语法中不支持变量定义赋值,变量值只能通过无状态方式调用传递。错误日志{ "errorMessage": "ETL config doesn't pass security check, detail: invalid assign detected: variable_name" }错误排查方法检查LOG DSL规则中是否使用了变量定义赋值。可根据错误日志中的traceback信息定位产生错误的位置。错误及解决样例#错误样例 sum_value = op_add(v("a"), v("b")) e_set("sum", sum_value) #正确样例 e_set("sum", op_add(v("a"), v("b")))变量值只能通过无状态方式调用传递。源Logstore读取错误排查指南在加工引擎成功启动后,下一步是读取源Logstore的数据。数据加工引擎对源日志库采用流式读取,在加工过程中会持续不断的读取源logstore中的数据。这个环节产生错误主要是由于对源logstore的访问异常。源Logstore访问异常的原因可能是:源logstore信息配置错误源logstore信息发生变化网络错误...错误影响在读取源Logstore产生错误时,加工任务会一直重试,直到被手动停止或重试成功。重试成功后,加工任务会继续正常工作。加工任务会保存之前成功的断点并一直重试。重试成功后,从断点处继续读取,不会产生数据的丢失和冗余。常见错误排查1. 为源logstore配置了非法的AccessKey错误日志非法的AccessKey主要分为两种:非法的AccessKeyId和非法的AccessKeySecret#非法的AccessKeyId { "errorCode": "Unauthorized", "errorMessage": "AccessKeyId not found: xxxx" } #非法的AccessKeySecret { "errorCode": "SignatureNotMatch", "errorMessage": "signature uJfAJbc0ji04gb+cXhh0qWtajpM= not match" }错误排查方法检查任务配置项中,源Logstore的AccessKeyID和AccessKeySecret是否存在、正确。2. 源logstore信息发生变化错误分析用户配置了正确的源logstore信息,可能也已经进行了部分加工任务,但是在数据加工的过程中,源logstore信息发生了变化,导致原有的配置信息无法访问源logstore。错误日志源logstore信息发生变化有主要是如下2种情况:源logstore被删除{ "errorMessage": "Logstore [logstore_name] does not exist." }源logstore AccessKey信息发生变化#非法的AccessKeyId { "errorCode": "Unauthorized", "errorMessage": "AccessKeyId not found: xxxx" } #非法的AccessKeySecret { "errorCode": "SignatureNotMatch", "errorMessage": "signature uJfAJbc0ji04gb+cXhh0qWtajpM= not match" }错误排查方法检查源Logstore是否被删除。检查源Logstore的AccessKey信息是否发生改变。3. 网络错误错误日志{ "errorCode": "LogRequestError", "errorMessage": "HTTPConnectionPool(host='your_host', port=80): Max retries exceeded with url: your_url (Caused by NewConnectionError: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'" }错误排查方法检查网络连接是否正常。4. 读取不到源Logstore数据此条不属于错误,没有报错信息。具体参考通用排错步骤规则错误排查指南在成功读取源Logstore数据后,加工引擎开始对源Logstore的日志事件进行加工。该环节产生错误主要是由于在数据加工过程中, 部分(也可能是全部)日志事件不适配加工规则,从而引发的逻辑错误。除了逻辑错误之外,如果加工规则中涉及RDS、Logstore等外联资源的加载,则也有可能会产生资源的加载或刷新错误。该类错误的排查参考资源加载错误排查。本篇主要介绍逻辑错误排查指南。错误影响在日志事件加工阶段,与加工规则冲突的日志事件会引发报错,并被丢弃(跳过),加工后的输出结果中将不包含这些日志事件。加工任务会丢弃(跳过)与加工规则冲突的日志事件,并继续加工其他的日志事件,不会重试。需要注意的是,如果多条事件分裂自同一条事件(参考 事件分裂),只要其中有一条事件出错被丢弃,所有和该事件分裂自同一条源事件的其他事件也都会被丢弃。(在输出之前,这些事件都是以树结构互相关联,并不是互相独立的)错误排查方法查看错误日志的messgae字段,定位是哪些日志事件报错。参考错误日志查看方式。查看错误日志的reason字段,确定这些日志事件的报错原因。根据报错原因,为这些异常日志事件增添逻辑(可使用e_if, e_switch等流程控制函数),捕获并处理这些错误。常见逻辑错误样例1. 日志事件中存在异常值加工规则样例1#部分日志事件字段b的值为0,引发除数为0的错误 e_set("c", op_div_floor(v("a"), v("b")))错误日志{ "reason": "error when calling : floordiv\nDetail: integer division or modulo by zero", }错误排查方法检查报错的日志事件,确认它们的字段b的值是否为"0",如果是,此处为除数为0引发的错误。错误解决方案只有日志事件的字段b为0时才会报错。因此可以增添e_if逻辑来捕获b的字段值为0的异常情况。e_if_else(op_eq(v("b"), "0"), e_set("c", v("a")), e_set("c", op_div_floor(v("a"), v("b")))加工规则样例2#部分日志事件的字段a的值不是合法的时间戳格式,引发报错 e_set("b", dt_fromtimestamp(v("a")))错误日志{ "reason": "error when calling : int\nDetail: invalid literal for int() with base 10: '异常值'", }错误排查方法检查报错的日志事件,查看它们字段a的值是否为合法的时间戳格式(数值型字符串)。错误解决方案增加判断逻辑,如果字段a的值不是合法的时间戳格式(数值型字符串),则将这条日志事件输出到target2中。e_if_else(str_isdigit(v("a")),e_set("b", dt_fromtimestamp(v("a"))), e_output("target2"))2. 数值运算前未进行数据类型转换加工规则样例e_set("a", 10) e_set("b", 10) e_set("c", op_mul(v("a"), v("b")))错误日志{ "reason": "error when calling : mul\nDetail: can't mulltiply sequence by non-int of type' str'", }错误原因分析LOG DSL处理过程中,日志事件中的各个字段值都是以字符串形式存储。上述规则样例中,v("a")和v("b")都是字符串类型,直接传递给op_mul会引发报错。错误排查方法检查LOG DSL规则,是否在做数值运算前未进行数据类型转换。(字符串转数值型等)错误解决方案e_set("a", 10) e_set("b", 10) e_set("c", op_mul(c_int(v("a")), c_int(v("b"))))使用ct_int函数进行数据类型转换,将字符串型转化为整型后,再传递给op_mul函数。目标logstore输出错误排查指南日志事件被加工完之后,会被输出到预先配置好的目标logstore中去。需要注意的是,LOG DSL引擎并不是加工一条日志就输出一条,而是会先将加工好的数据存放在一个缓存池中,等缓存池中的数据达到一定的量,再一并输出。该环节产生错误主要是由于对目标logstore的访问异常。目标logstore访问异常的原因可能是:目标logstore信息配置错误目标logstore信息发生变化网络错误...错误影响在输出到目标logstore阶段产生错误时,加工任务会一直重试,直到被手动停止或重试成功。如果重试成功,加工任务会继续正常工作,不会产生日志的丢失和冗余。如果是已经输出部分数据后才报错(例如配置了多个目标, 一个目标成功, 另外一个目标失败),会保存断点并一直重试,重试成功后,不会有数据的丢失与冗余. 如果这时停止加工任务, 再重启加工时, 会从断点继续, 不会有数据丢失, 但可能会有数据的冗余(重复输出)。常见错误排查1. 为目标logstore配置了非法的AccessKey错误日志非法的AccessKey主要分为两种:非法的AccessKeyId和非法的AccessKeySecret#非法的AccessKeyId { "errorCode": "Unauthorized", "errorMessage": "AccessKeyId not found: xxxx" } #非法的AccessKeySecret { "errorCode": "SignatureNotMatch", "errorMessage": "signature uJfAJbc0ji04gb+cXhh0qWtajpM= not match" }错误排查方法检查任务配置项,查看对应的目标Logstore的AccessKeyID和AccessKeySecret是否存在、正确。2. 目标Project不存在错误日志{ "errorCode": "ProjectNotExist", "errorMessage": "The Project does not exist : your_project_name" }错误原因分析错误日志提示 Project does not exist有三种可能的原因:任务配置项中的目标Project名称输入错误。目标Project名称输入正确,但是在加工任务过程中,对应的project被删除。目标Project和当前的Project不在同一地域内。目前LOG DSL只支持同一地域内的不同Project之间的数据传输,不支持不同地域下的Project之间的数据传输。错误排查方法检查任务配置项中的目标Project名称是否输入有误。检查目标Project是否被删除。检查目标Project和当前Project是否在同一地域内。目前不支持不同地域下的Project之间的数据传输。3. 输出目标不存在加工规则样例e_coutput("target1")错误日志{ "errorMessage": "transform_data: output target target1 is not found in configurations" }错误原因分析上述LOG DSL规则将日志事件输出到target1中,而错误日志提示 target1 is not found in configurations。说明该LOG DSL规则的配置中并没有定义和target1关联的目标project和logstore。错误排查方法检查LOG DSL规则的配置项,确保配置项中定义了加工规则中涉及到的所有存储目标。4. 目标logstore信息发生变化错误分析用户配置了正确的目标ogstore信息,可能也已经进行了部分加工任务,但是在数据加工的过程中,目标logstore信息发生了变化,导致原有的配置信息无法访问目标logstore。错误日志目标logstore信息发生变化有主要是如下2种情况:目标logstore被删除{ "errorMessage": "Logstore [logstore_name] does not exist." }目标logstore AccessKey信息发生变化#非法的AccessKeyId { "errorCode": "Unauthorized", "errorMessage": "AccessKeyId not found: xxxx" } #非法的AccessKeySecret { "errorCode": "SignatureNotMatch", "errorMessage": "signature uJfAJbc0ji04gb+cXhh0qWtajpM= not match" }错误排查方法检查目标Logstore是否被删除。检查目标Logstore的AccessKey信息是否发生改变。5. 网络错误错误日志{ "errorCode": "LogRequestError", "errorMessage": "HTTPConnectionPool(host='your_host', port=80): Max retries exceeded with url: your_url (Caused by NewConnectionError: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'" }错误排查方法检查网络连接是否正常。进一步参考日志服务最佳实践汇总(持续更新)完整DSL语法介绍与参考PDF下载(持续更新)数据加工指南介绍:功能概述概念原理快速开始:快速开始(SLB日志加工实战)控制台操作源与目标访问秘钥配置规则洞察仪表盘性能指南成本优化指南语法:DSL语法介绍查询字符串语法JMES语法介绍管理配置:错误排查指南子账号授权配置任务状态监控与告警欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
作者: 唐恺 概述 对数据加工任务配置监控,可以发现数据流量、加工逻辑、系统运行的一些潜在异常,帮助业务及时做好异常恢复。 本文大部分监控指标基于“数据加工诊断”仪表盘(参考规则洞察仪表盘)。在系统层面关注:流式加工的消费延迟、是否有异常报错。业务层面上,处理日志行数或写出日志行数是可以被考虑的指标。 进入数据加工任务所属project,选择“仪表盘”-“数据加工诊断”,可以根据业务需求选择以下指标设置告警。 加工延迟 选择“shard消费延迟 (秒)”图表 设置触发条件 例如,选择阈值为120秒时告警: [delay (s)] > 120 设置通知方式 本文以钉钉webhook为例,更多通知方式参考告警通知方式。 告警通知 异常报错 选择“异常详情”图表 设置触发条件 例如,希望在出现ERROR时告警: 设置通知方式 告警通知 ERROR日志一般由加工逻辑或代码上引发,可以在修改代码并重启(停止、启动)作业后观察是否还有新的错误。 加工流量(绝对值) 选择“加工速率 (lines/s)”图表 设置触发条件 例如,当每秒处理日志条数少于4万行告警时告警(每种数据的流量特征不同,请根据实际情况设置有效的告警条件): accept < 40000 设置通知方式 告警通知 加工流量(日同比) 自定义监控指标(图表) 进入数据加工任务所属project,选择logstore internal-etl-log,使用如下SQL计算每5分钟的写出日志行数与昨日同比的指标: __topic__: __etl-log-status__ AND __tag__:__schedule_type__: Resident and event_id: "shard_worker:metrics:checkpoint" | select dt, today, yesterday, round((today - yesterday) * 100.0 / yesterday, 3) as inc_ration from (select dt, (case when diff[1] is null then 0 else diff[1] end) as today, (case when diff[2] is null then 0 else diff[2] end) as yesterday from (select dt, compare("delivered lines", 86400) as diff from (select date_format(__time__ - __time__ % 300, '%H:%i') as dt, sum("progress.delivered") as "delivered lines" from log group by dt order by dt asc limit 5000) group by dt order by dt asc limit 5000)) 保存该查询条件到仪表盘etl-monitor: 你还可以修改SQL以设置更精确的告警指标,例如只对任务ID:06f239b7362ad238e613abb3f7fe3c87设置告警: __topic__: __etl-log-status__ AND __tag__:__schedule_type__: Resident and event_id: "shard_worker:metrics:checkpoint" and __tag__:__schedule_id__: 06f239b7362ad238e613abb3f7fe3c87 | select ... 设置触发条件 例如,当日志处理速率比昨日下降40%时告警: inc_ration < (-40) 设置通知方式 告警通知 告警相关操作 可以在告警列表中进行删除或禁用通知或修改操作: 更多告警使用指南请参考设置告警。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 规则洞察仪表盘 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 任务状态监控与告警 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
直播讲师: 丁来强(成喆)- 阿里高级技术专家从事阿里云日志服务相关的产品与研发工作,擅长AIOps/SecOps的大数据分析平台构建与场景落地,乐于分享, PyCon历届讲师/云栖直播讲师,曾经在中国PyCon2015、2016与2018以及云栖分享过10+不同议题,覆盖Jupyter扩展、大数据分析可视化、工作流调度、函数式、设计模式、Python核心语言等方面,广受好评。 唐恺(风毅) - 阿里技术专家从事阿里云日志服务相关的产品与研发工作,日志采集、ETL、数据集成、相关领域专家, 在构建高可靠、高性能服务方面有相当的经验。 分享介绍日志服务数据加工功能提供托管的自动伸缩的实时可靠数据加工服务,本次系列培训从实战案例角度完整介绍数据加工的主要场景. 包括功能语法、数据分发、结构与非结构化数据清洗、外部资源关联、并发可靠性与排错等,解决实日志数据在接入、分析、投递、对接时存在各种数据加工需求与问题。 一览 # 主题 内容介绍 讲解人 排期 资料 1 数据加工介绍与实战 介绍数据加工的各种痛点, 场景以及解决方案. 介绍与演示日志服务数据加工的功能, 概念, 原理, 控制台操作, 以及SLB数据加工实战等 成喆 8月7日 19:30-20:30 直播资料下载(待发布) 2 数据加工DSL核心语法介绍 介绍与演示日志服务数据加工的核心与发的框架. 深入浅出的覆盖基础语法, 数据结构, 30+全局函数概述, 200+表达式函数概述, 以及类SLS/Lucene的查询字符串语法 成喆 8月8日 19:30-20:30 直播资料下载(待发布) 3 数据加工DSL语法实践 逐步深入介绍日志服务的事件字段提取模式, 正则表达式实践, 400+GROK模式概览, 灵活强大的JMES语法. 以及ETL核心场景最佳实践: 函数调用、事件判断、日期时间处理等 成喆 8月13日 19:30-20:30 直播资料下载(待发布) 4 数据加工动态数据分发汇集实践 介绍与演示数据加工对于跨账号多目标logstore数据分发. 以及跨账号多源logstore数据汇总的场景 唐恺 8月14日 19:30-20:30 直播资料下载(待发布) 5 非结构化数据解析实践 通过多个实战案例介绍日志服务数据加工如何全面灵活的支持非结构化数据的解析. 包括: 解析标准syslog协议框架、使用正则表达式与grok解析Ngnix日志、动态键值对KV加工, 特定格式的、特定格式文本的数据加工等 唐恺 8月20日 19:30-20:30 直播资料下载(待发布) 6 结构化化数据解析实践 通过多个实战案例介绍日志服务数据加工如何全面灵活的支持非结构化数据的解析. 包括:复杂JSON格式加工、多子键为数组的复杂JSON、多层数组对象嵌套的复杂JSON, 解析CSV格式日志等 唐恺 8月21日 19:30-20:30 直播资料下载(待发布) 7 数据映射富化实践 通过多个实战案例介绍日志服务数据加工对于数据富化的支持. 介绍如何从RDS-MySQL、Logstore、OSS等外部资源自动化加载数据支持各种维表更新与数据富化场景。也覆盖特定复杂逻辑映射的实战内容。 唐恺 8月28日 19:30-20:30 直播资料下载(待发布) 8 数据加工可靠性性与排错实践 深入浅出介绍介绍如何构建健壮性数据加工任务. 从异常错误监控, 到异常错误排查框架流程, 通过实战覆盖数据加工各个阶段的错误排查手段, 包括: 初始化错误排查, 读取错误, 加工错误, 输出错误等 成喆 8月29日 19:30-20:30 直播资料下载(待发布) 扫码观察直播:日志服务开发者技术交流群 阿里云开发者群:
场景1:Unix时间戳、日期时间字符串和日期时间对象的相互转换 LOG DSL语法中的日期时间处理主要涉及三种数据类型:Unix时间戳、日期时间字符串和日期时间对象。它们的相互转换方式如下图: 子场景1:日期时间对象和Unix时间戳的相互转换 1)日期时间对象转为Unix时间戳 dt_parsetimestamp智能转换函数,可以将日期时间对象或日期时间字符串转化为Unix时间戳。 dt_totimestamp,只支持将日期时间对象转化为Unix时间戳。 2)Unix时间戳转为日期时间对象 dt_parse智能转换函数,可以将Unix时间戳或日期时间字符串转化为日期时间对象 dt_fromtimestamp,只支持将Unix时间戳转化为日期时间对象。 子场景2:日期时间对象和日期时间字符串的相互转换 1)日期时间对象转为日期时间字符串 dt_str智能转换函数,可以将日期时间对象、Unix时间戳和日期时间字符串转化为制定格式的日期时间字符串。 dt_strftime,只支持将日期时间对象转化为日期时间字符串。 2)日期时间字符串转为日期时间对象 dt_parse智能转换函数,可以将日期时间字符串或Unix时间戳转化为日期时间对象。 dt_strptime,只支持将日期时间字符串转化为日期时间对象。 子场景3:日期时间字符串和Unix时间戳的相互转换 1)日期时间字符串转为Unix时间戳 dt_parsetimestamp智能转换函数,可以将日期时间字符串或日期时间对象转化为Unix时间戳。 2)Unix时间戳转为日期时间字符串 dt_str智能转换函数,可以将Unix时间戳、日期时间对象和日期时间字符串转化为制定格式的日期时间字符串。 dt_strftimestamp,只支持将Unix时间戳转化为日期时间字符串。 子场景4: 理解dt_parse等智能转换函数的应用场景 上述的转化过程中,大多数转化都有两种方式,一种使用智能转换函数,另一种使用该转换的专用函数。以dt_parse为代表的智能转换函数(参考日期时间函数)可以接受Unix时间戳、日期时间对象以及日期时间字符串等不同类型的参数,实现智能转换,例如: 原始日志1 time1: 1562741899 time2: 2019-07-10 06:58:19 LOG DSL编排 e_set("time3", dt_parse(v("time1"), tz="Asia/Shanghai")) e_set("time4", dt_parse(v("time2"), tz="Asia/Shanghai") 编排后日志 time1: 1562741899 time2: 2019-07-10 06:58:19 time3: 2019-07-10 06:58:19+08:00 time4: 2019-07-10 06:58:19+08:00 但是有一些场景下,智能转换函数就无法满足用户的需求。如对于用户自定义的特殊日期格式,dt_parse等智能转换函数无法自动解析日志,此时需要使用dt_strptime来进行指定格式的解析。如下: 原始日志2 time1: 2019-07-10 06:58:19 time2: 2019/07/10 06-58-19 LOG DSL编排 e_set("time3", dt_parsetimestamp(v("time1"))) e_set("time4", dt_parsetimestamp(dt_strptime(v("time2"), fmt="%Y/%m/%d %H-%M-%S"))) 编排后日志 time1: 2019-07-10 06:58:19 time2: 2019/07/10 06-58-19 time3: 1562741899 time4: 1562741899 结论 智能转换函数可以对不同类型的参数进行自动转换,因此默认推荐使用智能转换函数。 智能转换函数也有缺点,对于一些特殊的用户自定义日期格式,智能转换函数无法自动解析,需要使用dt_strptime来进行解析。 场景2: 理解时区的概念 子场景1: 理解时间字符串的含义 LOG DSL语法中的日期时间字符串主要分为两种形式: 带有时区信息的日期时间字符串,如2019-06-02 18:41:26+08:00。 不带时区信息的日期施时间字符串,如2019-06-02 10:41:26。 带有时区信息的日期时间字符串通过在日期时间后添加额外的时差信息来表达时区: 2019-06-02 18:41:26+08:00表示该时间是东8区时区下的2019-06-02 18:41:26。 2019-06-02 18:41:26-07:00表示该时间是西7区时区下的2019-06-02 18:41:26。 对于不带有时区信息的日期时间字符串,如果时区信息不同,该时间字符串表达的时间也不同。如果没有传递时区参数,默认为UTC时区下的时间。以2019-06-02 18:41:26为例: 默认情况下是UTC时区下的时间,即该时间字符串表达的时间是UTC时区下的时间,等价于2019-06-02 18:41:26+00:00。 如果交代了时区是东8区,该时间字符串表达的时间是东8区下的时间,等价于2019-06-02 18:41:26+08:00。 子场景2: 将日期时间转化为Unix时间戳 不带时区信息的日期时间 对于不带时区信息的日期时间字符串(如'2019-06-02 18:41:26'),将给定日期时间转化为Unix时间戳,需要指定该日期时间是哪个时区下的时间,不同的时区转化得到的Unix时间戳的值是不一样的。 原始日志 { 'time': '2019-06-02 18:41:26''} LOG DSL编排 e_set("Shanghai_timestamp", dt_parsetimestamp(v("time"), tz="Asia/Shanghai")) e_set("Los_Angeles_timestamp", dt_parsetimestamp(v("time"), tz="America/Los_Angeles")) e_set("UTC_timestamp", dt_parsetimestamp(v("time"))) 指定tz="Asia/Shanghai"表示time字段所表达的时间是上海所在时区对应的时间。 如果不指定时区,默认将给定日期时间当做UTC时区下的日期时间。 时区参数tz=时区字符串中所有可选时区字符串的值参考所有时区列表。可使用Ctrl + F搜索目标时区字符串。 加工后日志 { 'Shanghai_timestamp': '1559472086', 'Los_Angeles_timestamp': '1559526086', 'UTC_timestamp': '1559500886' } 带有时区信息的日期时间 对于带有时区信息的日期时间字符串(如'2019-06-02 18:41:26+08:00'),则无须指定时区参数。 原始日志 { 'China_time': '2019-06-02 18:41:26+08:00', 'America_time': '2019-06-02 3:41:26-07:00', 'UTC_time': '2019-06-02 10:41:26+00:00' } LOG DSL编排 e_set("timestamp1", dt_parsetimestamp(v("China_time"))) e_set("timestamp2", dt_parsetimestamp(v("America_time"))) e_set("timestamp3", dt_parsetimestamp(v("UTC_time"))) 加工后日志 { "timestamp1": "1559472086", "timestamp2": "1559472086", "timestamp3": "1559472086" } 子场景3: 不同时区下的日期时间相互转换 不带时区信息的日期时间 对于不带时区信息的日期时间字符串(如'2019-06-02 18:41:26'),可以通过Unix时间戳为媒介,实现不同时区下的日期时间的相互转换。 加工需求 将洛杉矶时区下的日期时间转换为上海时区下的日期时间 原始日志 #已知time字段的值的时间是洛杉矶时间 {'time': '2019-06-04 2:41:26'} LOG DSL编排 e_set("timestamp", dt_parsetimestamp(v("time"), tz="America/Los_Angeles")) e_set("Shanghai_time", dt_parse(v("timestamp"), tz="Asia/Shanghai")) 加工后日志 { 'time': '2019-06-04 2:41:26', 'Shanghai_time': '2019-06-04 17:41:26+08:00' } 带有时区信息的日期时间 对于带有时区信息的日期时间字符串(如'2019-06-02 18:41:26+08:00''),可直接通过dt_astimezone实现不同时区下的日期时间的相互转换。 原始日志 {'time': '2019-06-04 2:41:26+08:00'} LOG DSL编排 e_set("new_time", dt_astimezone(v("time"), tz="America/Los_Angeles")) 加工后日志 { 'time': '2019-06-04 2:41:26+08:00', 'new_time': '2019-06-03 11:41:26-07:00' } 场景3: 理解日期时间和Unix时间戳的应用场景 LOG DSL编排中涉及到的日期时间主要有两种形式Unix时间戳和日期时间(字符串或对象)。 日期时间 日期时间形式主要是为了便于展示以及提升用户可读性等。 unix_time: 1562741899 date_time: 2019-07-10 06:58:19 Unix时间戳 Unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,它的主要应用场景有: 1. 表示系统时间 如日志事件中表示日志产生时间的元字段__time__,表示日志接收时间的字段__receieve_time__等,这些字段的值都使用Unix时间戳来表示对应的系统时间。 __source__: 1.2.3.4 __tag__:__receive_time__: 1562741899 __topic__: __time__: 1562731122 2. 便于时间相关的计算。 由于Unix时间戳是从1970年1月1日开始所经过的秒数,因此在很多场景下便于直接进行日期时间相关的计算。 原始日志 time1: 1562741899 time2: 1562731122 LOG DSL编排 e_set("time3", op_sub(v("time1"), v("time2"))) 加工后日志 time1: 1562741899 time2: 1562731122 time_diff: 10777 场景4: 使用dt_add做灵活的日期偏移 dt_add函数支持在特定时间粒度上修改(增加、减少、覆盖)日期时间的值。dt_add的参数如下 dt_add(字段名, dt1=None, dt2=None, year(s)=None, month(s)=None, day(s)=None, hour(s)=None, minute(s)=None, second(s)=None, microsecond(s)=None, weeks(s)=None, weekday=None) 子场景1:理解year(s),month(s)区别 year(s), month(s),day(s)等参数的后面都带有(s),表示这些参数可以有两种形式,即year和years,month和months等。 以year和years为例,如果参数传递的是year,表示在年份粒度上覆盖为year参数的值;如果传递的是years,表示在年份粒度上增加years参数的值。(可参考下面的例子) 原始日志 "time1": "2019-06-04 2:41:26" LOG DSL编排1 e_set("time2", dt_add(v("time1"), year=2018)) 编排后日志 "time1": "2019-06-04 2:41:26" "time2": "2018-06-04 02:41:26" LOG DSL编排2 e_set("time2", dt_add(v("time1"), years=2018)) 编排后日志 "time1": "2019-06-04 2:41:26" "time2": "4037-06-04 02:41:26" 子场景2:理解weekday参数用法 weekday参数通常和dt_MO,dt_TU等参数一起使用,表示特定星期几的偏移。具体可参考dt_MO。 原始日志 #2019-06-04是周二 "time1": "2019-06-04 2:41:26" LOG DSL编排 #time1的下一个星期一对应的日期 e_set("nex_Monday", dt_add(v("time1"), weekday=dt_MO(1))) #time1的上一个星期二对应的日期 e_set("previous_Tuesday", dt_add(v("time1"), weekday=dt_TU(op_neg(1)))) #time1的下下一个星期六对应的日期 e_set("nex_next_Saturday", dt_add(v("time1"), weekday=dt_SA(2))) #time1的上上一个星期日对应的日期 e_set("previous_previous_Sunday", dt_add(v("time1"), weekday=dt_SU(op_neg(2)))) 编排后日志 "time1": "2019-06-04 2:41:26", "next_Monday": "2019-06-10 02:41:26", "previous_Tuesday": "2019-06-04 2:41:26", "next_next_Saturday": "2019-06-15 02:41:26", "previous_previous_Sunday": "2019-05-26 02:41:26" 需要注意的是,如果time1对应的日期是周二,那么它的上一个周二和下一个周二都是time1本身。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 日志服务数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 完整DSL语法介绍与参考PDF下载(持续更新) 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
最佳实践汇总 基础 函数调用最佳实践 事件判断最佳实践 日期时间处理最佳实践 分发 数据分发: 跨账号多目标logstore数据分发 数据汇总: 跨账号多源logstore数据汇总 非结构化文本解析 解析syslog协议框架: 解析syslog/Rsyslog的标准格式 一般性文本: 使用正则表达式与grok解析Ngnix日志 动态KV: 字符串动态键值对的提取 特定格式的: 特定格式文本的数据加工 结构化文本解析 复杂JSON格式加工: 多子键为数组的复杂JSON 多层数组对象嵌套的复杂JSON CSV格式的: 解析CSV格式的日志 数据富化 构建字典与表格 从RDS-MySQL获取数据 从其他logstore获取数据 使用搜索映射做高级数据富化 进一步参考 日志服务数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 完整DSL语法介绍与参考PDF下载(持续更新) 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
普通映射 vs 搜索映射 典型映射方式不能满足富化需求时, 可以使用搜索映射, 搜索映射与传统方式映射的区别在于匹配方式不同. 普通映射方式 一般映射使用文本完全匹配方式来映射, 例如NGNIX日志中, 需要将状态码转换为一个文本表示: 状态码 文本 200 成功 300 跳转 400 请求错误 500 服务器错误 下面规则调用e_dict_map将字段status中的http请求状态码转化为文本描述, 放入字段status_desc. e_dict_map({"400": "请求错误", "500": "服务器错误", "300": "跳转", "200": "成功"}, "status", "status_desc") 实际上, NGNIX的HTTP请求的状态是不止上述4种, 当status值是401, 404时, 需要更新字典覆盖, 否则会匹配不上. 参考Http请求状态码 搜索映射方式 当需要一些灵活的针对特定值做匹配逻辑时的映射, 例如: 状态码 文本 1XX-2XX 成功 3XX 跳转 4XX 请求错误 5XX 服务器错误 就需要使用搜索映射来实现, 这里的字典的关键字实际是一个搜索查询字符串. 状态码 文本 status<=299 成功 status: [300, 399] 跳转 status: [400, 499] 请求错误 status: [500, 599] 服务器错误 使用如下代码: e_search_dict_map({"status: [400, 499]": "请求错误", "status: [500, 599]": "服务器错误", "status: [300, 399]": "跳转", "status<=200": "成功"}, "status", "status_desc") 场景样例1:使用搜索映射字典做复杂富化 这里以网络请求日志来距离做一个更复杂逻辑的映射: 需求 原始日志 "日志1" http_host: m1.abcd.com http_status: 200 request_method: GET body_bytes_sent: 740 "日志2" http_host: m2.abcd.com http_status: 200 request_method: POST body_bytes_sent: 1123 "日志3" http_host: m3.abcd.com http_status: 404 request_method: GET body_bytes_sent: 711 "日志4" http_host: m4.abcd.com http_status: 504 request_method: GET body_bytes_sent: 1822 加工需求 根据日志事件的http_status和body_bytes_sent的值的不同,为每个事件添加不同的type信息。 http_status为2XX并且body_bytes_sent长度小于1000的日志事件设置type为正常。 http_status为2XX并且body_bytes_sent长度大于等于1000的日志事件设置type为过长警告。 http_status为3XX的日志事件设置type为重定向。 http_status为4XX的日志事件设置type为错误。 其他的日志事件设置type为其他。 LOG DSL编排 e_search_dict_map({'http_status~="2\d+" and body_bytes_sent < 1000': "正常", 'http_status~="2\d+" and body_bytes_sent >= 1000': "过长警告", 'http_status~="3\d+"': "重定向", 'http_status~="4\d+"': "错误", "*": "其他"}, "http_status", "type") 加工后的日志 "日志1" type: 正常 http_host: m1.abcd.com http_status: 200 request_method: GET body_bytes_sent: 740 "日志2" type: 过长警告 http_host: m2.abcd.com http_status: 200 request_method: POST body_bytes_sent: 1123 "日志3" type: 错误 http_host: m3.abcd.com http_status: 404 request_method: GET body_bytes_sent: 711 "日志4" type: 其他 http_host: m4.abcd.com http_status: 504 request_method: GET body_bytes_sent: 1822 说明 上面使用了函数e_search_dict_map,具体语法参照e_search_dict_map函数. 其中映射的关键字字段是搜索查询字符串,可支持正则,完全匹配,模糊匹配等形式。 和基于表格来进行数据富化一样,基于字典的富化,除了可以使用通过{}直接构建的字典之外,也可以基于任务配置资源、外部OSS资源、表格资源等来构建字典,具体可参考字典构建。 场景样例2:使用搜索表格做数据富化 原始日志 "日志1" http_host: m1.abcd.com http_status: 200 request_method: GET body_bytes_sent: 740 "日志2" http_host: m2.abcd.com http_status: 200 request_method: POST body_bytes_sent: 1123 "日志3" http_host: m3.abcd.com http_status: 404 request_method: GET body_bytes_sent: 711 "日志4" http_host: m4.abcd.com http_status: 504 request_method: GET body_bytes_sent: 1822 需求 针对数据中的http_status, body_bytes_sent等字段, 映射出其他多个字段例如type, warning_level和warning_email等. 具体的规则样例存储于RDS-MySQL中, 例如: MYSQL 数据库表中数据 content type warning_level warning_email http_status~="2d+" and body_bytes_sent < 1000 正常 INFO normal@etl.com http_status~="2d+" and body_bytes_sent >= 1000 过长警告 WARNING over-long@etl.com http_status~="3d+" 重定向 WARNING redirect@etl.com http_status~="4d+" 错误 ERROR error@etl.com LOG DSL编排 e_search_table_map(res_rds_mysql("...连接MySQL参数..."),"content",["type", "warning_level", "warning_email"]) 使用了e_search_table_map 语法,详细请参照e_search_table_map搜索表格语法,此处简单讲解,res_rds_mysql()里面填入的是去 RDS MYSQl 拉取数据的配置,该函数会拉取指定的mysql表格,具体语法请见res_rds_mysql函数用法,"content"字段指定的是mysql表中的字段,会使用该字段的值的内容去匹配原始日志中的内容,具体匹配规则请见e_search用法,可支持正则,完全匹配,模糊匹配等形式。 加工后日志 根据日志事件的http_status和body_bytes_sent的值的不同,为每个事件添加不同的type, warning_level以及warning_email信息。 "日志1" type: 正常 warning_level: INFO warning_email: normal@etl.com http_host: m1.abcd.com http_status: 200 request_method: GET body_bytes_sent: 740 "日志2" type: 过长警告 warning_level: WARNING warning_email: over-long@etl.com http_host: m2.abcd.com http_status: 200 request_method: POST body_bytes_sent: 1123 "日志3" type: 错误 warning_level: ERROR warning_email: error@etl.com http_host: m3.abcd.com http_status: 404 request_method: GET body_bytes_sent: 711 "日志4" type: 其他 warning_level: INFO warning_email: others@etl.com http_host: m4.abcd.com http_status: 504 request_method: GET body_bytes_sent: 1822 以上加工规则默认匹配到表中一行之后,立即返回。可以为e_search_table_map设置参数 multi_match=True和multi_join=",",分别表示开启多行匹配和匹配到多个值时候,多值使用逗号进行组合。 e_search_table_map(res_rds_mysql("...连接MySQL参数..."),"content",["type", "warning_level", "warning_email"], multi_match=True,multi_join=",") 以上加工规则默认使用表格中的列名作为添加的字段名称,也可以修改为新的字段名称。例如warning_email字段重命名为email字段,把新字段和原字段写在一个原组里面即可,如下示例 e_search_table_map(res_rds_mysql("...连接MySQL参数..."),"content",["type", "warning_level", ("warning_email", "email")],multi_match=True,multi_join=",") 说明 上面使用了函数e_search_table_map,具体语法参照e_search_table_map函数. 其中映射的关键字字段是搜索查询字符串,可支持正则,完全匹配,模糊匹配等形式。 基于表格的富化,构建表格方式除了RDS-MySQl外, 还有其他方法, 例如动态构建, 本地资源, OSS等, 具体可参考表格构建。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 使用全局富化函数做富化时, 需要传递一个字典或者表格结构做富化. 参考构建字典与表格做数据富化的各种途径比较. 本篇介绍从使用资源函数res_rds_mysql从RDS-MySQL拉取数据的做富化的详细实践.关于res_rds_mysql的参数说明, 参考这里. 背景 这里我们在RDS中存放用户信息表格userinfo. 原始数据库表中内容: id province city uid 1 jiangsu nanjing 01234 2 henan zhengzhou 01235 3 heilongjiang haerbin 01236 4 jiangsu yantai 01237 场景1:定期刷新拉取所有 富化数据如果定期会全量刷新时, 希望数据加工任务能够自动定期去拉取, 可以如下配置: res_rds_mysql(..., refresh_interval=300) 上述语法会返回一个表格结构, 并且会自动跟踪表格, 每隔5分钟重新拉取一遍mysql 表的内容并刷新这个表格内容。 场景2:拉取部分数据 如果仅仅使用RDS-MySQL中个别字段做富化, 推荐使用参数table, sql和fields来进行行或者列过滤. 这样可以降低维表大小, 增加富化效率. 如下进行列过滤, 值选择city和uid列, 两者效果没有任何区别. res_rds_mysql(..., sql="select city, uid from userinfo") # 列过滤 res_rds_mysql(..., table="userinfo", fields=["city", "uid"]) # 列过滤 如下使用sql进行列与的行过滤, 选择所有uid > 1234的数据. res_rds_mysql(..., sql="select * from userinfo where uid > 1234") # 行过滤 res_rds_mysql(..., sql="select city, uid from userinfo where uid > 1234") # 行列过滤 场景3:拉取后再过滤数据 在使用参数table, sql和fields来进行行或者列过滤不能满足需求时, 可以进一步使用参数fetch_exclude_data和/或fetch_include_data来进行行过滤. 例如: res_rds_mysql(..., fetch_include_data="uid==0123*") # 保留所有uid以0123开头的数据 res_rds_mysql(..., fetch_exclude_data="uid < 1234") # 去除所有uid小于1234的数据 res_rds_mysql(..., fetch_include_data="city:n", fetch_exclude_data="uid < 1234") 参考以上注释了解两者区别, 注意到这里的这两个参数的格式都是查询字符串. 同时配置fetch_exclude_data和fetch_include_data, 会优先执行fetch_exclude_data语法,将不符合的数据剔除,然后在执行fetch_include_data语法,将符合的数据添加进来,fetch_exclude_data和fetch_exclude_data参数语法都是根据e_search语法,支持正则匹配,模糊匹配等多种方式,上述第三行语法含义为,拉取表中uid大于等于1234, 且以city包含字母n的所有数据做维表. 注意: 这种过滤是在拉取数据到本地后再进行过滤, 因此效率没有参数table, sql和fields过滤高. 场景4:调整返回表格结构 默认返回的表格列名与RDS-MySQL中的表格结构一致, 如果需要调整, 例如将province字段编程prov等, 可以使用如下方法: res_rds_mysql(..., sql="select id, uid, province as prov, city from userinfo") res_rds_mysql(..., table="userinfo", fields=["id", "uid", ("province", "prov"), "city" ]) 两个方法是一样效果. 关于fields参数, 可以进一步参考数据列列表 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
字典和表格是数据富化(映射)过程中主要使用的两种数据结构,本文主要介绍这两种数据结构的常见构建方式,并对比不同构建方式的优缺点。 字典构建 直接构建 e_dict_map({"400": "错误", "200": "正常", "*": "其他"}, "status", "message") 优点: 直观、简单、方便. 从任务配置资源构建 e_dict_map(res_local("http_code_map"), "status", "message") 其中http_code_map是任务高级配置项, 值为: {"400": "错误", "200": "正常", "*": "其他"} 优点: 如果内容较多, 且经常修改的话, 更易于维护. 从表格构建 使用tab_to_dict从表格构建, 而表格的构建参考本文后续的表格构建. e_dict_map(tab_to_dict(tab_parse_csv("status_code,status_info\n400,错误\n200,正常\n*,其他"), "status_code", "status_info"), "status", "message") 优点: 高级场景下使用. 维护机制更灵活. 从字典函数构建 e_dict_map(dct_make("400", "错误", "200", "正常", "*", "其他"), "status", "message") 优点: 高级场景下可以实现特定效果, 因为dct_make还可以接受其他函数的返回值. 具体参考字典函数 从其他表达式构建 e_dict_map(json_parse(v("http_code_map")), "status", "message") 这里从源日志的字段http_code_map中获取映射关系. 优点:可以从日志事件的字段中动态提取映射关系,并构建字典。 不同字典构建方式对比 构建方式 优点 缺点 直接构建 直观、简单、方便 1.如果内容较多, 规则会相对冗长.2. 静态不灵活. 从任务配置资源构建 如果内容较多, 且经常修改的话, 较为易于维护. 1. 不易于扩展和跨任务复用.2.不支持自动刷新. 从表格构建 1. 高级场景下使用, 支持的场景更丰富. 2. 维护机制更灵活. 需要构建和维护对应的表格,过程相对繁琐. 从字典函数构建 可以基于逻辑动态构建字典, 特定场景下适用. 1.较为高级, 不易于维护. . 从其他表达式构建 可以从日志事件的字段中动态提取映射关系, 特定场景下适用.。 1.较为高级, 不易于维护. 表格构建 从文本构建 e_table_map(tab_parse_csv("city,name,age\nshanghai,aliyun,10\ncity:nanjing,Maki,18"), "name",["city", "age"]) 优点: 直观、简单、方便. 从任务配置资源构建 e_search_table_map(tab_parse_csv(res_local("table_info")), "name",["city", "age"]) 其中table_info是加工规则的任务配置项, 值为: content,name,age shanghai,aliyun,10 nanjing,Maki,18 优点: 如果内容较多, 且经常修改的话, 较为易于维护. 从RDS资源中构建 e_table_map(tab_parse_csv(res_rds_mysql(...database="db", table="city")), "name",["city", "age"]) RDS表格city的内容为: content,name,age shanghai,aliyun,10 nanjing,Maki,18 优点: 如果内容较多, 且经常修改的话, 易于维护, 且会自动刷新. 从其他Logstore资源构建 e_table_map(res_log_logstore_pull(..., project="project_name", logstore="logstore_name", fields=["city","name","age"]),, "name",["city", "age"]) 对应logstore中日志事件为: "日志1" { "city": "shanghai", "name": "aliyun", "age": "10" } "日志2" { "city": "city:nanjing and data > 100", "name": "Maki", "age": "18" } 优点: 支持实时读取, 维护机制更灵活. 高级场景下使用. 不同表格构建方式对比 构建方式 优点 缺点 从文本构建 直观、简单、方便 1.如果内容较多, 规则会相对冗长。2. 不易于维护、扩展和复用. 从任务配置资源构建 如果内容较多, 且经常修改的话, 较为易于维护. 1. 不易于扩展和跨任务复用.2.不支持自动刷新. 从RDS资源构建 1. 如果内容较多, 且经常修改的话, 易于维护. 2. 支持自动刷新.3.支持跨任务复用 需要连接外部RDS资源,配置过程相对比较繁琐. 从其他Logstore资源构建 支持实时读取, 维护机制更灵活. 高级场景下使用. 需要连接其他Logstore,配置过程相对比较繁琐. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
本实践案例主要是从多层数组对象嵌套的场景,向读者介绍如何使用LOG DSL解决工作中的具体需求。 需求 这里以一个复杂的包括多层数组嵌套的对象举例, 希望可以将users下的每个对象中的login_histories的每个登录信息都拆成一个登录事件. 原始日志举例 __source__: 1.2.3.4 __topic__: content:{ "users": [ { "name": "user1", "login_historis": [ { "date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1" }, { "date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1" }, { ...更多登录信息... } ] }, { "name": "user2", "login_historis": [ { "date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2" }, { "date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3" }, { ...更多登录信息... } ] }, { ....更多user.... } ] } 期望分裂出的日志 __source__: 1.2.3.4 name: user1 date: 2019-10-11 1:0:0 login_ip: 1.1.1.1 __source__: 1.2.3.4 name: user1 date: 2019-10-11 0:0:0 login_ip: 1.1.1.1 __source__: 1.2.3.4 name: user2 date: 2019-10-11 0:0:0 login_ip: 1.1.1.2 __source__: 1.2.3.4 name: user2 date: 2019-10-11 1:0:0 login_ip: 1.1.1.3 ....更多日志.... 解决方案 1、首先对content中的users做分裂和展开操作 e_split("content", jmes='users[*]', output='item') e_json("item",depth=1) 处理后返回的日志: __source__: 1.2.3.4 __topic__: content:{...如前...} item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1"}, {"date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1"}]} login_histories: [{"date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1"}, {"date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1"}] name: user1 __source__: 1.2.3.4 __topic__: content:{...如前...} item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2"}, {"date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3"}]} login_histories: [{"date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2"}, {"date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3"}] name: user2 2、然后对login_histories先做分裂在做展开操作 e_split("login_histories") e_json("login_histories", depth=1) 处理后返回的日志: __source__: 1.2.3.4 __topic__: content: {...如前...} date: 2019-10-11 0:0:0 item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2"}, {"date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3"}]} login_histories: {"date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2"} login_ip: 1.1.1.2 name: user2 __source__: 1.2.3.4 __topic__: content: {...如前...} date: 2019-10-11 1:0:0 item: {"name": "user2", "login_histories": [{"date": "2019-10-11 0:0:0", "login_ip": "1.1.1.2"}, {"date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3"}]} login_histories: {"date": "2019-10-11 1:0:0", "login_ip": "1.1.1.3"} login_ip: 1.1.1.3 name: user2 __source__: 1.2.3.4 __topic__: content: {...如前...} date: 2019-10-10 1:0:0 item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1"}, {"date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1"}]} login_histories: {"date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1"} login_ip: 1.1.1.1 name: user1 __source__: 1.2.3.4 __topic__: content: {...如前...} date: 2019-10-10 0:0:0 item: {"name": "user1", "login_histories": [{"date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1"}, {"date": "2019-10-10 1:0:0", "login_ip": "1.1.1.1"}]} login_histories: {"date": "2019-10-10 0:0:0", "login_ip": "1.1.1.1"} login_ip: 1.1.1.1 name: user1 3、经过以上两步操作,基本上得到相应的数据,只需要删除无关字段即可 e_drop_fields("content", "item", "login_histories") 处理后返回的日志: __source__: 1.2.3.4 __topic__: name: user1 date: 2019-10-11 1:0:0 login_ip: 1.1.1.1 __source__: 1.2.3.4 __topic__: name: user1 date: 2019-10-11 0:0:0 login_ip: 1.1.1.1 __source__: 1.2.3.4 __topic__: name: user2 date: 2019-10-11 0:0:0 login_ip: 1.1.1.2 __source__: 1.2.3.4 __topic__: name: user2 date: 2019-10-11 1:0:0 login_ip: 1.1.1.3 4、综上LOG DSL规则可以如以下形式: e_split("content", jmes='users[*]', output='item') e_json("item",depth=1) e_split("login_histories") e_json("login_histories", depth=1) e_drop_fields("content", "item", "login_histories") 总结 针对以上类似的需求,首先需要进行分裂,然后在做展开操作,最后删除无关信息。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
加工需求 统计类日志形式 程序构建的日志经常会以一种统计性质的JSON格式写入, 通常其包含一个基础信息, 以及多个子健为数组的形式. 例如一个服务器每隔1分钟写入一条日志, 包含当前信息状态, 以及相关服务器和客户端节点的统计状态信息. 样例: __source__: 1.2.3.4 __topic__: content:{ "service": "search_service", "overal_status": "yellow", "servers": [ { "host": "1.2.3.4", "status": "green" }, { "host": "1.2.3.5", "status": "green" } ], "clients": [ { "host": "1.2.3.6", "status": "green" }, { "host": "1.2.3.7", "status": "red" } ] } 加工需求 1、对原始日志进行topic分裂,主题主要分为三个,分别是overall_type、client_status、server_status2、对于不同的topic保存不同的信息 overall_type:保留server、client数量,overal_status颜色和service信息 client_status: 保留对应的host地址、status状态和service信息 server_status: 保留对应的host地址、status状态和service信息 期望输出的日志 期望样例中的一条日志会被分裂加工成5条日志: __source__: 1.2.3.4 __topic__: overall_type client_count: 2 overal_status: yellow server_count: 2 service: search_service __source__: 1.2.3.4 __topic__: client_status host: 1.2.3.7 status: red service: search_service __source__: 1.2.3.4 __topic__: client_status host: 1.2.3.6 status: green service: search_service __source__: 1.2.3.4 __topic__: server_status host: 1.2.3.4 status: green service: search_service __source__: 1.2.3.4 __topic__: server_status host: 1.2.3.5 status: green service: search_service 解决方案 初步处理 1、第一步将一条日志拆分成3条日志, 给主题赋予3个不同值, 在进行分裂,经过分裂后会分成除了topic不同,其他信息相同的三条日志。 e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__") 处理后日志格式如下(在内存中): __source__: 1.2.3.4 __topic__: server_status // 另外2条是client_status和overall_type, 其他一样 content: { ...如前... } 2、第二步为基于content的JSON内容在第一层展开, 并删除content字段: e_json('content',depth=1) e_drop_fields("content") 处理后的日志格式如下(在内存中): __source__: 1.2.3.4 __topic__: overall_type // 另外2条是client_status和overall_type, 其他一样 clients: [{"host": "1.2.3.6", "status": "green"}, {"host": "1.2.3.7", "status": "red"}] overal_status: yellow servers: [{"host": "1.2.3.4", "status": "green"}, {"host": "1.2.3.5", "status": "green"}] service: search_service 处理overall_type日志 针对主题是overall_type的日志, 统计client_count和server_count: e_if(e_search("__topic__==overall_type"), e_compose( e_set("client_count" json_select(v("clients"), "length([*])", default=0)), e_set("server_count" json_select(v("servers"), "length([*])", default=0)) )) 处理后的日志为(仅显示修改部分): __topic__: overall_type server_count: 2 client_count: 2 丢弃相关字段: e_if(e_search("__topic__==overall_type"), e_drop_fields("clients", "servers")) 处理server_status日志 针对主题是server_status的日志, 进行进一步分裂. e_if(e_search("__topic__==server_status"), e_compose( e_split("servers"), e_json("servers", depth=1) )) 处理后的日志为2条如下(仅显示修改部分): __topic__: server_status servers: {"host": "1.2.3.4", "status": "green"} host: 1.2.3.4 status: green 和 __topic__: server_status servers: {"host": "1.2.3.5", "status": "green"} host: 1.2.3.5 status: green 保留相关字段: e_if(e_search("__topic__==overall_type"), e_drop_fields("servers")) 处理client_status日志 同理, 针对主题是client_status的日志, 进行进一步分裂, 在删除多余字段. e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") )) 处理后的日志为2条如下(仅显示修改部分): __topic__: client_status host: 1.2.3.6 status: green 和 __topic__: clients host: 1.2.3.7 status: red 综合 综上,LOG DSL规则是 # 总体分裂 e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__") e_json('content',depth=1) e_drop_fields("content") # 处理overall_type日志 e_if(e_search("__topic__==overall_type"), e_compose( e_set("client_count" json_select(v("clients"), "length([*])", default=0)), e_set("server_count" json_select(v("servers"), "length([*])", default=0)) )) # 处理server_status日志 e_if(e_search("__topic__==server_status"), e_compose( e_split("servers"), e_json("servers", depth=1) )) e_if(e_search("__topic__==overall_type"), e_drop_fields("servers")) # 处理client_status日志 e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") )) 方案优化 一个边界问题 注意到以上方案对于content.servers和content.servers是空时的处理有一些问题, 假设原始日志是: __source__: 1.2.3.4 __topic__: content:{ "service": "search_service", "overal_status": "yellow", "servers": [ ], "clients": [ ] } 会被分裂为3条日志, 其中主题是client_status和server_status的日志内容是空的. __source__: 1.2.3.4 __topic__: overall_type client_count: 0 overal_status: yellow server_count: 0 service: search_service __source__: 1.2.3.4 __topic__: client_status service: search_service __source__: 1.2.3.4 __topic__: server_status host: 1.2.3.4 status: green service: search_service 方案1 这里可以在初始分裂后, 处理server_status和client_status日志前分别判断并丢弃空的相关事件: # 处理server_status: 空的丢弃(非空保留) e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])"))) # 处理client_status: 空的丢弃(非空保留) e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])"))) 综合 综上,LOG DSL规则是 # 总体分裂 e_set("__topic__", "server_status,client_status,overall_type") e_split("__topic__") e_json('content',depth=1) e_drop_fields("content") # 处理overall_type日志 e_if(e_search("__topic__==overall_type"), e_compose( e_set("client_count" json_select(v("clients"), "length([*])", default=0)), e_set("server_count" json_select(v("servers"), "length([*])", default=0)) )) # 新加: 预处理server_status: 空的丢弃(非空保留) e_keep(op_and(e_search("__topic__==server_status"), json_select(v("servers"), "length([*])"))) # 处理server_status日志 e_if(e_search("__topic__==server_status"), e_compose( e_split("servers"), e_json("servers", depth=1) )) e_if(e_search("__topic__==overall_type"), e_drop_fields("servers")) # 新加: 预处理client_status: 空的丢弃(非空保留) e_keep(op_and(e_search("__topic__==client_status"), json_select(v("clients"), "length([*])"))) # 处理client_status日志 e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") )) 方案2 在初始分裂时进行判断, 如果对应数据是空的就不分裂出更多事件: # 初始主题 e_set("__topic__", "server_status") # 如果content.servers非空, 则从server_status分裂出1条日志 e_if(json_select(v("content"), "length(servers[*])"), e_compse( e_set("__topic__", "server_status,overall_type"), e_split("__topic__") )) # 如果content.clients非空, 则从overall_type再分裂出1条日志 e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")), e_compse( e_set("__topic__", "client_status,overall_type"), e_split("__topic__") )) 综合 综上,LOG DSL规则是 # 总体分裂 e_set("__topic__", "server_status") # 如果content.servers非空, 则从server_status分裂出1条日志 e_if(json_select(v("content"), "length(servers[*])"), e_compse( e_set("__topic__", "server_status,overall_type"), e_split("__topic__") )) # 如果content.clients非空, 则从server_status分裂出1条日志 e_if(op_and(e_search("__topic__==overall_type"), json_select(v("content"), "length(clients[*])")), e_compse( e_set("__topic__", "client_status,overall_type"), e_split("__topic__") )) # 处理overall_type日志 e_if(e_search("__topic__==overall_type"), e_compose( e_set("client_count" json_select(v("clients"), "length([*])", default=0)), e_set("server_count" json_select(v("servers"), "length([*])", default=0)) )) # 处理server_status日志 e_if(e_search("__topic__==server_status"), e_compose( e_split("servers"), e_json("servers", depth=1) )) e_if(e_search("__topic__==overall_type"), e_drop_fields("servers")) # 处理client_status日志 e_if(e_search("__topic__==client_status"), e_compose( e_split("clients"), e_json("clients", depth=1), e_drop_fields("clients") )) 比较 方案1会在分裂出日志后再删除, 逻辑上有些多余, 但规则简单易维护. 默认推荐.方案2会在分裂前进行判断, 处理效率会高一些, 但规则略微冗余, 仅在特定场景(例如初始分裂可能导致大量额外事件产生)时推荐. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
本案例是根据客户实际应用需求中产生,以下将详细从原始数据到需求再到解决方案等几个方面向读者解答如何使用LOG DSL加工语法,解决实际生产中的问题。 原始日志 _program_:error _severity_:6 _priority_:14 _facility_:1 topic:syslog-forwarder content:10.64.10.20|10/Jun/2019:11:32:16 +0800|m.zf.cn|GET /zf/11874.html HTTP/1.1|200|0.077|6404|10.11.186.82:8001|200|0.060|https://yz.m.sm.cn/s?q=%E8%9B%8B%E8%8A%B1%E9%BE%99%E9%A1%BB%E9%9D%A2%E7%9A%84%E5%81%9A%E6%B3%95&from=wy878378&uc_param_str=dnntnwvepffrgibijbprsvdsei|-|Mozilla/5.0 (Linux; Android 9; HWI-AL00 Build/HUAWEIHWI-A00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36|-|- 需求 在_program_等于access时,对字段content做一次PSV(pipe分隔的解析),然后丢弃content字段。 request: GET /css/mip-base.css HTTP/1.1这个字段需要拆分为request_method,http_version,以及request。 http_referer做url解码 time做格式化 解决方案 1、如果_program_是access,则执行e_psv(解析content内容,详细用法见全局操作函数部分)函数,并删除原始字段content内容 e_if(e_search("_program_==access"), e_compose(e_psv("content", "remote_addr, time_local,host,request,status,request_time,body_bytes_sent,upstream_addr,upstream_status, upstream_response_time,http_referer,http_x_forwarded_for,http_user_agent,session_id,guid", restrict=True), e_drop_fields("content"))) 返回的日志为: __source__: 1.2.3.4 __tag__:__client_ip__: 2.3.4.5 __tag__:__receive_time__: 1562845168 __topic__: _facility_: 1 _priority_: 14 _program_: access _severity_: 6 body_bytes_sent: 6404 guid: - host: m.zf.cn http_referer: https://yz.m.sm.cn/s?q=%E8%9B%8B%E8%8A%B1%E9%BE%99%E9%A1%BB%E9%9D%A2%E7%9A%84%E5%81%9A%E6%B3%95&from=wy878378&uc_param_str=dnntnwvepffrgibijbprsvdsei http_user_agent: Mozilla/5.0 (Linux; Android 9; HWI-AL00 Build/HUAWEIHWI-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 http_x_forwarded_for: - remote_addr: 10.64.10.20 request: GET /zf/11874.html HTTP/1.1 request_time: 0.077 session_id: - status: 200 time_local: 10/Jun/2019:11:32:16 +0800 topic: syslog-forwarder upstream_addr: 10.11.186.82:8001 upstream_response_time: 0.060 upstream_status: 200 2、使用e_regex函数拆分request,解析成request_method,request,http_version e_regex("request",r"^(?P<request_method>\w+) (?P<request>.+) (?P<http_version>\w+/[\d\.]+)$") 返回的日志为: request: /zf/11874.html request_method: GET http_version: HTTP/1.1 3、对http_referer做url解码 e_set("http",url_decoding("http_referer")) 返回的日志为: http: https://yz.m.sm.cn/s?q=蛋花龙须面的做法&from=wy878378&uc_param_str=dnntnwvepffrgibijbprsvdsei 4、对时间做格式化处理 e_set("time_local",dt_strptime(v("time"),"%d/%b/%Y:%H:%M:%S +0800")) 返回的日志为: time_local: 2019-06-13 13:45:11 5、综上解决方案具体如下: e_if(e_search("_program_==access"), e_compose(e_psv("content", "remote_addr, time_local,host,request,status,request_time,body_bytes_sent,upstream_addr,upstream_status, upstream_response_time,http_referer,http_x_forwarded_for,http_user_agent,session_id,guid", restrict=True), e_drop_fields("content"))) e_regex("request",r"^(?P<request_method>\w+) (?P<request>.+) (?P<http_version>\w+/[\d\.]+)$") e_set("http",url_decoding("http_referer")) e_set("time_local",dt_strptime(v("time"),"%d/%b/%Y:%H:%M:%S +0800")) 输出的日志 __source__: 1.2.3.4 __tag__:__client_ip__: 2.3.4.5 __tag__:__receive_time__: 1562840879 __topic__: _facility_: 1 _priority_: 14 _program_: access _severity_: 6 body_bytes_sent: 6404 guid: - host: m.zf.cn http_referer: https://yz.m.sm.cn/s?q=%E8%9B%8B%E8%8A%B1%E9%BE%99%E9%A1%BB%E9%9D%A2%E7%9A%84%E5%81%9A%E6%B3%95&from=wy878378&uc_param_str=dnntnwvepffrgibijbprsvdsei http_user_agent: Mozilla/5.0 (Linux; Android 9; HWI-AL00 Build/HUAWEIHWI-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 http_x_forwarded_for: - remote_addr: 10.64.10.20 request: GET /zf/11874.html HTTP/1.1 request_time: 0.077 session_id: - status: 200 time_local: 10/Jun/2019:11:32:16 +0800 topic: syslog-forwarder upstream_addr: 10.11.186.82:8001 upstream_response_time: 0.060 upstream_status: 200 http: https://yz.m.sm.cn/s?q=蛋花龙须面的做法&from=wy878378&uc_param_str=dnntnwvepffrgibijbprsvdsei 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
 本部分实践案例主要是根据在实际工作中的工单需求产生。接下来将从工单需求,加工编排(解决方案)等几个方面给读者解答如何使用LOG DSL编排解决任务需求。 场景:非标准JSON对象转JSON展开 需要对收集的dict数据进行二次嵌套展开操作。解决方案是先将dict数据转成json数据,然后使用e_json函数进行展开即可。 原始日志 在控制台收集到的日志格式是dict格式,如下所示: content: { 'referer': '-', 'request': 'GET /phpMyAdmin', 'status': 404, 'data-1': { 'aaa': 'Mozilla', 'bbb': 'asde' }, 'data-2': { 'up_adde': '-', 'up_host': '-' } } LOG DSL编排 1、首先是对上述content数据做转json格式数据处理 e_set("content_json",str_replace(ct_str(v("content")),"'",'"')) 此时经过处理后的日志为: content: { 'referer': '-', 'request': 'GET /phpMyAdmin', 'status': 404, 'data-1': { 'aaa': 'Mozilla', 'bbb': 'asde' }, 'data-2': { 'up_adde': '-', 'up_host': '-' } } content_json: { "referer": "-", "request": "GET /phpMyAdmin", "status": 404, "data-1": { "aaa": "Mozilla", "bbb": "asde" }, "data-2": { "up_adde": "-", "up_host": "-" } } 2、对经过处理后的标准化的content_json数据进行展开。比如要展开第一层只需要设定JSON中的depth参数为1即可 e_json("content_json",depth=1,fmt='full') 此时的展开的的日志为: content_json.data-1: {"aaa": "Mozilla", "bbb": "asde"} content_json.data-2: {"up_adde": "-", "up_host": "-"} content_json.referer: - content_json.request: GET /phpMyAdmin content_json.status: 404 如果depth设置为2,则展开的日志为: content_json.data-1.aaa: Mozilla content_json.data-1.bbb: asde content_json.data-2.up_adde: - content_json.data-2.up_host: - content_json.referer: - content_json.request: GET /phpMyAdmin content_json.status: 404 3、综上LOG DSL规则可以如以下形式: e_set("content_json",str_replace(ct_str(v("content")),"'",'"')) e_json("content_json",depth=2,fmt='full') 加工后数据 加工后的数据是按照depth为2处理的,具体形式如下: content: { 'referer': '-', 'request': 'GET /phpMyAdmin', 'status': 404, 'data-1': { 'aaa': 'Mozilla', 'bbb': 'asde' }, 'data-2': { 'up_adde': '-', 'up_host': '-' } } content_json: { "referer": "-", "request": "GET /phpMyAdmin", "status": 404, "data-1": { "aaa": "Mozilla", "bbb": "asde" }, "data-2": { "up_adde": "-", "up_host": "-" } } content_json.data-1.aaa: Mozilla content_json.data-1.bbb: asde content_json.data-2.up_adde: - content_json.data-2.up_host: - content_json.referer: - content_json.request: GET /phpMyAdmin content_json.status: 404 场景:其他格式的文本转JSON格式展开 对于一些非标准的json格式数据,如果进行展开操作可以考虑组合规则的形式进行操作 原始日志 原始日志收集到的格式如以下格式: content : { "pod" => { "name" => "crm-learning-follow-7bc48f8b6b-m6kgb" }, "node" => { "name" => "tw5" }, "labels" => { "pod-template-hash" => "7bc48f8b6b", "app" => "crm-learning-follow" }, "container" => { "name" => "crm-learning-follow" }, "namespace" => "testing1" } LOG DSL编排 1、首先对日志格式进行转换json形式,可以使用str_logtash_config_normalize函数进行转换,操作如下: e_set("normalize_data",str_logtash_config_normalize(v("content"))) 2、展开操作可以使用JSON函数,具体如下: e_json("normalize_data",depth=1,fmt='full') 3、综上LOG DSL规则可以如以下形式: e_set("normalize_data",str_logtash_config_normalize(v("content"))) e_json("normalize_data",depth=1,fmt='full') 加工后数据 content : { "pod" => { "name" => "crm-learning-follow-7bc48f8b6b-m6kgb" }, "node" => { "name" => "tw5" }, "labels" => { "pod-template-hash" => "7bc48f8b6b", "app" => "crm-learning-follow" }, "container" => { "name" => "crm-learning-follow" }, "namespace" => "testing1" } normalize_data: { "pod": { "name": "crm-learning-follow-7bc48f8b6b-m6kgb" }, "node": { "name": "tw5" }, "labels": { "pod-template-hash": "7bc48f8b6b", "app": "crm-learning-follow" }, "container": { "name": "crm-learning-follow" }, "namespace": "testing1" } normalize_data.container.container: {"name": "crm-learning-follow"} normalize_data.labels.labels: {"pod-template-hash": "7bc48f8b6b", "app": "crm-learning-follow"} normalize_data.namespace: testing1 normalize_data.node.node: {"name": "tw5"} normalize_data.pod.pod: {"name": "crm-learning-follow-7bc48f8b6b-m6kgb"} 场景:部分文本特殊编码转换 在真实的工作环境下,总会遇到一些十六进制字符,需要对其解码才能正常阅读。因此,对于一些十六进制字符进行转义操作可是使用str_hex_escape_encode函数。 原始日志 content : "\xe4\xbd\xa0\xe5\xa5\xbd" LOG DSL编排 e_set("hex_encode",str_hex_escape_encode(v("content"))) 加工后数据 content : "\xe4\xbd\xa0\xe5\xa5\xbd" hex_encode : "你好" 场景:XML字段展开 测试日志 在工作中也会时不时遇到各种类型数据,比如xml数据。如果要展开xml数据可是使用xml_to_json函数处理。 str : <?xmlversion="1.0"?> <data> <countryname="Liechtenstein"> <rank>1</rank> <year>2008</year> <gdppc>141100</gdppc> <neighborname="Austria"direction="E"/> <neighborname="Switzerland"direction="W"/> </country> <countryname="Singapore"> <rank>4</rank> <year>2011</year> <gdppc>59900</gdppc> <neighborname="Malaysia"direction="N"/> </country> <countryname="Panama"> <rank>68</rank> <year>2011</year> <gdppc>13600</gdppc> <neighborname="Costa Rica"direction="W"/> <neighborname="Colombia"direction="E"/> </country> </data> LOG DSL编排 e_set("str_json",xml_to_json(v("str"))) 加工后的日志 str : <?xmlversion="1.0"?> <data> <countryname="Liechtenstein"> <rank>1</rank> <year>2008</year> <gdppc>141100</gdppc> <neighborname="Austria"direction="E"/> <neighborname="Switzerland"direction="W"/> </country> <countryname="Singapore"> <rank>4</rank> <year>2011</year> <gdppc>59900</gdppc> <neighborname="Malaysia"direction="N"/> </country> <countryname="Panama"> <rank>68</rank> <year>2011</year> <gdppc>13600</gdppc> <neighborname="Costa Rica"direction="W"/> <neighborname="Colombia"direction="E"/> </country> </data> str_dict :{ "data": { "country": [{ "@name": "Liechtenstein", "rank": "1", "year": "2008", "gdppc": "141100", "neighbor": [{ "@name": "Austria", "@direction": "E" }, { "@name": "Switzerland", "@direction": "W" }] }, { "@name": "Singapore", "rank": "4", "year": "2011", "gdppc": "59900", "neighbor": { "@name": "Malaysia", "@direction": "N" } }, { "@name": "Panama", "rank": "68", "year": "2011", "gdppc": "13600", "neighbor": [{ "@name": "Costa Rica", "@direction": "W" }, { "@name": "Colombia", "@direction": "E" }] }] } } 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
本部分实践案例,旨在通过一种场景多种解决方案的对比,选择出一种最快最好的解决方案。本专题主要讲解正则解析方面的场景实践。 场景:解析Nginx日志 以下以一条Nginx日志为例,向大家展开如何解析Nginx日志的多种方案。 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET /atom.xml HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" 需求 1、从Nginx日志中提取出clientip、ident、auth、timestamp、verb、request、url、httpversion、response、bytes、referrer、agent信息2、对解析出来的url进行再提取,提取出url_proto、url_host、url_param3、对解析出来的url_param进行再提取,提取出url_path、url_query信息 原始日志 在控制台收集到的日志格式是string格式,如下所示: __source__: 30.43.16.15 __tag__:__client_ip__: 12.120.75.140 __tag__:__receive_time__: 1563443076 content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" LOG DSL编排 本部分将提供两种方案,解决以上需求。 方案一:正则解析 1、针对需求1解析Nginx日志的加工编排如下: e_regex("content",r'(?P<ip>\d+\.\d+\.\d+\.\d+)( - - \[)(?P<datetime>[\s\S]+)\] \"(?P<verb>[A-Z]+) (?P<request>[\S]*) (?P<protocol>[\S]+)["](?P<code>\d+) (?P<sendbytes>\d+) ["](?P<refere>[\S]*)["] ["](?P<useragent>[\S\s]+)["]') 预览处理日志: ip: 203.208.60.89 datetime: 04/Jan/2019:16:06:38 +0800 verb: GET request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 protocol: HTTP/1.1 code: 200 sendbytes: 273932 refere: - useragent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) 2、针对需求2解析第一步加工后得到的url的加工编排如下: e_regex('url',r'(?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)') 预览处理日志: url_proto: http url_domain: cdn1cdedge0001.coxlab.net uri_param: /_astats?application=&inf.name=eth0 3、针对需求3解析第二步得到的url参数的加工编排如下: e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)') 预览处理日志: uri_path: /_astats uri_query: application=&inf.name=eth0 4、综上LOG DSL规则可以如以下形式: """第一步:初步解析Nginx日志""" e_regex("content",r'(?P<ip>\d+\.\d+\.\d+\.\d+)( - - \[)(?P<datetime>[\s\S]+)\] \"(?P<verb>[A-Z]+) (?P<request>[\S]*) (?P<protocol>[\S]+)["](?P<code>\d+) (?P<sendbytes>\d+) ["](?P<refere>[\S]*)["] ["](?P<useragent>[\S\s]+)["]') """第二步:解析第一步得到的url""" e_regex('url',r'(?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$)') """第三步:解析第二步的到的url参数""" e_regex('uri_param',r'(?P<uri_path>\/\_[a-z]+[^?])\?(?<uri_query>(.+)$)') 预览综上处理后的日志如下: __source__: 30.43.16.15 __tag__:__client_ip__: 12.120.75.140 __tag__:__receive_time__: 1563443076 content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" ip: 203.208.60.89 datetime: 04/Jan/2019:16:06:38 +0800 verb: GET request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 protocol: HTTP/1.1 code: 200 sendbytes: 273932 refere: - useragent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) url_proto: http url_domain: cdn1cdedge0001.coxlab.net uri_param: /_astats?application=&inf.name=eth0 uri_path: /_astats uri_query: application=&inf.name=eth0 方案二:Grok解析 1、使用grok模式解析Nginx日志,只需要COMBINEDAPACHELOG模式即可。 模式 规则 说明 COMMONAPACHELOG `%{IPORHOST:clientip} %{HTTPDUSER:ident} %{USER:auth} [%{HTTPDATE:timestamp}] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})? %{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes} -)` 解析出clientip、ident、auth、timestamp、verb、request、httpversion、response、bytes字段内容 COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent} 解析出上一行中所有字段,另外还解析出referrer、agent字段 针对需求1解析Nginx日志的加工编排如下: e_regex('content',grok('%{COMBINEDAPACHELOG}')) 预览处理日志: clientip: 203.208.60.89 ident: - auth: - timestamp: 04/Jan/2019:16:06:38 +0800 verb: GET request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 httpversion: 1.1 response: 200 bytes: 273932 referrer: "-" agent: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" 2、解析request只需要使用grok的以下几种模式组合即可完成解析: 模式 规则 说明 URIPROTO [A-Za-z]+(\+[A-Za-z+]+)? 匹配url中的头部分,如http://hostname.domain.tld/_astats?application=&inf.name=eth0会匹配到http USER [a-zA-Z0-9._-]+ 匹配字母、数字和._-组合 URIHOST %{IPORHOST}(?::%{POSINT:port})? 匹配IPORHOST和POSINT URIPATHPARAM %{URIPATH}(?:%{URIPARAM})? 匹配url参数部分 针对需求2解析第一步加工后得到的request的加工编排如下: e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) 预览处理日志: url_proto: http url_domain: cdn1cdedge0001.coxlab.net uri_param: /_astats?application=&inf.name=eth0 3、解析url_param可以使用grok的以下模式即可完成解析: 模式 规则 说明 GREEDYDATA .* 匹配任意或多个除换行符 针对需求3解析第二步得到的url参数的加工编排如下: e_regex('url_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}")) 预览处理日志: uri_path: /_astats uri_query: application=&inf.name=eth0 4、综上LOG DSL规则可以如以下形式: """第一步:初步解析Nginx日志""" e_regex('content',grok('%{COMBINEDAPACHELOG}')) """第二步:解析第一步得到的url""" e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) """第三步:解析第二步的到的url参数""" e_regex('url_param',grok("%{GREEDYDATA:uri_path}\?%{GREEDYDATA:uri_query}")) 预览综上处理后的日志如下: __source__: 30.43.16.15 __tag__:__client_ip__: 12.120.75.140 __tag__:__receive_time__: 1563443076 content: 203.208.60.89 - - [04/Jan/2019:16:06:38 +0800] "GET http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 HTTP/1.1" 200 273932 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" clientip: 203.208.60.89 ident: - auth: - timestamp: 04/Jan/2019:16:06:38 +0800 verb: GET request: http://cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 httpversion: 1.1 response: 200 bytes: 273932 referrer: "-" agent: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" url_proto: http url_domain: cdn1cdedge0001.coxlab.net uri_param: /_astats?application=&inf.name=eth0 uri_path: /_astats uri_query: application=&inf.name=eth0 对比 综上所述,可以看出使用正则解析和Grok模式解析Nginx日志两种方案优劣。 正则方案 对于不是很熟悉的开发人员使用正则解析日志效率会比较低,而且学习成本会比较大,另外一点是灵活性不够,比如在request内容改成 http://twiss@cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 那么还使用以上正则 (?P<url_proto>(\w+)):\/\/(?P<url_domain>[a-z0-9.]*[^\/])(?P<uri_param>(.+)$) request则会解析成 url_proto: http url_domain: twiss@ uri_param: cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 很显然,如果还使用原来的正则模式的话,解析出来的内容是不符合要求的。因此,还需要修改正则模式才能正常解析。由此可见,灵活的使用正则的解析的难度比较高。 Grok方案 Grok模式解析对于开发人员是友好的,对于非开发人员亦然如此。Grok学习成本低,只需要了解哪些模式代表的哪些字段类型就可以轻松解析你想解析的日志内容。Grok学习曲线低,可以通过用户文档中GROK参考来学习实践。 Grok灵活性高,比如还是以上述正则方案中例子为参考: request内容改成 http://twiss@cdn1cdedge0001.coxlab.net/_astats?application=&inf.name=eth0 Grok模式不变 e_regex('request',grok("%{URIPROTO:uri_proto}://(?:%{USER:user}(?::[^@]*)?@)?(?:%{URIHOST:uri_domain})?(?:%{URIPATHPARAM:uri_param})?")) request则会解析成 url_proto: http user: twiss url_domain: cdn1cdedge0001.coxlab.net uri_param: /_astats?application=&inf.name=eth0 在Grok模式不变的情况下,request添加user的情况下,还是能够正确解析出正确的日志内容。 结论 从灵活性、高效性、低成本、学习曲线等方面对比, GROK都要比直接使用正则表达式要有优势. 但是GROK模式的本质其实还是正则表达式, 但是数据加工已经提供了400种模式包装了场景的正则, 建议优先使用. 当然在需要的情况下, 也可以混合使用GROK与正则甚至自行编写需要的正则. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概况 syslog是一种工业标准的协议,可用来记录设备的日志。在UNIX系统,路由器、交换机等网络设备中,系统日志(System Log)记录系统中任何时间发生的大小事件。管理者可以通过查看系统记录,随时掌握系统状况。UNIX的系统日志是通过syslogd这个进程记录系统有关事件记录,也可以记录应用程序运作事件。通过适当的配置,还可以实现运行syslog协议的机器间通信,通过分析这些网络行为日志,藉以追踪掌握与设备和网络有关的状况。 问题 在Unix类操作系统上,syslog广泛应用于系统日志。syslog日志消息既可以记录在本地文件中,也可以通过网络发送到接收syslog的服务器。接收syslog的服务器可以对多个设备的syslog消息进行统一的存储,或者解析其中的内容做相应的处理。常见的应用场景是网络管理工具、安全管理系统、日志审计系统。但是,长期以来,没有一个标准来规范syslog的格式,导致syslog的格式是非常随意的。最坏的情况下,根本就没有任何格式,导致程序不能对syslog 消息进行解析,只能将它看作是一个字符串。对syslog日志解析一直是比较令人头疼的事情,本篇介绍如何使用LOG DSL中的grok高效快捷的去解析不同格式的syslog日志 解决方案 syslog协议标准 目前业界存在常见两种syslog日志协议,一个是2009年起草协议RFC524,另外一个是2001年的RFC3164协议。以下,为大家介绍这两种协议的不同之处,以便能够在以后实践中能够灵活应用grok解析syslog日志。 RFC5424协议 RFC5424协议包含以下字段信息,具体信息可以参考官方协议(https://tools.ietf.org/html/rfc5424) PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID 以下通过几个示例来解释以上字段代表信息: """ Example1: <34>1 2019-07-11T22:14:15.003Z aliyun.example.com ali - ID47 - BOM'su root' failed for lonvick on /dev/pts/8 """ PRI -- 34 VERSION -- 1 TIMESTAMP -- 2019-07-11T22:14:15.003Z HOSTNAME -- aliyun.example.com APP-NAME -- ali PROCID -- 无 MSGID -- ID47 MESSAGE -- 'su root' failed for lonvick on /dev/pts/8 """ Example-2: <165>1 2019-07-11T22:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts. """ PRI -- 165 VERSION -- 1 TIMESTAMP -- 2019-07-11T05:14:15.000003-07:00 HOSTNAME -- 192.0.2.1 APP-NAME -- myproc PROCID -- 8710 STRUCTURED-DATA -- “-” MSGID -- “-” MESSAGE -- "%% It's time to make the do-nuts." """ Example3: - with STRUCTURED-DATA <165>1 2019-07-11T22:14:15.003Z aliyun.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= "Application" eventID="1011"] BOMAn application event log entry... """ PRI -- 165 VERSION -- 1 TIMESTAMP -- 2019-07-11T22:14:15.003Z HOSTNAME -- aliyun.example.com APP-NAME -- evntslog PROCID -- "-" MSGID -- ID47 STRUCTURED-DATA -- [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] MESSAGE -- An application event log entry... RFC3164协议 RFC3164协议包含以下字段信息,具体信息可以参考官方协议(https://tools.ietf.org/html/rfc3164) PRI HEADER[TIME HOSTNAME] MSG 以下通过几个示例来解释以上字段代表信息: """ <30>Oct 9 22:33:20 hlfedora auditd[1787]: The audit daemon is exiting. """ PRI -- 30 HEADER - TIME -- Oct 9 22:33:20 - HOSTNAME -- hlfedora MSG - TAG -- auditd[1787] - Content --The audit daemon is exiting. grok解析syslog常见格式 以上内容介绍了syslog,接下来进入实践解析阶段。在syslog配置文件中为用户定制了几种常用格式的syslog,以下使用grok一一解析。此外,syslog采集到logstore上要使用文本格式采集。具体的grok规则可参考grok模式参考: TraditionalFormat格式 日志内容形式如下: May 5 10:20:57 iZbp1a65x3r1vhpe94fi2qZ systemd: Started System Logging Service. LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: May 5 10:20:57 iZbp1a65x3r1vhpe94fi2qZ systemd: Started System Logging Service. 处理后日志格式: receive_time: 1558663265 __topic__: content: May 5 10:20:57 iZbp1a65x3r1vhpe94fi2qZ systemd: Started System Logging Service. timestamp: May 5 10:20:57 logsource: iZbp1a65x3r1vhpe94fi2qZ program: systemd message: Started System Logging Service. """ e_regex('content', grok('%{SYSLOGBASE} %{GREEDYDATA:message}')) FileFormat格式 日志内容形式如下: 2019-05-06T09:26:07.874593+08:00 iZbp1a65x3r1vhpe94fi2qZ root: 834753 LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: 2019-05-06T09:26:07.874593+08:00 iZbp1a65x3r1vhpe94fi2qZ root: 834753 处理后日志格式: receive_time: 1558663265 __topic__: content: 2019-05-06T09:26:07.874593+08:00 iZbp1a65x3r1vhpe94fi2qZ root: 834753 timestamp: 2019-05-06T09:26:07.874593+08:00 hostname: iZbp1a65x3r1vhpe94fi2qZ program: root message: 834753 """ e_regex('content',grok('%{TIMESTAMP_ISO8601:timestamp} %{SYSLOGHOST:hostname} %{SYSLOGPROG} %{GREEDYDATA:message}')) RSYSLOG_SyslogProtocol23Format格式 日志内容形式如下: <13>1 2019-05-06T11:50:16.015554+08:00 iZbp1a65x3r1vhpe94fi2qZ root - - - twish LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: <13>1 2019-05-06T11:50:16.015554+08:00 iZbp1a65x3r1vhpe94fi2qZ root - - - twish 处理后日志格式: receive_time: 1558663265 __topic__: content: <13>1 2019-05-06T11:50:16.015554+08:00 iZbp1a65x3r1vhpe94fi2qZ root - - - twish priority: 13 version: 1 timestamp: 2019-05-06T11:50:16.015554+08:00 hostname: iZbp1a65x3r1vhpe94fi2qZ program: root message: twish """ e_regex('content',grok('%{POSINT:priority}>%{NUMBER:version} %{TIMESTAMP_ISO8601:timestamp} %{SYSLOGHOST:hostname} %{PROG:program} - - - %{GREEDYDATA:message}')) RSYSLOG_DebugFormat格式 日志内容形式如下: 2019-05-06T14:29:37.558854+08:00 iZbp1a65x3r1vhpe94fi2qZ root: environment LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: 2019-05-06T14:29:37.558854+08:00 iZbp1a65x3r1vhpe94fi2qZ root: environment 处理后日志格式: receive_time: 1558663265 __topic__: content: 2019-05-06T14:29:37.558854+08:00 iZbp1a65x3r1vhpe94fi2qZ root: environment timestamp: 2019-05-06T14:29:37.558854+08:00 hostname: iZbp1a65x3r1vhpe94fi2qZ program: root message: environment """ e_regex('content',grok('%{TIMESTAMP_ISO8601:timestamp} %{SYSLOGHOST:hostname} %{SYSLOGPROG} %{GREEDYDATA:message}')) 自此,syslog配置文件常见log日志文件格式解析已解析完成,接下来介绍两种FluentSyslog日志格式。 FluentRFC5424格式 日志内容形式如下: <16>1 2013-02-28T12:00:00.003Z 192.168.0.1 fluentd 11111 ID24224 [exampleSDID@20224 iut='3' eventSource='Application' eventID='11211] Hi, from Fluentd! LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: <16>1 2019-02-28T12:00:00.003Z 192.168.0.1 aliyun 11111 ID24224 [exampleSDID@20224 iut='3' eventSource='Application' eventID='11211] Hi, from Fluentd! 处理后日志格式: receive_time: 1558663265 __topic__: content: <16>1 2019-02-28T12:00:00.003Z 192.168.0.1 aliyun 11111 ID24224 [exampleSDID@20224 iut='3' eventSource='Application' eventID='11211] Hi, from aliyun! priority: 16 version: 1 timestamp: 2019-02-28T12:00:00.003Z hostname: 192.168.0.1 ident: aliyun pid: 1111 msgid: ID24224 extradata: [exampleSDID@20224 iut='3' eventSource='Application' eventID='11211] message: Hi, from aliyun! """ e_regex('content',grok('%{POSINT:priority}>%{NUMBER:version} %{TIMESTAMP_ISO8601:timestamp} %{SYSLOGHOST:hostname} %{WORD:ident} %{NUMBER:pid} %{USERNAME:msgid} (?P<extradata>(\[(.*)\]|[^ ])) %{GREEDYDATA:message}')) FluentRFC3164格式 日志内容形式如下: <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test LOG DSL编排规则处理: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: <6>Feb 28 12:00:00 192.168.0.1 aliyun[11111]: [error] Syslog test 处理后日志格式: receive_time: 1558663265 __topic__: content: <6>Feb 28 12:00:00 192.168.0.1 aliyun[11111]: [error] Syslog test priority: 6 timestamp: Feb 28 12:00:00 hostname: 192.168.0.1 ident: aliyun pid: [1111] level: [error] message: Syslog test """ e_regex('content', grok('%{POSINT:priority}>%{SYSLOGTIMESTAMP:timestamp} %{SYSLOGHOST:hostname} %{WORD:ident}(?P<pid>(\[(\d+)\]|[^:])): (?P<level>(\[(\w+)\]|[^ ])) %{GREEDYDATA:message}')) 对priority做进一步解析 针对,以上解析过后的日志内容还可以对priority进一步解析,并且匹配出解析出来的facility和serverity信息,使用RFC5424协议更多参考e_syslogrfc函数,如下: """ 处理前日志格式: receive_time: 1558663265 __topic__: content: <13>1 2019-05-06T11:50:16.015554+08:00 iZbp1a65x3r1vhpe94fi2qZ root - - - twish priority: 13 version: 1 timestamp: 2019-05-06T11:50:16.015554+08:00 hostname: iZbp1a65x3r1vhpe94fi2qZ program: root message: twish 处理后日志格式: receive_time: 1558663265 __topic__: content: <13>1 2019-05-06T11:50:16.015554+08:00 iZbp1a65x3r1vhpe94fi2qZ root - - - twish priority: 13 version: 1 timestamp: 2019-05-06T11:50:16.015554+08:00 hostname: iZbp1a65x3r1vhpe94fi2qZ program: root message: twish _facility_: 1 _severity_: 5 _severitylabel_: Notice: normal but significant condition _facilitylabel_: user-level messages """ e_syslogrfc("priority","SYSLOGRFC5424") 总结 通过以上内容,覆盖了syslog各种常见日志解析。syslog格式还有很多种,解析方法还是推荐使用LOG DSL的grok解析效率更快更高。grok还可以解析其他类型的log内容,比如Apache、Http错误日志等等。如果解析出来之后的message或者body. 还需要进一步解析的,有各种方式:grok, 正则或者CSV等等 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 在跨账号同Region下的不同logstore之间操作数据并汇总数据. 日志服务数据目前支持多源logstore汇总到一个目标的方式是通过对每一个源logstore配置一份数据加工任务来实现的. 注意 目前数据加工仅支持同Region下分发 原始日志 """ 账号1的logstore中的日志 Prject区域:英国(伦敦) Project名称:Project_1 logstore名称: Logstore_1 """ "日志1" request_id: 1 http_host: m1.abcd.com http_status: 200 request_method: GET request_uri: /pic/icon.jpg "日志2" request_id: 2 http_host: m2.abcd.com http_status: 301 request_method: POST request_uri: /data/data.php """ 账号2的logstore中的日志 Prject区域:英国(伦敦) Project名称:Project_2 logstore名称: Logstore_2 """ "日志1" request_id: 3 host: m3.abcd.com status: 404 request_method: GET request_uri: /category/abc/product_id "日志2" request_id: 4 host: m4.abcd.com status: 200 request_method: GET request_uri: /data/index.html 汇总目标 将账号1的Logstore_1和账号2下的Logstore_2中所有http_status为200日志事件汇总到账号3下的Logstore_3中。 统一两个Logstore中日志事件的字段表达(host -> http_host, status -> http_status) LOG DSL规则 首先在账号1的Logstore_1中配置如下加工规则 e_if(e_match("http_status", "200"), e_output("target_logstore")) 并且在该加工规则的任务配置项中配置存储目标target_logstore为账号3下面的Logstore_3。 接着在账号2的Logstore_2中配置如下加工规则 e_if(e_match("status", "200"), e_compose(e_rename("status", "http_status", "host", "http_host"), e_output("target_logstore"))) 同账号1一样,在该加工规则的任务配置项中配置存储目标target_logstore为账号3下面的Logstore_3。 加工后的日志 """ 账号3的logstore中的日志 Prject区域:英国(伦敦) Project名称:Project_3 logstore名称: Logstore_3 """ "日志1" request_id: 1 http_host: m1.abcd.com http_status: 200 request_method: GET request_uri: /pic/icon.jpg "日志2" request_id: 4 http_host: m4.abcd.com http_status: 200 request_method: GET request_uri: /data/index.html 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 数据分发主要包含两种方法:跨账号多目标Logstore的分发和动态目标Logstore的分发, 其特点如下表. 需要时也可以合并两种方法. 注意 目前数据加工仅支持同Region下分发 方法 优点 缺点 配置多个目标Logstore 支持不同账号(AK)的目标Logstore 多个目标时, 配置与使用较为繁琐. 目标一般在代码中静态指定. 目前最多20个目标. 配置少量目标, 再代码中重置project, logstore参数 目标project, logstore可以在LOG DSL规则中动态获取与设置. 可以发送超过20个以上目标. 秘钥使用目标中的配置, 不能动态改. 因此最多跨20个账号. 场景1:跨账号多目标Logstore的分发 原始日志 """ 以下日志都存储在同一个logstore中,该logstore的默认逻辑名为target0 """ "日志1" http_host: m1.abcd.com http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https "日志2" http_host: m2.abcd.com http_status: 301 request_method: POST request_uri: /data/data.php scheme: http "日志3" http_host: m3.abcd.com http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https "日志4" http_host: m4.abcd.com http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 分发目标 http_status为2XX的日志事件保留在源logstore target0中,并设置主题为success_event。 http_status为3XX的日志事件分发到logstore target1中,并设置主题为redirection_event。 http_status为4XX的日志事件分发到logstore target2中,并设置主题为unauthorized_event。 http_status为5XX的日志事件分发到logstore target3中,并设置主题为internal_server_error_event。 LOG DSL编排 e_switch(e_match("status", r"2\d+"), e_set("__topic__", "success_event"), e_match("status", r"3\d+"), e_compose(e_set("__topic__", "redirection_event"), e_output("target1")), e_match("status", r"4\d+"), e_compose(e_set("__topic__", "unauthorized_event"), e_output("target2")), e_match("status", r"5\d+"), e_compose(e_set("__topic__", "internal_server_error_event`"), e_output("target3")) ) 加工后的日志 """ 源logstore: target0 """ __topic__: success_event http_host: m1.abcd.com http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https """ 目标logstore: target1 """ __topic__: redirection_event http_host: m2.abcd.com http_status: 301 request_method: POST request_uri: /data/data.php scheme: http """ 目标logstore: target2 """ __topic__: unauthorized_event http_host: m3.abcd.com http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https """ 目标logstore: target3 """ __topic__: internal_server_error_event http_host: m4.abcd.com http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 不同Logstore的逻辑名(target0, target1等)可在LOG DSL规则配置项中的存储目标设置, 在这里可以通过AccessKey配置多账号下的Project和Logsotre. 调用e_output之后,对应事件会在源logstore被删除,如果输出之后仍然想在源Logstore中保留对应事件,可调用e_coutput。 场景2:动态目标Logstore的分发 原始日志 ''' project1和project2属于同一账号 project1下有两个Logstore:logstore1, logstore2 project2中也有两个Logstore:logstore1, lostore2 ''' "日志1" host: a.b.c.com project: project1 logstore: logstore1 http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https "日志2" host: m.n.q.com project: project1 logstore: logstore2 http_status: 301 request_method: POST request_uri: /data/data.php scheme: http "日志3" host: e.f.d.com project: project2 logstore: logstore1 http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https "日志4" host: p.q.t.com project: project2 logstore: logstore2 http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 分发目标 根据日志事件project和logstore字段值的不同,进行日志事件的动态分发。 为日志事件添加标签__tag:__type,值为dynamic_dispatch LOG DSL编排 e_output(project=v("project"), logstore=v("logstore"), tags={"type": "dynamic_dispatch"}) 动态目标分发默认使用的AccessKey信息是加工配置项中配置的第一个存储目标对应的AccessKey账号信息。 因此第一个存储目标的project和logstore信息对以上加工规则不会有影响,因为该加工规则中的e_output会动态提取project和logstore的值进行事件分发。 加工后日志 "project1 logstore1" __tag__:type: dynamic_dispatch host: a.b.c.com project: project1 logstore: logstore1 http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https "project1 logstore2" __tag__:type: dynamic_dispatch host: m.n.q.com project: project1 logstore: logstore2 http_status: 301 request_method: POST request_uri: /data/data.php scheme: http "project2 logstore1" __tag__:type: dynamic_dispatch host: e.f.d.com project: project2 logstore: logstore1 http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https "project2 logstore2" __tag__:type: dynamic_dispatch host: p.q.t.com project: project2 logstore: logstore2 http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 场景3:跨账号动态目标Logstore的分发 本场景是场景1和场景2的结合使用场景。 原始日志 """ project1属于账号1,该Project中有两个Logstore:logstore1, lostore2 project2属于账号2,该Project中有两个Logstore:logstore1, lostore2 """ "日志1" host: a.b.c.com project: project1 logstore: logstore1 http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https "日志2" host: m.n.q.com project: project1 logstore: logstore2 http_status: 301 request_method: POST request_uri: /data/data.php scheme: http "日志3" host: e.f.d.com project: project2 logstore: logstore1 http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https "日志4" host: p.q.t.com project: project2 logstore: logstore2 http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 分发目标 根据日志事件project和logstore字段值的不同,进行日志事件的动态分发。 分发的目标属于不同的账号, project1属于账号1,project2属于账号2。 LOG DSL编排 e_switch(e_match(v("project"), "project1"), e_output(name="target0", project=v("project"), logstore=v("logstore")), e_match(v("project"), "project2"), e_output(name="target1", project=v("project"), logstore=v("logstore"))) 在任务配置项中为存储目标target0和target1分别配置账号1和账号2的AccessKey信息 存储目标target0和target1的project和logstore信息对以上加工规则不会有影响,因为该加工规则中的e_output会动态提取project和logstore的值进行事件分发。 加工后日志 """ 账号1 project1 logstore1 """ host: a.b.c.com project: project1 logstore: logstore1 http_status: 200 request_method: GET request_uri: /pic/icon.jpg scheme: https """ 账号1 project1 logstore2 """ host: m.n.q.com project: project1 logstore: logstore2 http_status: 301 request_method: POST request_uri: /data/data.php scheme: http """ 账号2 project2 logstore1 """ host: e.f.d.com project: project2 logstore: logstore1 http_status: 404 request_method: GET request_uri: /category/abc/product_id scheme: https """ 账号2 project2 logstore2 """ host: p.q.t.com project: project2 logstore: logstore2 http_status: 504 request_method: GET request_uri: /data/index.html scheme: https 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
场景1:判断字段是否存在 原始日志 a: a_vlue b: // 空串 LOG DSL编排 方案一:使用e_has, e_not_has e_if(e_has("a"), e_set("has_a", true)) e_if(e_has("b"), e_set("has_b", true)) e_if(e_has("c"), e_set("has_c", true)) e_if(e_not_has("a"), e_set("not_has_a", true)) e_if(e_not_has("b"), e_set("not_has_b", true)) e_if(e_not_has("c"), e_set("not_has_c", true)) 方案二:使用e_search e_if(e_search("a: *"), e_set("has_a", true)) e_if(e_search("b: *"), e_set("has_b", true)) e_if(e_search("c: *"), e_set("has_c", true)) e_if(e_search("not a: *"), e_set("not_has_a", true)) e_if(e_search("not b: *"), e_set("not_has_b", true)) e_if(e_search("not c: *"), e_set("not_has_c", true)) 加工后日志 a: a_vlue b: // 空串 has_a: true has_b: true has_c: false not_has_a: false not_has_b: false not_has_c: true 以上两种方案都可用于判断字段是否存在,但是方案一更直观、易理解。 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。 场景2:判断字段值存在且不为空 原始日志 a: a_vlue b: // 空串 LOG DSL编排 方案一:使用字段取值函数v e_if(v("a"), e_set("not_empty_a", true)) e_if(v("b"), e_set("not_empty_b", true)) e_if(v("c"), e_set("not_empty_c", true)) 字段取值函数v,当对应字段存在且值不为空时,其自动转换的Bool值为true,否则为false 方案二:使用e_search # 至少一个字符 e_if(e_search('a: "?"'), e_set("not_empty_a", true)) e_if(e_search('b: "?"'), e_set("not_empty_b", true)) e_if(e_search('c: "?"'), e_set("not_empty_c", true)) # 正则 e_if(e_search('a~=".+"'), e_set("not_empty_a", true)) e_if(e_search('b~=".+"'), e_set("not_empty_b", true)) e_if(e_search('c~=".+"'), e_set("not_empty_c", true)) # 存在且不为空 e_if(e_search('a: * and not a==""'), e_set("not_empty_a", true)) e_if(e_search('b: * and not b==""'), e_set("not_empty_b", true)) e_if(e_search('c: * and not c==""'), e_set("not_empty_b", true)) 加工后日志 a: a_vlue b: // 空串 not_empty_a: true not_empty_b: false not_empty_c: false 以上三方案都可用于判断字段是否存在,但是方案一更简洁。 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。 场景3:判断字段值存在但为空 原始日志 a: a_vlue b: // 空串 LOG DSL编排 方案一:使用字段取值函数v e_if(op_and(e_has("a"), op_not(v("a"))), e_set("empty_a", true)) e_if(op_and(e_has("b"), op_not(v("b"))), e_set("empty_b", true)) e_if(op_and(e_has("c"), op_not(v("c"))), e_set("empty_c", true)) # 错误的方案 e_if(op_not(v("a")), e_set("empty_a", true)) e_if(op_not(v("b")), e_set("empty_b", true)) e_if(op_not(v("c")), e_set("empty_c", true)) 字段取值函数v,当对应字段存在且值不为空时,其自动转换的Bool值为true,否则为false. 但是只不存在是, 其返回None, op_not(None)时也是返回True. 方案二:使用e_search e_if(e_search('a==""'), e_set("empty_a", true)) e_if(e_search('b==""'), e_set("empty_b", true)) e_if(e_search('c==""'), e_set("empty_c", true)) # 以下是错误调用 e_if(e_search('a:""'), e_set("empty_a", true)) e_if(e_search('b:""'), e_set("empty_b", true)) 注意, 以上错误调用中, 因为:在e_search是部分查询, 字段存在时, 无论是否空串的情况下, 空串a: ""永远会真. 加工后日志 a: a_vlue b: // 空串 empty_a: false empty_b: true empty_b: false 以上方案中显然方案二更简洁. 以上加工规则中的两个e_if可通过e_if(条件1,操作1,条件2,操作2)的形式合并为一个e_if,此处为了易于读者阅读,拆成两个。 场景4:基于字段值的逻辑查询判断 原始日志 "日志1" http_host: m1.abcd.com status: 200 request_method: GET scheme: https header_length: 700 body_length: 1200 "日志2" http_host: m2.abcd.com status: 200 request_method: POST scheme: https header_length: 100 body_length: 800 "日志3" http_host: m3.abcd.com status: 200 request_method: GET scheme: http header_length: 700 body_length: 800 "日志4" http_host: m4.abcd.com status: 404 request_method: GET scheme: https header_length: 100 body_length: 300 加工需求1 为所有status字段值为200的日志事件, 添加一个字段type,其值为normal。 LOG DSL编排 e_if(e_match("status", "200"), e_set("type", "normal)) 或者 e_if(e_search("status==200"), e_set("type", "normal")) 在此相对简单的场景下,以上两种方式都可以,并无太大差别。 一般情况下status: 200也可以, 只是==更精准一些. 加工后的日志 "日志1" type: normal http_host: m1.abcd.com status: 200 request_method: GET scheme: https header_length: 700 body_length: 1200 "日志2" type: normal http_host: m2.abcd.com status: 200 request_method: POST scheme: https header_length: 100 body_length: 800 "日志3" type: normal http_host: m3.abcd.com status: 200 request_method: GET scheme: http header_length: 700 body_length: 800 "日志4" http_host: m4.abcd.com status: 404 request_method: GET scheme: https header_length: 100 body_length: 300 加工需求2 为所有status字段值为200 并且 request_method字段值为GET 并且 scheme字段值为https 的日志事件, 添加一个字段type,其值为normal。 LOG DSL编排 e_if(e_search("status==200 and request_method==GET and scheme==https"), e_set("type", "normal")) 或者 e_if(e_match_all("status", "200", "request_method","GET", "scheme", "https"), e_set("type", "normal")) 在此场景下,需要同时满足多个字段的匹配条件,可以使用e_search或e_match_all。 e_search用法相对更简洁一些。 一般情况下e_search的==也可以换成:如status: 200也可以, 只是==更精准一些. 加工后的日志 "日志1" type: normal http_host: m1.abcd.com status: 200 request_method: GET scheme: https header_length: 700 body_length: 1200 "日志2" http_host: m2.abcd.com status: 200 request_method: POST scheme: https header_length: 100 body_length: 800 "日志3" http_host: m3.abcd.com status: 200 request_method: GET scheme: http header_length: 700 body_length: 800 "日志4" http_host: m4.abcd.com status: 404 request_method: GET scheme: https header_length: 100 body_length: 300 加工需求3 为所有status字段值为200 或者 request_method字段值为GET 或者 scheme字段值为https 的日志事件, 添加一个字段type,其值为normal。 LOG DSL编排 e_if(e_search("status==200 or request_method==GET or scheme==https"), e_set("type", "normal")) 或者 e_if(e_match_any("status", "200", "request_method","GET", "scheme", "https"), e_set("type", "normal")) 在此场景下,需要满足多个字段的匹配条件中的其中一个,可以使用e_search或e_match_any。 e_search用法相对更简洁一些。 加工后的日志 "日志1" type: normal http_host: m1.abcd.com status: 200 request_method: GET scheme: https header_length: 700 body_length: 100 "日志2" type: normal http_host: m2.abcd.com status: 200 request_method: POST scheme: https header_length: 100 body_length: 800 "日志3" type: normal http_host: m3.abcd.com status: 200 request_method: GET scheme: http header_length: 700 body_length: 800 "日志4" type: normal http_host: m4.abcd.com status: 404 request_method: GET scheme: https header_length: 100 body_length: 1300 加工需求4 为所有status字段值为200 并且 request_method字段值为GET 并且 header_length和body_length的字段值之和小于等于1000的日志事件, 添加一个字段type,其值为normal。 LOG DSL编排 e_if(op_and(e_search("status: 200 and request_method: GET"), op_le(op_sum(v("header_length"), v("body_length")), 1000)), e_set("type", "normal")) 在更复杂的逻辑场景下,可通过e_search和其他表达式函数的组合来完成。 加工后的日志 "日志1" type: normal http_host: m1.abcd.com status: 200 request_method: GET scheme: https header_length: 700 body_length: 100 "日志2" http_host: m2.abcd.com status: 200 request_method: POST scheme: https header_length: 100 body_length: 800 "日志3" http_host: m3.abcd.com status: 200 request_method: GET scheme: http header_length: 700 body_length: 800 "日志4" http_host: m4.abcd.com status: 404 request_method: GET scheme: https header_length: 100 body_length: 1300 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
编写数据加工规则过程中,需根据场景选择不同的函数。LOG DSL函数的具体用法可参考数据加工语法参考。 场景1: 理解e_keep/KEEP的应用场景 默认规则中, 不做处理的事件都是保留. 所以KEEP通常只用于特定场景. 如果需要丢弃日志, 可以使用e_drop/e_drop传入条件或者使用e_if/e_if_else与DROP搭配使用: 如下比较区别: e_keep(e_search(...) ) # 满足保留, 不满足丢弃 e_drop(e_search(...) ) # 满足丢弃, 不满足保留 e_if(e_search("..."), KEEP) # 没有意义的代码, 满足后KEEP e_if_else(e_search("..."), KEEP, DROP) # 有意义 e_if(e_search("not ..."), DROP) # 有意义 场景2:尽可能使用函数自身提供的功能 子场景1:当原字段不存在或者为空时,为字段赋值 最佳实践 e_set("result", ".....value...", mode="fill") 非最佳实践 e_if(op_not(v("result")), e_set("result", ".....value...")) 字段的提取与覆盖模式,参考字段提取与覆盖模式提取与覆盖模式包含: fill – 当原字段不存在或者值为空时 add –当原字段不存在时设置 overwrite – 总是设置 fill/add/overwrite-auto – 当新值非空时才fill/add/overwrite 子场景2:使用GROK函数简化正则表达式 加工逻辑提取content字段中的IP地址 原日志格式 content:"ip address: 192.168.1.1" 提取目标: 192.168.1.1 最佳实践 e_regex("content", grok(r"\w+: (%{IP})"), "addr") # 或者 e_regex("content", grok(r"\w+: (%{IP:addr})")) 非最佳实践如下代码相对复杂一些: e_regex("content", grok(r"\w+: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"), "addr") 进一步参考Grok函数说明 子场景3:给多个字段赋值 最佳实践 e_set("k1", "v1", "k2", "v2", "k3", "v3", ....) 相对冗余的规则 e_set("k1", "v1") e_set("k2", "v2") e_set("k3", "v3") ... 场景3:使用e_compose减少重复判断 加工逻辑如果content是123,则首先执行删除age和name字段,然后重命名content为ctx 原日志格式 content:123 age:23 name:twiss 加工后的日志格式 ctx: 123 最佳实践一般情况下推荐使用e_compose组合相同逻辑下的操作. e_if(e_search("content==123"), e_compose(e_drop_fields("age|name"), e_rename("content", "ctx"))) 非最佳实践如下代码会多次判断, 一定程度上效率低一些. e_if(e_search("content==123"), e_drop_fields("age|name")) e_if(e_search("content==123"), e_rename("content", "ctx")) 进一步参考事件判断 场景4:注意表达式函数的参数类型 日志事件的字段和值在函数之间传递的过程中,始终都是字符串形式。非字符串类型的数据会被自动转化为字符串类型。因此在调用函数时,要注意各个函数能接收的参数类型。具体每个函数接收的参数类型可参考数据加工语法参考中对各个函数的解释。 样例1:op_add既可以接收字符串类型,也可以接受数值类型,因此不需要做参数类型转换 e_set("a", 1) e_set("b", 2) op_add(v("a"), v("b")) # 合法,返回值为"12" op_add(ct_int(v("a")), ct_int(v("b"))) # 合法, 返回值为3 样例2:op_sum, op_mul等函数只能接受数值类型,因此需要做数据类型转换,将字符串转化为数值型 e_set("a", 1) e_set("b", 2) op_sum(v("a"), v("b")) # 非法 op_sum(ct_int(v("a")), ct_int(v("b"))) # 合法, 返回值为3 op_mul(v("a"), v("b")) # 非法 op_mul(ct_int(v("a")), ct_int(v("b"))) # 合法, 返回值为2 日志事件在函数间传递,字段值都被自动转化为字符串类型,因此v("a"), v("b")都是字符串类型。而op_sum, op_mul等函数只能接受数值类型,可通过ct_int将字符串转化为整型,再传递给这类函数。 样例3: """ 加工逻辑: 将time1表示的日期时间转化为Unix时间戳 """ e_set("time1", "2019-06-03 2:41:26") e_set("time2", dt_totimestap(v("time1"))) # 非法 e_set("time2", dt_totimestap(dt_parse(v("time1")))) # 合法 e_set("time2", dt_parsetimestamp(v("time1"))) # 合法 dt_totimestap接收的参数类型为日期时间对象, 不是字符串。因此需要调用dt_parse将time1的字符串值类型转化为日期时间对象类型。 也可直接使用dt_parsetimestamp函数,它既可接收日期时间对象,也可接收字符串。 场景5:注意表达式函数的异常处理情况 许多表达式函数对于输入的参数有一定的要求, 如果不满足会报错. 也有一些会返回默认值容错, 需要特别注意, 这些默认值传递给后续的函数时可能进一步会报错. 例如: e_set("data_len": op_len(v("data"))) # 错误调用 e_set("data_len": op_len(v("data", default=""))) # 正确调用 如果字段data不存在时v("data")会返回None, 那么第一个调用会报错. 第二个通过默认值default给与一个合法的默认值, 让表达式可以不会报错. 场景6:理解e_if与e_switch的区别 e_if语法 e_if(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ....) 条件与操作的配对组合, 依次根据条件判断, 满足条件的进行相应操作, 不满足条件的不进行对应操作, 进行下一个条件判断. e_switch语法 e_switch(条件1, 操作1, 条件2, 操作2, 条件3, 操作3, ...., default=None) 条件与操作的配对组合, 依次根据条件判断, 满足条件的进行相应操作, 然后直接返回操作的结果, 不满足条件的不进行对应操作, 进行下一个条件判断. 如果没有任何条件满足, 并且配置了default参数的话, 执行default配置的操作并返回 样例 原始日志 status1: 200 status2: 404 e_if加工规则 e_if(e_match("status1", "200"), e_set("status1_info", "normal"), e_match("status2", "404"), e_set("status2_info", "error")) 加工后日志 status1: 200 status2: 404 status1_info: normal status2_info: error e_if会进行所有条件的判断,满足的则进行对应操作,不满足不进行对应操作。e_switch加工规则 e_switch(e_match("status1", "200"), e_set("status1_info", "normal"), e_match("status2", "404"), e_set("status2_info", "error")) 加工后日志 status1: 200 status2: 404 status1_info: normal e_switch只要有一个条件满足,就会返回结果,不会再进行后续条件判断。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 JMES是一个增强型的JSON查询计算语言, 不仅可以用于对JSON数据进行抽取, 还可以做计算与转换。关于JMES的语法详细介绍可以参考JMES Tutorial。 数据加工中的json_select函数和e_json函数支持以JMES语法对(字段或表达式表示JSON的)值提取或计算特定值。其用法为 json_select(字段, "jmes表达式", default=None, restrict=False) e_json(字段, jmes="jmes表达式", ...) 关于这两个函数的具体用法,可参考json_select函数和e_json函数。本文介绍JMES常用语法。 通过key来获取值 原始日志 "json_data":{ "a": "foo", "b": "bar", "c": "baz" } jmes语法 json_select(v("json_data"), "a") #返回值 foo json_select(v("json_data"), "b") #返回值 bar json_select(v("json_data"), "c") # 返回值 baz 通过层级访问来获取值 原始日志 "json_data":{"a": {"b": {"c": {"d":"value"} } } } jmes语法 json_select(v("json_data"), "a.b.c.d") # 返回值 value 通过索引来获取值 通过索引访问主要用于json中的数组数据 原始日志 "json_data":{ "a": ["b", "c", "d", "e", "f"] } jmes语法 json_select(v("json_data"), "a[2]") # 返回值 d 通过切片操作获取值 对于json中的数组数据还可以使用切片操作 原始日志 "json_data":{ "a": ["b", "c", "d", "e", "f"] } jmes语法 json_select(v("json_data"), "a[2: ]") # 返回值 ["d", "e", "f"] 多种用法综合使用 原始日志 "json_data":{ "a": { "b": { "c": [{"d": [0, [1, 2]]}, {"d": [3, 4]}] } } } jmes语法 json_select(v("json_data"), "a.b.c[0].d[1][0]") # 返回值 1 通过投影来获取值 原始日志1 "json_data":{ "people": [ {"first": "James", "last": "d"}, {"first": "Jacob", "last": "e"}, {"first": "Jayden", "last": "f"}, {"missing": "different"} ], "foo": {"bar": "baz"} } jmes语法 json_select(v("json_data"), "people[*].first") # 返回值 ["James","Jacob","Jayden"] 原始日志2 "json_data":{ "ops": { "functionA": {"numArgs": 2}, "functionB": {"numArgs": 3}, "functionC": {"variadic": true} } } jmes语法 json_select(v("json_data"), "ops.*.numArgs") # 返回值 [2, 3] 原始日志3 "json_data":{ "machines": [ {"name": "a", "state": "running"}, {"name": "b", "state": "stopped"}, {"name": "c", "state": "running"} ] } jmes语法 json_select(v("json_data"), "machines[?state=='running'].name") # 返回值 ["a", "c"] 多值选取 原始日志 "json_data":{ "people": [ { "name": "a", "state": {"name": "up"} }, { "name": "b", "state": {"name": "down"} } ] } jmes语法 json_select(v("json_data"), "people[].[name, state.name]") # 返回值[["a","up"],["b","down"]] 计算数组长度 对于json中的数组数据,jmes语法支持数组长度的计算 原始日志 "json_data":{ "a": ["b", "c", "d", "e", "f"] } jmes语法 json_select(v("json_data"), "length(a)") # 返回值 5 # length(a) > 0, 设置"no-empty"字段为true e_if(json_select(v("json_data"), "length(a)", default=0), e_set("no-empty", true)) 更多关于JMES的语法参考JMES Tutorial。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 本文提供400页完整手册下载,包括完整200+函数与400+GROK模式的规范和20+篇最佳实践.并持续更新. 下载地址日志服务数据加工: 用户手册 (最后更新 2019年8月5日) 目录结构: 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
作者: 唐恺 概述 本文介绍如何授权阿里云子账号操作数据加工功能. 所有资源都属于阿里云主账号,子账号配置日志服务数据加工必须由主账号为其授权,包括: 创建、删除、修改、更新数据加工任务及配置。 读取源Logstore数据以便在控制台进行加工任务预览。 有以下两种授权供选择: 日志服务管理权限 主账号登录控制台,为子账号授予全部管理权限AliyunLogFullAccess。 详细步骤请参考授权RAM 用户。 细粒度权限 主账号创建自定义权限策略,并为子账号授予该自定义权限策略。 登录RAM控制台。 在左侧导航选择权限管理 > 权限策略管理。 在权限策略管理页面单击新建权限策略。 输入策略名称和备注。 勾选脚本配置的配置模式。 请替换参数后,输入以下策略内容。 说明:请将替换为当前进行数据加工作业的日志服务Project名称。请将替换为当前进行数据加工作业的源Logstore名称。 { "Version":"1", "Statement":[ { "Effect":"Allow", "Action":[ "log:CreateLogStore", "log:CreateIndex", "log:UpdateIndex", "log:Get*" ], "Resource":"acs:log:*:*:project/<Project名称>/logstore/internal-etl-log" }, { "Action":[ "log:List*" ], "Resource":"acs:log:*:*:project/<Project名称>/logstore/*", "Effect":"Allow" }, { "Action":[ "log:Get*", "log:List*" ], "Resource":[ "acs:log:*:*:project/<Project名称>/logstore/<Logstore名称>" ], "Effect":"Allow" }, { "Effect":"Allow", "Action":[ "log:GetDashboard", "log:CreateDashboard", "log:UpdateDashboard" ], "Resource":"acs:log:*:*:project/<Project名称>/dashboard/internal-etl-insight" }, { "Effect":"Allow", "Action":"log:CreateDashboard", "Resource":"acs:log:*:*:project/<Project名称>/dashboard/*" }, { "Effect":"Allow", "Action":[ "log:*" ], "Resource":"acs:log:*:*:project/<Project名称>/job/*" }, { "Effect":"Allow", "Action":[ "log:*" ], "Resource":"acs:log:*:*:project/<Project名称>/jobschedule/*" } ] } 单击确定。 在左侧导航栏中选择人员管理 > 用户。 找到需要授权的子账号并单击对应的授权。 添加上文中创建的自定义权限策略,并单击确定。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 数据加工主要解决用户规整数据的痛点以及进一步挖掘数据价值, 降低时间与人力成本, 另一方面, 在某些情况下, 合理配置也可以节约一定财务费用. 本文进一步介绍了如何以较优的成本方式使用数据加工. 典型配置 根据数据加工的原理和性能指南介绍了如何规划日志服务的源logstore与目标logstore. 一般推荐的做法是简化采集方案, 使用一个或多个logstore快速接入数据, 然后使用数据加工进行快速分发, 并根据用户需求配置不同目标logstore的存储时间以及索引配置. 例如SLB访问日志加工分发案例. 成本优化 成本要素 参考日志服务的计费方式, 可以了解到日志服务的主要成本由以下几个主要因素决定: 每日导入的数据量 数据存储的时间 是否建立索引 案例1 - 优化存储结构 假设客户持续采集K8S的访问日志, 每天写入100GB原始日志, 存储30天, 并建立全索引, 那么日志服务的成本大约是2248元/每月. 假设用户更关心的是其中某一类POD的日志, 例如用户操作日志与出错日志. 那么这类日志的比例假设是20%, 且希望存储30天, 对于其他的日志, 只需要存储7天即可. 则可以如下安排: 构建接入源logstore: 存储3天, 不建立索引 构建目标logstore1, 存储30天, 建立索引. 用于存储用户操作日志与出错日志. 构建目标logstore2, 存储7天, 建立索引. 用于存储一般性日志. 这种情况下, 客户的成本大约是1672元/每月, 节约成本大约25%如果用户原始存储日志是60天, 通过数据加工只将关心的20%日志存储60天, 其他存储7天的话, 成本可以节约大约39% (3226元/每月变成2000元/每月) 案例2 - 优化存储内容 假设客户持续采集某类应用日志, 每天写入100GB原始日志, 存储30天, 并建立全索引, 那么日志服务的成本大约是2248元/每月. 假设后面例子中原始日志字段有些冗余, 用户更关心的其中某些字段, 例如经过数据加工优化每条日志的原来大小的60%, 且继续存储30天,则可以如下安排: 构建接入源logstore: 存储3天, 不建立索引 构建目标logstore, 存储30天, 建立索引. 用于存储用户操作日志与出错日志. 这种情况下, 客户的成本大约是1579元/每月, 节约成本大约30%. 源logstore是一个NGNIX访问与解析日志, 大小1021 Bytes, 加工后变成618 Bytes: __source__: 1.2.3.4 __topic__: ddos_access_log body_bytes_sent: 3866 cc_action: none cc_blocks: cc_phase: content_type: text/x-flv host: www.dbb.mock-domain.com http_cookie: i1=w1;x2=q2 http_referer: http://www.cbc.mock-domain.com http_user_agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36 http_x_forwarded_for: 105.120.151.10 https: true isp_line: BGP matched_host: www.cbd.mock-host.com method: GET real_client_ip: 105.120.160.17 remote_addr: 105.120.160.0 remote_port: 48196 request_length: 2946 request_method: GET request_time_msec: 78920 request_uri: /request/nvwlvvkhw server_name: www.bd.mock-host.com status: 502 time: 2019-07-22T17:40:26+08:00 ua_browser: mozilla ua_browser_family: ua_browser_type: ua_browser_version: 9.0 ua_device_type: ua_os: windows_7 ua_os_family: upstream_addr: 106.120.157.15:80 upstream_ip: 109.120.152.11 upstream_response_time: 0.858 upstream_status: 200 user_id: st0s2b5 经过数据加工后得到: __source__: 1.2.3.4 __topic__: ddos_access_log body_bytes_sent: 3866 content_type: text/x-flv host: www.dbb.mock-domain.com http_referer: http://www.cbc.mock-domain.com ua_browser: mozilla ua_browser_family: ua_browser_type: ua_browser_version: 9.0 ua_device_type: ua_os: windows_7 http_x_forwarded_for: 105.120.151.10 matched_host: www.cbd.mock-host.com method: GET real_client_ip: 105.120.160.17 request_length: 2946 request_uri: /request/nvwlvvkhw status: 502 upstream_addr: 106.120.157.15:80 upstream_ip: 109.120.152.11 upstream_response_time: 0.858 upstream_status: 200 user_id: st0s2b5 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
性能指标参考 参考原理, 数据加工任务的总体速度取取决于源shard的数量、用户配置的规则逻辑和复杂度有关, 一般可以按照1MB每秒每shard(=85GB每天每shard)规划.例如: 源logstore的数据写入速度是每天1TB, 那么需要分裂源logstore的shard数量为1024GB/85=12个. 数据加工与性能的关系 数据加工的速率与加工的规则有关, 具体体现如下: 输出写入相关: 事件大小相关: 写入事件越多(进行了分裂), 写入事件字段越多, 内容越长, 写入的数据包计算与网络量消耗越大, 速度越慢. 反之越快. 事件分组相关: 写入目标越多, 事件标签TAG越多, 输出的数据包日志组越多, 网络交互越多, 速度越慢. 反之越快. 加工逻辑相关: 加工逻辑约复杂, 搜索计算等越多, 使用频繁外部资源同步, 对计算与网络消耗越大, 速度越慢. 反之越快. 源logstore实时数据加工扩展 可以通过增加shard数量来实现扩展. 源logstore历史数据加工扩展 shard分裂仅仅对新写入数据有关. 如果历史数据量较大, 且shard数量较少的情况下. 可以对源logstore构建多个数据加工任务, 支持分别配置无重叠的加工时间即可. 注意: 加工时间是日志接收时间即可, 具体配置参考控制台配置 目标Logstore的扩展 目标Logstore的shard数量主要由2个方面决定: 数据加工的写入速率. Logstore单个Shard的写入速率上线是5MB/s, 因此可以根源logstore的shard数量, 加工的并发读来估算. 例如源logstore有20个shard, 那么推荐目标logstore至少有4个shard. 目标logstore的是否有建立索引, 做查询统计的需求, 如果目标Logstre希望建立索引并且进行统计查询(SQL), 那么建议基于SQL统计每次查询的覆盖范围是: 每5000万条日志一个shard的粒度来规划. 例如, 每天加工并写入10GB日志, 每条1KB的话, 有1千万条日志每天规模, 每次查询和统计希望可以覆盖30天数据量, 其总体日志量大约是3亿条日志, 建议目标logstore的shard数量规划为6个shard. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 搜索字符串是ETL语言中用于快速过滤的语法, 语法基本上与日志服务的查询语法, 以及lucene 语法相同. 可以极大简化数据加工的条件判断的逻辑. 相关函数 如下函数会使用到查询字符串语法. 类型 函数 使用场景 表达式函数-事件判断函数 e_search 使用查询字符串判断事件的字段值是否满足特定条件, 返回True或False 表达式函数-资源函数 res_log_logstore_pull 拉取Logstore资源返回表格, 构建表格中, 使用查询字符串配置黑白名单, 来判断每行是保留还是丢弃. 全局事件函数-搜索表格映射 e_search_table_map 关键字是查询字符串, 值是匹配的值的字典进行映射 功能概览 功能 字段 全文 子串搜索 支持 支持 通配符*?搜索 支持 支持 完全匹配搜索 支持 - 正则表达式搜索 支持 - 数值范围搜索 支持 - 数值比较 支持 - 关系and, or, not以及自由组合 支持 支持 搜索值转义 包含", \时需要用\转义.例如: content: "abc\"xy\\z" 合法 希望搜索*, ?时, 也需要用\转义, 否则会被视为通配符匹配 包含中文, 字母, 数字, 下划线, 小横线, *, ?等情况不需要用双引号, 其他情况需要用双引号括起来. 例如: status: "\*\?()[]:=" 值含特殊字符的值, 推荐放在"中, 除了*?和需要转义外, 其他不用转义status: active\*test 值只包含*或?, 可以不用双引号status: active\?test 值只包含*或?, 可以不用双引号content: ()[]:= 非法 字段名转义 字段名不能使用双引号, 包含特殊字符时直接使用\转义, 包含中文, 字母, 数字, 下划线, 小横线等情况不需要用双引号, 其他情况需要. 例如:"\*\(\1+1\)\?: abc") 字段不可以用双引号, 特殊字符用转义 __tag__\:__container_name__: abc 用转义中文字段: abc" 中文不需要转义"content": abc 非法, 字段名不能用双引号括起来 注意: 值用字符串用双引号括起来, 不支持单引号e_search("domain: '/url/test.jsp'")是错误的, 只能e_search('domain: "/url/test.jsp"') 子串搜索 语法 e_search("子串") e_search("字段名: 子串") 全文搜索 对所有字段进行搜索子串 样例 场景 e_search("active error") 多个子串搜索, 默认关系是OR e_search('"active error"') 搜索完整带空格的子串 e_search('"错误"') 中文子串 字段搜索 对特定字段进行搜索 样例 场景 e_search("status: active") 子串搜索 e_search('author: "john smith"') 带空格子串搜索 e_search('fileld: active error') 相当于 field:active OR "error" 通配符搜索 * ? 匹配: *表示 0个或多个字符串, ? 表示一个字符, 也可以表示一个宽字符如中文. 全文搜索 对所有字段进行搜索子串 样例 场景 e_search("active*test") 匹配0到多个, 不需要用双引号括起来 e_search("发生*错误") 中文匹配, 可以匹配发生错误, 发生严重错误等 e_search("active?good") ? 可以不用双引号 e_search("ac*tive?good") 也可以应用于完全匹配 e_search("ac*tive??go*od") 支持多个混合使用 字段搜索 对特定字段进行搜索子串 样例 场景 e_search("status: active*test") 匹配0到多个 e_search("status: active?good") 匹配一个 完全匹配 不同于子串搜索, 只需要局部匹配即可, 完全匹配要求对字段值从开头到结尾的值完全匹配. 语法 e_search("字段名==子串") 样例 样例 场景 e_search('author== "john smith"') 字段author必须完全等于john smith e_search("status== ac*tive?good") 可以与通配符结合使用 正则表达式匹配 语法 使用正则表达式匹配, 比通配符更强大的匹配方式. e_search("字段名~=正则表达式字符串") 因为正则表达式大量使用\, 推使用r修饰搜索字符串. 注意: 默认使用的是局部匹配, 而不是完全匹配, 如果需要完全匹配只需要在开头和结尾加上^和$即可 样例 样例 场景 e_search(r'status~= "\d+"') status字段包含数字 e_search(r'status~= "^\d+$"') status字段等于数字 数值比较 范围类比较 语法 这里是左右闭区间, 支持*表示无边界. e_search("字段: [左值, 右值]" # >= 左值, <= 右值 e_search("字段: [*, 右值]") # <= 右值 e_search("字段: [左值, *]") # >= 左值 样例 e_search('count: [100, 200]') # >=100 and <=200 e_search('count: [*, 200]') # <=200 e_search('count: [200, *]') # >=200 数值直接比较 语法样例 直接使用>, >=, =, <, <=比较: e_search('age >= 18') # >= 18 e_search('age > 18') # > 18 e_search('age = 18') # = 18 e_search('age <= 18') # <=18 e_search('age < 18') # <18 逻辑关系 全局逻辑关系 支持任意搜索之间的逻辑关系, 也支持用 ( ) 进行嵌套. 逻辑 关键字 且 and AND && 大小写不敏感 或 or OR 双竖线 大写小不敏感 否 not ! 大小写不敏感 样例 e_search("abc OR xyz") # 大小写不敏感 e_search("abc and (xyz or zzz)") e_search("abc and not (xyz and not zzz)") e_search("abc && xyz") # and e_search("abc || xyz") # or e_search("abc || !xyz") # or not 子串匹配逻辑关系 在子串匹配是也支持逻辑关系: 样例 e_search("field: (abc OR xyz)") # 字段field包含 abc 或 xyz e_search("field: (abc OR not xyz)") # 字段field包含 abc 或 不包含xyz e_search("field: (abc && !xyz)") # 字段field包含 abc 且 不包含xyz 字段判断 也可以使用搜索字符串对字段做定性判断: 样例 场景 e_search("field: *") 字段存在(任意值) e_search('field: ""') 字段存在(至少包含一个子空串) e_search("not field:*") 字段不存在 e_search('not field:""') 字段不存在 e_search('field==""') 字段存在, 值为空 e_search('field~=".+"') 字段存在, 值不为空 e_search('not field~=".+"') 字段不存在或值为空 e_search('not field==""') 字段不存在或值不为空 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 日志服务领域专用语言LOG DSL (Domain Specific Language)是日志服务数据加工使用的编排语言, 一种Python兼容的脚本语言. LOG DSL基于Python提供内置200个函数简化常见数据加工模式. 也支持用户自由定义的Python扩展(目前仅针对特定客户开放). 自由编排 LOG DSL通过一个Python兼容的DSL(领域专用语言)进行自由编排,对各种逻辑进行复杂组合, 可以满足大部分数据加工的需求和自由度. 例如, 可以自由编排达到如下一个场景: 完整的加工功能 支持近30种全局步骤函数, 支持通过各种参数调节行为, 且可以接受其他表达式函数的调用组合的结果作为参数, 其中控制的函数不仅可以搭配表达式函数, 也可以搭配其他步骤函数操作. 控制: 支持基于条件判断后的流程分支, 包括if-else, if条件-操作配对组合, switch分派, compose组合等场景. 借助e_search等简单搜索语法可以对不同类型日志进行灵活的加工. 事件操作: 支持对事件进行丢弃, 保留, 分裂, 输出, 复制等 字段操作: 支持保留, 删除, 重命名字段等 字段赋值: 支持基于任意表达式组合结果设置字段的值 字段值提取: 支持基于正则表达式, GROK, KV, KV分隔符, CSV, TSV, PSV, syslog等方式提取字段中的多个值或键值对. 支持JSON提取并展开的完整攻略. 字段富化: 支持基于字典, 表格进行映射或搜索, 其中搜索表格的映射方式尤其强. 支持从规则配置, 外部OSS, RDS, Logstore等资源获取富化的维表信息. 支持基于全量, 增量或改动日志对外部资源进行自动刷新. 灵活的函数库 目前提供近200个内置的表达式函数, 以便转换事件或控制全局函数的行为,覆盖了主流的数据加工的需求,包括: 事件搜索: 提供类似Lucene语法的, 完整的正则表达式, 字符串, 泛字符, 数值比较, and/or/not等组合的条件过滤机制 基本操作函数: 字段取值, 控制, 比较, 容器判断, 多字段操作等 转换函数: 基础类型转换, 数字转换, 字典, 列表操作. 算术函数: 基础计算, 多值计算比较, 数学计算, 数学参数等 字符串函数: 多字段操作, 编码/解码, 排序、倒叙、替换, 常规规整, 查找判断, 切分, 格式化, 字符集判断等 日期时间函数: 智能日期时间转换, 获取日期时间属性, 获取日期时间, 获取Unix时间戳, 获取日期时间字符串, 修改日期时间, 修改日期时间, 比较日期时间等 正则表达式函数: 字段提取, 匹配判断, 替换, 切分 GROK支持: 支持GROK模式替换, 提供400种GROK内置模式. JSON与XML函数: 提取过滤等 Protobuf: 支持基于Protobuf配置对Protobuf格式的内容进行转换与提取 编解码: 支持SHA1/256/512等, MD5, HTML, URL, Base64等格式的文本进行单项或双向的编解码. 支持动态分发 支持根据业务需求, 将数据按照特定逻辑分发到不通的目标logstore. 目标logstore的名称甚至是动态计算或者外部第三方获取到的. 支持灵活富化 支持从本地资源, 外部资源(包括OSS, RDS, 日志服务Logstore)来获取, 支持从字典, 表格的常规映射到搜索表格的复杂映射. 外部资源加载支持自动刷新, snapshot 支持自定义UDF扩展 使用内置的200个函数(持续增加)基本可以完成大部分工作,因特殊场景, 不能满足需求的,可以提工单并获得及时支持。另一方面, 底层目前采用Python引擎,理论上任意Python的库稍加包装即可进入日志服务的数据加工,自定义UDF功能尚未全面开放,需提工单申请, 语言约束 ETL语言兼容Python, 但标准模式下约束了使用方式, 可以视为Python的子集. 除了基本的数据结构与表达方式外, 规则以函数方式进行编排. 白名单高级模式完全兼容Python, 标准模式与Python的语言区别如下: 类别 Python语法 标准模式支持 白名单高级模式 数据结构 数字, 字符串, 布尔 支持, 除"""形式字符串不支持, 参考数据结构 支持 数据结构 元祖, 列表, 集合, 字典 支持, 除集合set如{1,2,3}不支持. 参考数据结构 支持 数据结构 对象定义 仅支持内置扩展数据结构,如表格, 日期时间对象等. 参考数据结构 支持 基本语法 操作符, 如加减乘除等 不直接支持, 需要通过函数方式支持, 参考基础语法 支持 基本语法 注释 支持, 参考基础语法 支持 基本语法 变量定义赋值 不支持, 需使用无状态方式调用传递 支持 函数 标准Python内置函数 不支持, 使用内置200+函数 支持 函数 函数调用 支持, 除解包调用不支持 支持 函数 自定义函数def或lambda 不支持, 提供200+事件与表达式函数, 且支持基于现有函数的自由组合调用 支持, 按照协议规范自由编写 模块 导入与使用Python标准库 不支持 支持 模块 线程与进程创建 不支持 支持 模块 导入第三方库 不支持 需提工单支持 模块 外部网络连接或命令调用 提供内置的资源连接器, 参考资源连接器 需提工单支持 函数式编排 标准模式的ETL语言是通过函数调用的方式完成编排的, 其工作原理可参考数据加工原理. 更具体的加工逻辑描述如下: 内置200+的函数, 主要分为2类: 全局操作函数 接收事件, 处理并返回事件的函数, 且只有全局操作函数才能构建加工规则的每一步骤. 表达式函数 通用型函数, 接收特定参数, 组合调用后作为传递给全局操作函数以定义更加灵活逻辑. 两者区别 函数类型 可做全局步骤 接受 返回 修改事件 可否组合调用 全局操作函数 是 事件 0到多条事件 大部分会 可以 表达式函数 否 各种数据(有些也接收事件) 特定数据结构 不会 可以 全局操作函数 接受事件并返回事件(单个, 列表或None)的函数. 除了全局函数外, 其他内置函数不能够放在每一个步骤的第一行. 一个ETL规则的形式如下: 全局函数1(..参数....) 全局函数2(..参数....) 全局函数3(..参数....) 全局函数4(..参数....) 子类 函数子类 描述 样例 流程控制函数 用于规则步骤流程控制, 接收事件, 基于条件做控制, 调用其他事件函数完成处理. e_if, e_switch, e_if_else等 事件处理函数 对事件进行加工的主体函数, 会修改传递的事件.返回0到多条事件. e_drop_fields丢弃事件字段, e_kv提取并添加事件的键值对, e_dict_map做给事件做富化等 如下概览: 类型 函数 说明 流程控制 e_if 多个条件操作的配对操作 流程控制 e_if_else if-else操作 流程控制 e_switch 满足一个条件操作后跳出 流程控制 e_compose 组合 事件操作 e_drop 丢弃 事件操作 e_keep 保留 事件操作 e_split 分裂 事件操作 e_output 输出 事件操作 e_coutput 复制输出 字段操作 e_drop_fields 删除 字段操作 e_keep_fields 保留 字段操作 e_rename 重命名 字段值赋值 e_set 赋值 字段值提取 e_regex 正则提取 字段值提取 e_json json展开或提取 字段值提取 e_kv 自动提取键值对 字段值提取 e_kv_delimit 基于分隔符提取键值对 字段值提取 e_csv 逗号或其他分隔符提取 字段值提取 e_tsv tab分隔符提取 字段值提取 e_psv pipe分隔符提取 字段值提取 e_syslogrfc 根据syslog协议提取头 字段富化 e_dict_map 字典映射 字段富化 e_table_map 表格映射 字段富化 e_search_map 搜索映射 资源操作 res_local_update 设置任务参数上下文 加工逻辑样例 1. 基本处理 默认加工框架会以流式方式读取源logstore的数据, 并将每一条日志事件以字典数据结构, 传递给加工规则中的逻辑, 每个设定的步骤都会顺序处理事件, 最终处理修改的事件, 会输出到默认的Logstore 注意: 在传递的过程中事件的字段和值始终都是字符串形式. 例如: e_set("f1", 200) 设置字段f1的值为200, 源事件: {"__time__": "1234567", "__topic__": "", "k1": "test"}, 经过这个函数处理后, 会变成: {"__time__": "1234567", "__topic__": "", "k1": "test", "f1": "200"}, 注意其中f1的值是"200"字符串形式. 普通情况下: 规则中定义的每个事件函数会顺序执行, 每一个函数会对每个事件处理和修改, 返回一个修改的事件. 例如e_set("type", "test")会对每个事件添加一个字段"type"值为"test", 下一个函数接收到的事件就是最新的. 2. 条件判断 条件判断if: 某些步骤可以设定条件, 也就是不满足条件的事件会跳过本次操作. 相当于一个if的逻辑. 例如e_if(e_match("status", "200"), e_regex("data", "ret: \d+", "result")), 会首先检查字段status是否为200, 不满足不会做任何操作. 如果满足, 则会对字段data用正则表达式提取出新字段result 类似的e_if_else会进行if_else的操作. 3. 停止处理 某些步骤可能返回0个事件, 表示删除事件, 例如e_if(str_islower(v("result")), e_drop()), 对每个字段result的值是小写字符串, 则丢弃这条事件. 这条事件被丢弃后, 后续的操作将不再进行. 自动重新开始下一条事件. 输出事件可以视为一种特殊的停止处理, 例如e_output在提前输出事件到目标, 并删除事件, 其后续的操作也不会再进行. 注意: 函数e_coutput会复制一份当前的事件输出, 并继续处理后续. 4. 分裂并行 某些步骤也可能返回多个事件, 表示分裂事件, 例如e_split(data)表示, 根据字段data的值, 例如是"abc, xyz", 分裂成2条事件, 每条事件的字段data的值分别是abc和xyz. 分裂后的每条事件都会继续进行后续的步骤并最终输出(或被后续某一步删除). 接收事件并返回0-多条事件的函数, 可以作为全局操作步骤. 表达式函数 除了全局的操作函数外, ETL语言还提供了内置近200个表达式函数, 表达式的函数组合调用, 表达式函数接收特定参数, 返回特定值,一般是单个表达式函数或其调用组合,其形式如下: 全局操作1(表达式函数1(...), ....) 全局操作2(..., 表达式函数2(...), 表达式函数3(...),...) 子类 函数子类 描述 样例 事件检查函数 接收事件, 提取或检索返回特定信息的函数, 不会修改传递的事件. v返回事件字段的值, e_search和e_match等返回事件是否符合特定条件等 资源函数 接受特定参数配置, 连接本地或外部资源, 返回数据, 一般是字典, 表格等类型. OSS, RDS, Logstore资源函数 控制函数 用于表达式逻辑操作, 接受特定参数, 基于条件做控制, 调用其他表达式函数返回. op_and, op_or, op_not, op_if, op_op_coalesce等 一般表达式函数 表达式函数的主体, 接受固定或者其他函数的调用的结果, 返回特定的值. 字符串, 时间, 类型转换函数等 如下概览: 类型 函数 说明 事件检查函数 v, e_has, e_not_has, e_search, e_match, e_match_any, e_match_all等 获取事件字段值, 或判断字段或字段值是否符合特定内容 基础操作函数 部分op_* 函数 比较, 条件判断, 容器类计算, 一般性多值操作 转换函数 ct_*函数 数字,字符串,布尔之间的转换, 数字进制转换 算术函数 部分op_*函数, math_*函数等 数字的+-*/幂等计算, 数学计算, 多值计算等 字符串函数 str_*函数 字符串的所有相关操作与判断搜索等 日期时间函数 dt_*函数 Unix时间戳, 日期时间对象, 日期时间字符串转化, 时区调整, 圆整等 正则表达式函数 reg_*函数 正则提取, 检索, 替换, 分裂多值等 GROK函数 grok函数 提取grok模式返回对应正则表达式 JSON, XML, Protobuf函数 json_*, xml_*, pb_* 函数 对应提取或解析 编码解码类函数 url_*, html_*, md5_*, sha1_*, base64_*函数 相关单向或双向函数 列表函数 lst_*函数 列表相关构建,获取, 修改, 操作等 字典函数 dct_*函数 字典相关构建,获取, 修改, 操作等 资源函数 res_*函数 本地配置, RDS, Logstore等资源获取 基础语法 注释 规则中支持注释, 以便标记某些步骤的意思, 以#开头 或放在行尾巴均支持. # 设置默认主题 (放在行首的注释) e_set("__topic__", "access_log") # 设置默认主题 (放在行尾的注释) 换行 一般函数调用的)是可以直接跨行的. 结构中存在,时, 在,的地方进行分隔也是可以的. 如果某个字符串过长需要换行时, 需要使用\表示上一行. 样例: e_set("__topic__", "this is a very long long long .........." \ "......long text") e_set("__topic__", "v1", "type", "v2", # 逗号分隔的可以直接换行 "length", 100) 函数调用方式 1. 基本调用方式:e_set("abc", "xyz")注意: 非命名参数一般都需要传递所有值. 2. 命名参数调用方式:e_set("abc", "xyz", mode="fill")注意: 命名参数不传递, 会使用默认值. 某些情况有特殊要求. 参考每个函数的参数说明 多个命名参数下, 可以根据选择传递, 顺序不要求:e_csv("data", ["f1", "f2", "f3"], sep='#', quote="|") 等价于e_csv("data", ["f1", "f2", "f3"], quote="|", sep='#') 注意: 命名参数的顺序, 始终在非命名参数的后面. 3. 组合调用, 参数是其他函数的调用方式e_set("abc", v("xyz"))e_set("abc", str_lower(v("xyz"))) 4. 变参某些函数支持变参传递, 例如:e_set("k1", "v1", "k2", "v2", ....) 带命名参数时, 命名参数放最后:e_set("k1", "v1", "k2", "v2", ...., mode="fill") 5. 事件参数传递注意到上面的函数并没有参数接受事件, 这点是默认的, 由框架自动传递. 操作符 标准模式下不支持操作符, 提供了对应函数达到一样效果. 场景操作 函数 样例 加 + op_add op_add(v("age"), 2) 减 - op_sub op_sub(v("age"), 2) 乘 * op_mul op_mul(v("size"), 2) 幂 ** op_pow op_pow(v("size"), 2) 整除 // op_div_floor op_div_floor(v("bytes"), 1024) 取模 % op_mod op_mod(v("age"), 10) 取负 - op_neg op_neg(v("profit")) 判断存在 in op_in op_in(["pass", "ok"], v("result")) 判断不存在 not in op_not_in op_in(["pass", "ok"], v("result")) 逻辑且 and op_and op_and(op_gt(v("age"), 18), op_lt(v("age"), 31)) 逻辑或 or op_or op_or(op_le(v("age"), 18), op_gt(v("age"), 65)) 逻辑否 not op_not op_not(op_gt(v("age"), 18)) 判断等于 == op_eq op_eq(v("name"), "xiao ming") 判断不等于 != op_ne op_ne(v("name"), "xiao ming") 大于 > op_gt op_gt(ct_int(v("age")), ) 大于等于 >= op_ge op_ge(ct_int(v("age")), 18) 小于 op_lt op_lt(ct_int(v("age")), 18) 小于等于 <= op_le op_le(ct_int(v("age")), 18) 字符串切片 [ ...] op_slice op_slice(v("message"), 0, 20) 例如, 赋值字段a为3600 * 6的话, 如下操作: # * e_set("a", 3600 * 6) # 非法 e_set("a", op_mul(3600, 6)) # 合法 # / e_set("bytes_kb", v("bytes") / 1024) # 非法 e_set("bytes_kb", op_div_floor(v("bytes"), 1024)) # 合法 真假判断 有一些函数会接收一个条件, 根据条件的值是True还是False来决定逻辑, 条件可以是一个固定值, 或者经过表达式返回的值. ETL语言中, 条件判断并不要求对象一定是布尔类型的值, 也支持对任意类型值进行判断, 如下表格是各种类型的值的真假条件: 数据类型 True的条件 False的条件 布尔 True, true False, false None - 总是False 数值 非0, 0.0 0, 0.0 字符串 非空 空串 字节 非空 空字节 元组 非空 空元组 列表 非空 空列表 字典 非空 空字典 表格 存在即为True 空对象(None) 日期时间 存在即为True 空对象(None) 以下样例, 会如注释时, 会丢弃事件 e_if(True, DROP) # 总是 e_if(1, DROP) # 总是 e_if(v("abc"), DROP) # 存在字段abc, 且字段不为空时 e_if(str_isdigit(v("abc")), DROP) # 存在字段abc, 且字段的内容都是数字时 基本数据结构 整数 1, 2, 3, 4例如, 用于值设置或者函数的参数传递, 例如:e_set("f1", 100) 赋值字段f1的值为100 浮点 1.5, 2.3例如:e_set("f1", 1.5) 赋值字段f1的值为1.5 字符串 "abc"等价于'abc' ,没有区别. 当字符串中包含"时, 可以'abc"xyz'这样. 避免使用\反转: "abc\"xyz" "\\abc\\xyz", 相当于 \abc\xyz r"\\10.64.1.1\share\folder" 所见即所得的字符串, 相当于\\10.64.1.1\share\folder 常用于简化正则表达式修饰. 多字节字符串以unicode表示, 和一般字符串一致. 例如 "中文" 的长度也是2. 正则表达式也是以字符串形式表示. 注意: 函数e_search等接受的搜索字符串里面的字符串用双引号括起来, 不支持单引号. e_search("domain: '/url/test.jsp'")是错误的, 只能e_search('domain: "/url/test.jsp"') 字节 b'abc'不同于字符串的内存编码形式. 某些特殊函数接收或者返回. 空 None 或 null, 表示无(不同于空字符串), 函数返回时表示空, 全局事件函数返回None表示删除. 许多函数也以None为命名参数的默认值. 列表 也叫数组: [1,2,3,4], 某些函数参数可能接收的是列表, 例如:e_dict_map("dict data", ["f1", "f2", "f3"], ...)某些函数在某些情况下返回的可能是列表, 例如: json_select选择了一个数组时. 元组(tuple) (1, 2, 3, 4), 和列表功能一样, 某些函数参数可能接收的是元组. 字典 形式为{"key": "value", "k2": "v2", ...}的键值对组合, 其关键字一般是字符串, 其值可以是以上的各种形式. 事件是一种特殊的字典. 某些函数可能接受的特定格式的字典. 例如: {"key": [1,2, 3], "ke": {"k3": "va3"} }, 字典结构也应用于字典映射的输入数据. 存储格式以哈希方式, 关键字不能重复. 查找时无序. 布尔 True, False, true, false 表格 多列的表格结构, 可以从资源中加载多行CSV格式内容构建, 或者从RDS, Logstore等中加载多列数据获取. 主要用于映射(富化)或其他高级配置场景. 日期时间对象 表示日期时间的内存对象, 可以转换为Unix字符串或者格式化的时间字符串或者传递给其他dt_类函数进行进一步转换. 事件类型 基本类型 数据加工的日志的数据结构是以字典例如{"__topic__": "access_log", "content": "....."}表示. 字典的关键字和值, 对应于日志的字段和值 事件类的函数默认接受输入源的每个事件, 并返回一个事件, 注意: 事件的关键字和值都是字符串. 进一步参考赋值自动转换 注意: 关键字不能重复. 元字段 时间字段: __time__: 是Unix时间戳的字符串 其他也有主题: __topic__ 源: __source__ 时间字段修改 修改这个字段的值, 也就修改了日志的事件时间. 可以使用时间字符串对齐进行进一步的各种操作. 删除了这个字段, 在输出日志时, 将会取当前时间戳作为新的事件的时间. 标记 标记(tag), 日志存在标记, 区别于一般字段, 标记会以__tag__:名称的关键字格式存在. 如果源logstore打开了服务器接收时间的日志, 则会存在tag: __tag__:__receive_time__ K8S的日志会存在许多容器类的tag, 例如: __tag__:__container_name__ 也可以添加修改tag, 例如添加一个tag名为"type":e_set("__tag__:type", "access_log") 赋值自动转换 事件的关键字和值都是字符串, 因此当对事件进行赋值或者设置新的字段值时, 会对结果进行自动字符串转换.例如: e_set("v1", 12.3) e_set("v2", True) 会设置一个字段v1值为转换的字符串12.3和字段v2值为true. 设置None值时会被忽略. 类型 样例 转换类型 转换样例 整数 1 字符串 "1" 浮点 1.2 字符串 "1.0" 布尔 True 字符串 "true" 字节 b"123" 使用UTF8反转为字符串 "123" 元组 (1, 2, 3) json转换 "[1, 2, 3]" 列表 [1,2,3] json转换 "[1, 2, 3]" 字典 {"1":2, "3":4} json转换 "{"1": 2, "3": 4}" 日期时间 datetime(2018, 10, 10, 10, 10, 10) ISO格式转换 2018-10-10 10:10:10 固定标示 预定了一些固定表示, 以便简化代码或便于理解: 类型 标示 说明 布尔 true 真, 等价于True 布尔 false 假, 等价于False None null 无, 等价于None 字符串 F_TAGS TAG字段正则表达式, 等价于"__tag__:.+" 字符串 F_META topic, source, TAG字段的正则表达式表示, 等价于`__tag__:.+ topic __source__` 字符串 F_TIME time字段的名称, 等价于__time__ 字符串 F_PACK_META pack meta字段的正则表达式表示形式, 等价于`"__pack_meta__ __tag__:__pack_id__"` 字符串 F_RECEIVE_TIME 服务器接收时间的tag字段, 等价于"__tag__:__receive_time__" JSON对象 一般指JSON表达式函数json_select或者json_parse解析提取后的对象, 并没有真正JSON对象, 本质上是上述基础数据结构的形式. 如下: 原始字符串 解析出的JSON对象 实际类型 1 1 整数 1.2 1.2 浮点 true True 布尔 false False 布尔 "abc" "abc" 字符串 null None None ["v1", "v2", "v3"] ["v1", "v2", "v3"] 列表 ["v1", 3, 4.0] ["v1", 3, 4.0] 列表 {"v1": 100, "v2": "good"} {"v1": 100, "v2": "good"} 字典 {"v1": {"v11": 100, "v2": 200}, "v3": "good"} {"v1": {"v11": 100, "v2": 200}, "v3": "good"} 字典 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
功能入口 进入Logstore页面,通过加工菜单右边的+号直接进入数据加工模式,如下图1所示。图1 Logstore页面加工入口 或者进入查询页面,手动切换到数据加工模式,如下图2所示。图2 查询页面切换加工模式 加工效果预览 进入数据加工模式之后,通过如下步骤进行调试:1)选择时间的范围,确保对应时间范围内有日志接入。注意: 这里选择的时间是日志接收时间为准.2)在代码编辑框编辑加工规则,规则语法参考加工规则。3)点击预览,等待返回加工结果。在首次点击预览时,需要输入AccessKey 用于消费源Logstore中的数据。4)等待加工结果返回。图 3 加工界面 结果展示区中的输出目标对应加工规则中的输出目标。如果加工规则存在语法错误或者Access Key不合法等原因导致加工失败,会提示如下错误信息:图 4 异常日志 创建任务 如果规则预览符合预期结果,点击保存数据加工按钮保存为加工任务,如下图5所示。图 5 创建加工任务 在左侧弹窗中输入加工任务必需的配置:图 6 创建加工任务 除了所需的Access key 之外,还需要输入任务名称,存储目标和加工范围等信息。 1)存储目标 加工规则结果支持输出到多个Logstore,每个存储目标对应一个当前Project或者当前地域内其他Project内的Logstore,需要提供用于写入数据的Access Key。注意: 在加工规则中可以通过存储目标名称进行引用, 在加工规则中不指定目标的情况下, 默认会输出到第一个配置的目标中. 注意:目前只支持相同地域的Project。 限制 目前只支持相同地域的Project, 最多20个固定目标. 如果希望存储更多目标, 且动态配置. 可以参考动态多目标分发. 存储目标的长度: 4~64, 字符集: 任意 2)加工范围 加工过范围对应三种类型: 所有:对Logstore中的数据从开始位置持续加工,直到任务被手动停止。 某时间开始:指定开始时间点,从该时间点对应的位置开始加工,直到任务被手动停止。 特定时间范围:指定任务的起止时间,加工到结束时间之后自动结束。 注意: 时间是以日志接收时间为准. 3)高级参数配置 对于加工规则代码中需要使用的密码信息,如数据库连接密码等,可以以Key-Value的形式保存在密钥对中,在代码中通过变量引用res_local("key")的方式进行使用。某些特殊配置项,例如简单的维表信息也可以存储于高级秘钥配置中. 具体参考资源. 加工规则保存之后自动开始运行。 限制 目前支持最多10个参数对 关键字: 最大长度100, 字符集[a-zA-Z0-9u4E00-u9FA5 ._-] 值: 最大长度2000, 字符集任意 任务管理 加工任务创建后,可以从左侧导航栏源logstore的数据加工中直接找到对应的任务, 如图: 也可以在源logstore的的数据处理下加工下找到对应任务, 如下图7所示。图 7 加工任务管理 操作 停止 对于运行中的任务,点击任务列表右侧操作栏的停止按钮,可以停止任务。注意:停止任务会保存当前的加工位置,如果任务再次启动会从停止的位置继续加工。如果需要从配置的时间范围重新开始执行,请参考 重新执行。 启动 对于状态为停止的任务,点击任务列表右侧操作栏的启动按钮,可以继续开始执行加工。 重新执行 任务在任何状态都可以通过点击列表右侧的重新执行按钮来重新执行任务,重新执行不会从已经加工结束的位置继续运行,而是从头开始执行。如果希望继续之前的位置加工可以参考停止任务和启动任务。 修改规则 通过修改按钮可以进入加工规则配置页面,重新调试规则后,可以通过修改配置按钮修改。注意: 如果在修改了代码之后希望能够使用新的代码继续运行,需要停止任务,再启动任务。控制台会在修改时会提示进行这样的操作. 删除 如图7所示,点击任务列表右侧操作栏的删除按钮,可以删除任务。 任务状态 加工任务可能的状态有:启动中,运行中,停止中,已停止,成功,失败。各个状态和允许的操作如下表所示: 状态操作 停止 启动 重新运行 修改 删除 启动中 支持 支持 运行中 支持 支持 支持 支持 停止中 支持 支持 已停止 支持 支持 支持 支持 成功 支持 支持 支持 失败 支持 支持 支持 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 日志服务加工服务的一个作业使用协同消费组, 对源日志库进行流式消费, 对每一条日志传给加工规则处理后再输出. 调度原理 调度机制 对每一个加工作业, 加工服务的调度器会启动一个或多个运行实例, 每个运行实例扮演一个消费者的角色去消费1个或者多个源logstore的shard, 调度器会根据运行实例的内存与CPU消耗情况决定或减少并行运行实例数, 最多启动与源logstore的shard数量一样的运行实例. 运行实例 对分配的每个shard读取用户配置的起点的数据, 在内存中将源日志传递给加载的加工规则引擎, 处理后, 再输出给配置的目标Logstore. 加工规则引擎也会根据规则从外部加载资源进行富化等操作. 运行实例会利用消费组机制保存每个shard消费到的位置, 确保意外停止后再启动时可以继续从断点处继续消费. 作业停止 当用户配置作业时间范围没有配置后终点时, 运行实例默认不会退出.当作业因为某些原因被停止(例如用户临时停止), 再次启动时, 对每个shard会默认从上次保存消费的点继续消费. 当用户配置作业时间范围有终止点时, 运行实例处理到配置的终点时间所接收的日志后会自动退出. 规则引擎原理: 基本操作 加工规则使用ETL语言编写, 可以理解为一个加工步骤的集合. 其中每一个步骤其实是一个Python的函数调用. 规则引擎加载规则后按步骤顺序执行. 例如这里4个以e_开头的函数的调用定义了4个主要步骤. e_set("log_type", "access_log") e_drop_fields("__action") e_if(e_search("ret: pass"), e_set("result", "pass")) e_if(e_search("ret: unknown"), DROP) 对应的逻辑如图: 基本逻辑 正常情况下: 规则中定义的每个事件函数会顺序执行, 每一个函数会对每个事件处理和修改, 返回一个修改的事件.例如e_set("log_type", "access_log")会对每个事件添加一个字段"log_type"值为"access_log", 下一个函数接收到的事件就是最新的. 条件判断 某些步骤可以设定条件, 也就是不满足条件的事件会跳过本次操作. 相当于一个if的逻辑.例如e_if(e_search("ret: pass"), e_set("result", "pass")), 会首先检查字段ret是否包含pass, 不满足不会做任何操作. 如果满足, 则会设置字段result值为pass. 停止处理 某些步骤可能返回0个事件, 表示删除事件, 例如e_if(e_search("ret: unknown"), DROP), 对每个字段ret的值是unknown的事件会丢弃. 这条事件被丢弃后, 后续的操作将不再进行. 自动重新开始下一条事件. 规则引擎原理: 输出, 复制与分裂 规则引擎也支持复制输出与分裂事件, 例如这里4个以e_开头的函数的调用定义了4个主要步骤. e_coutput("archive_logstore") ) e_split("log_type") e_if(e_search("log_type: alert"), e_output("alert_logstore") ) e_set("result", "pass") 假设现在处理一条源日志如下: log_type: access,alert content: admin login to database. 对应的逻辑如图: 输出事件 默认输出事件可以视为一种特殊的停止处理, 例如第3步中对log_type为alert的事件, 调用e_output("alert_logstore"), 提前输出事件到目标, 并删除事件, 其后续的操作也不会再进行. 复制输出事件 函数e_coutput会复制一份当前的事件输出, 并继续处理后续. 例如第1步中, 会将所有经过的日志输出到archive_logstore目标中. 分裂并行 第2步中, e_split("log_type")表示, 根据字段log_type的值, 例如是"access,alert", 分裂成2条事件, 2条事件完全一样, 除了字段log_type的值分别为access和alert.分裂后的每条事件都会分别继续进行后续的步骤. 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
基本概念 ETL, Data Transformation ETL (Extract, Transform, Load)是数据分析领域中数据加载、转换、输出的的意思,日志服务中主要加载源Logstore与输出到其他Logstore, 也会加载OSS, RDS, 其他Logstore做数据富化. 但主要是对数据进行Data Transformation的工作. 事件、数据、日志 在数据加工的上下文中, 事件、数据都是表示日志, 例如事件时间就是日志时间,丢弃事件字段函数drop_event_fields就是用于丢弃特定日志字段. 日志时间 日志自身的时间, 指日志表示的事件所发生的时间. 也称事件时间. 在日志中的保留字段为__time__,一般由日志中的时间信息直接提取生成。整型,Unix标准时间格式。单位为秒,表示从1970-1-1 00:00:00 UTC计算起的秒数。 日志接收时间 日志到达服务器被接收时的时间, 默认不保存在日志中, 但是如果数据logstore开启了记录外网IP, 会保留在日志标签字段的__receive_time__上, 数据加工中的完整字段名是__tag__:__receive_time__. 中, 类型整型,Unix标准时间格式。单位为秒,表示从1970-1-1 00:00:00 UTC计算起的秒数。 注意: 数据加工日志作业的配置项高级选项的时间范围, 是以日志接收时间为准的. 日志时间 vs. 日志接收时间 大部分场景下, 日志是实时发送给日志服务的, 因此日志的时间与接收日志的基本相同或者接近. 如果日志不是实时发送的, 日志时间与接收时间会不一致, 这种情况一般发生在导入历史时间的情况. 例如通过SDK导入了过去30天的日志. 那么日志时间就是上个月的, 而日志接收时间就是当前. 日志标签 日志的标签TAG,包括: 用户自定义标签:您通过API PutLogs写入数据时添加的标签。 系统标签:服务端为您添加的标签,包括__client_ip__和__receive_time__。在数据加工中, 标签字段以__tag__:作为前缀. 具体参考日志字段 配置相关 源Logstore 数据加工中, 读取数据的logstore再进行加工的是源logstore. 一个作业只支持一个源logstore. 但可以对一个源logstore配置多个加工作业. 目标Logstore 数据加工中数据写入的logstore叫目标logstore. 一个作业可以是一个或者多个. 可以是静态配置的, 也可以是动态配置的. LOG DSL 日志服务领域专用语言LOG DSL (Domain Specific Language)是日志服务数据加工使用的编排语言, 一种Python兼容的脚本语言. LOG DSL基于Python提供内置200个函数简化常见数据加工模式. 也支持用户自由定义的Python扩展(目前仅针对特定客户开放). 详情请参考日志服务DSL 加工规则 也叫加工脚本, 是使用ETL语言编排的逻辑代码集合. 加工作业 由源logstore、目标logstore、加工规则以及其他配置项如加工时间范围、权限参数等组成的任务, 叫做加工作业. 也是数据加工的最小调度单元. 规则相关 资源 除了数据源的logstore外的, 数据加工中会引用的第三方数据源叫做资源做某种配置或者富化, 包括但不限于本地资源, OSS, 外部Logstore, RDS等. 维表 用于做数据富化的数据某些维度信息的外部表格叫做维表. 例如公司用户账户列表, 产品列表, 地理位置信息库等. 维表可能会动态更新, 维表一般存在于资源中. 富化、映射 日志包含的信息不全时, 需要借助外部信息进行完善. 对日志一个或多个字段通过映射完善出更多信息的过程叫做富化或者映射. 例如某个请求包含HTTP状态码status, 可以通过如下表格富化出新字段HTTP状态描述status_desc: status status_desc 200 成功 300 跳转 400 权限错误 500 服务器错误 或者源数据中有user_id字段, 使用外部账户维表, 映射出其对应用户名、性别、注册时间、邮箱等信息放入到事件字段中并写入目标logstore中。 分裂 日志信息非常复杂, 同时包含了多条信息时, 对一条日志分裂成多条日志时的过程叫做事件分裂.例如某一条日志的字段content内容如下: __time__: 1231245 __topic: "win_logon_log" content: [ { "source": "1.2.3.4", "dest": "1.2.3.4" "action": "login", "result": "pass" },{ "source": "1.2.3.5", "dest": "1.2.3.4" "action": "logout", "result": "pass" } ] 可以分裂成2条日志: __time__: 1231245 __topic: "win_logon_log" content: { "source": "1.2.3.4", "dest": "1.2.3.4" "action": "login", "result": "pass" } 和 __time__: 1231245 __topic: "win_logon_log" content: { "source": "1.2.3.5", "dest": "1.2.3.4" "action": "logout", "result": "pass" } GROK 使用模式化语法代替复杂的正则表达式. 例如grok("%{IPV4}")表示一个匹配IP v4的正则表达式,等价于: "(?<![0-9])(?:(?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(?![0-9])"具体参考GROK参考 正则捕获 用于特定正则表达式中局部内容捕获并给与一个名字, 数据加工中主要用于传递给e_regex中更直观的配置提取的字段名. 例如e_regex("content", "(?P<email>[a-zA-Z][a-zA-Z0-9_.+-=:]+@\w+\.com")表示提取字段content中的邮件地址并放入到字段email中. 这里邮件是一个通用正则表达式, 推荐使用GROK进行简化: e_regex("content", grok("%{EMAILADDRESS:email}"). 进一步详情,请参考用户手册。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
作者:唐恺 日志服务的数据加工功能,在任务运行过程中重复如下动作: 读取源LogStore数据 对源数据做一系列加工得到结果数据 将结果数据写到目标LogStore 如上第一步和第三步涉及对LogStore的访问权限: 您可以使用主账号AccessKey,不需要额外配置权限,过程简单,这里不做介绍 为了账号安全,强烈建议使用子账号配置细粒度权限,下文做详述 请登录RAM控制台进行子账号及权限配置。 创建读源LogStore子账号 源Project所属主账号操作: 保存子账号的AccessKey ID/Secret备用: 设置源LogStore读权限 源Project所属主账号操作: 举例一:精确授权 源:Project(log-project-prod), LogStore(access_log) 策略内容: { "Version": "1", "Statement": [ { "Action": [ "log:ListShards", "log:GetCursorOrData", "log:GetConsumerGroupCheckPoint", "log:UpdateConsumerGroup", "log:ConsumerGroupHeartBeat", "log:ConsumerGroupUpdateCheckPoint", "log:ListConsumerGroup", "log:CreateConsumerGroup" ], "Resource": [ "acs:log:*:*:project/log-project-prod/logstore/access_log", "acs:log:*:*:project/log-project-prod/logstore/access_log/*" ], "Effect": "Allow" } ] } 举例二:模糊匹配授权 源:Project(log-project-dev-a、log-project-dev-b、log-project-dev-c等),LogStore(app_a_log、app_b_log、app_c_log等) 策略内容: { "Version": "1", "Statement": [ { "Action": [ "log:ListShards", "log:GetCursorOrData", "log:GetConsumerGroupCheckPoint", "log:UpdateConsumerGroup", "log:ConsumerGroupHeartBeat", "log:ConsumerGroupUpdateCheckPoint", "log:ListConsumerGroup", "log:CreateConsumerGroup" ], "Resource": [ "acs:log:*:*:project/log-project-dev-*/logstore/app_*_log", "acs:log:*:*:project/log-project-dev-*/logstore/app_*_log/*" ], "Effect": "Allow" } ] } 更多授权场景请参考文档。 将读源权限应用到读源子账号 源Project所属主账号操作: 创建写目标LogStore子账号 目标Project所属主账号操作,方式同步骤1。 保存子账号的AccessKey ID/Secret备用。 配置目标LogStore写权限 目标Project所属主账号操作,方式同步骤2。 举例一:精确授权 目标:Project(log-project-prod), LogStore(access_log_output) 策略内容: { "Version": "1", "Statement": [ { "Action": [ "log:Post*" ], "Resource": "acs:log:*:*:project/log-project-prod/logstore/access_log_output", "Effect": "Allow" } ] } 举例二:模糊匹配授权 目标:Project(log-project-dev-a、log-project-dev-b、log-project-dev-c等),LogStore(app_a_log_output、app_b_log_output、app_c_log_output等) 策略内容: { "Version": "1", "Statement": [ { "Action": [ "log:Post*" ], "Resource": "acs:log:*:*:project/log-project-dev-*/logstore/app_*_log_output", "Effect": "Allow" } ] } 更多授权场景请参考文档。 将写目标权限应用到写目标子账号 目标Project所属主账号操作,方式同步骤4。 在数据加工中使用子账号AccessKey 源Project所属账号登录日志服务控制台操作,步骤1子账号AccessKey ID/Secret填入上方框内,步骤4子账号AccessKey ID/Secret填入下方框内。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
作者:唐恺 数据加工诊断仪表盘 日志服务加工功能的运行状态可以通过该仪表盘进行查看,入口是加工列表页的“规则洞察”按钮: 点击改按钮跳转到仪表盘后,可以分别按照作业名称、实例ID、源LogStore筛选其它任务状态。例如下图是默认查看当前实例ID(90c9d47714dbb807d47c13b819d3e7df)的作业: 注:预览任务的状态数据不计入本报表。 总览指标 读日志数总计:从源LogStore各shard读取到的日志条数总计 投递日志数总计:从源LogStore各shard读取到日志并成功投递到目标LogStore的日志条数总计 失败日志数总计:从源LogStore各shard读取到日志并在加工过程中发生失败的日志条数总计 投递日志数占比:成功投递到目标LogStore的日志条数占源LogStore读取到日志条数的比例 加工速率指标 统计每分钟窗口内,数据加工处理的日志条数,包括四条指标: accept:从源LogStore读到的日志条数 dropped:从源LogStore读到并按代码预期丢弃的日志条数 delivered:从源LogStore读到并成功投递目标LogStore的日志条数 failed:从源LogStore各shard读取到日志并在加工过程中发生失败的日志条数 消费延迟与速率指标 统计每分钟窗口内,加工任务读取源LogStore时每个Shard指标: 消费延迟:当前时间 - 该Shard最近的已完成日志时间(日志写入日志服务时间,也级Server Arrived Time) 消费速率:Shard在该分钟窗口内每秒钟读取到的日志条数 注:处理实时(最新)日志时消费延迟一般是1s左右;如果处理的是历史时间范围的日志数据,在任务开始的阶段消费延迟可能会很高,并随这数据加工的进行,消费进度不断追赶最终达到低延迟水平。 活跃Shard指标 展示最近一段时间内发生的,Shard级别每秒处理的日志行数(accept、dropped、delivered、failed)。 异常详情 您可以根据reason字段,查看可能导致出错的代码问题。你还可以深入到当前Project下的internal-etl-log LogStore(免费提供使用): 通过关键词ERROR或者WARNING查看完整的代码执行错误日志。 如果问题仍无法解决,可以提供该部分信息联系日志服务进行支持。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
概述 目前数据加工全面支持ETL语言, 更简单灵活. 参考ETL语言介绍 1. 全局操作事件 1.1. 字段赋值(set event) 1.1.1. 语法介绍 语法: SET_EVENT_新字段 = 固定值 SET_EVENT_新字段 = 表达式函数 说明 设置单个字段值,字段名为新字段,如果已经存在,则会覆盖现有字段值 新字段的字符约束是:中英文数字_组成,但不能以数字开头。注意:支持中文,但不支持:,因此不能通过这种方式设置日志的tag等,可以参考通用操作完成这类需求。 表达式函数返回无值None时,该操作会被忽略 表达式函数返回的任何类型的值都会被转化成字符串(例如,数字会被格式化为字符串,放回到事件中) 完整的表达式函数信息,请参考表达式函数 1.1.2. 样例 例子1:设置固定值添加一个新字段city值为上海。 SET_EVENT_city = "上海" 例子2:复制字段值调用单个表达式函数v,将现有字段ret的值,赋值给新字段result。 SET_EVENT_result = v("ret") 例子3:动态设置值这里组合调用表达式函数,从字段ret和return中首先存在的字段的值,返回其小写赋值给字段result SET_EVENT_result = str_lower(v("ret", "return")) 1.2. 字段提取 (extract event) 1.2.1. 语法介绍 语法: EXTRACT_EVENT_字段 = 字符串 EXTRACT_EVENT_字段 = 字段操作类函数 说明 对单个字段的值进行操作,一般是提取值到多个字段,例如正则表达式、JSON展开、查表富化、键值对拆分等,也包括根据字段值,将一条事件分裂成多条。 字符串是字段操作类函数REGEX的一种简写。 提取的多个值默认会在原字段不存在或者值为空时覆盖,进一步参考提取字段的值检查与覆盖模式 字段的字符约束是:中英文数字_组成,但不能以数字开头。注意:支持中文,但不支持:,因此不能通过这种方式对日志的tag等进行操作,可以参考通用操作完成这类需求。 表达式函数返回无值None时,该操作会被忽略 完整的字段类操作函数信息,请参考字段类操作函数 1.2.2. 样例 例子1:正则表达式提取值从字段email中提取用户名user、邮箱司名company: EXTRACT_EVENT_email = r"(?P<user>\w+)@(?P<company>\w+)\.com" 注意:字符串是字段操作类函数正则表达式(REGEX)的简写方式,详情参考其更多使用方法。 例子2:根据字段值映射新字段根据字段level的值,调用字段类操作函数LOOKUP映射出新字段level_info来: EXTRACT_EVENT_level = LOOKUP({"1": "info","2": "warning","3": "error", "*": "other"},"level_info") 例子3:展开JSON根据字段request_body和response_body的值,调用字段类操作函数JSON自动展开(深度默认为10)成多个值: EXTRACT_EVENT_request_body = JSON EXTRACT_EVENT_response_body = JSON(depth=1) 不带参数的JSON是简化的调用方式,更多JSON功能参数,具体参考手册。 1.3. 通用操作 1.3.1. 语法介绍 语法: TRANSFORM_ANY = 操作 TRANSFORM_ANY = 操作列表 操作通用操作有三种形式,包含了前面的两种操作的扩展形式,和第三种事件操作类函数: 字段赋值操作 = {"新字段名": 固定值或表达式函数, "另一个字段名": } 字段提取操作 = 字段输入, 字符串或字段操作类函数 一般操作 = 事件操作类函数 操作列表多个操作的列表,如[操作1, 操作2, 操作3, ....]构成的列表,会被依次执行下去,除非某个操作丢弃了事件。注意:多个操作,必须用[]括起来。 说明 字段赋值操作:是一个{ key1: value1, key2: value2}的多个键值对形式,支持多个值的赋值。 字段提取操作:是一个输入, 操作的单个配对,其中输入可以不仅仅是一个字段,例如OSSLOOKUP中支持多个输入做映射。 一般操作:覆盖了对事件的常规性操作,例如:丢弃事件、保留或丢弃特定字段等,也包括输出事件等 多个通用操作需要使用不同值做占位符区分,一般用数字累加即可。 完整的事件类操作函数信息,详情参照事件类操作函数详情 1.3.2. 样例 例子1:多个字段赋值操作对多个字段赋值,也支持表达式函数。 TRANSFORM_ANY = {"__topic__": "default topic", "tag:__type__": v("event_type"), "level": "1"} 例子2:根据字段值提取根据字段request_body的值,调用字段类操作函数JSON展开成多个值: TRANSFORM_ANY = "request_body", JSON(depth=1) 例子3:一般操作丢弃事件字段field1和field2: TRANSFORM_ANY = DROP_F(["field1", "field2"]) 例子4:多个操作多个操作按顺序执行: TRANSFORM_ANY = [ {"email": "abc@default.com"}, ("request_body", JSON) ] 例子4:表达式函数与操作类函数互操作根据字段字段valid的值是否为true来保留或丢弃事件: TRANSFORM_ANY = op_if(op_eq(v("valid"), "true"), KEEP, DROP) 注意:其中KEEP和DROP是保留和丢弃事件类操作的标示。 1.4. 带条件的通用操作 1.4.1. 语法介绍 语法: TRANSFORM_EVENT = 条件操作 TRANSFORM_EVENT = 条件操作列表 条件操作就是带条件的通用操作,如果条件满足,即执行其操作,否则无操作。: 条件操作 = 条件, 操作 注意:其中操作也可以是操作列表,详情参考操作 条件条件是用于判断当前事件是否满足特定条件的表达式,其形式有三种: - 固定条件标识 - {"字段名1", "正则表达式1"} - {"字段名1", NOT("正则表达式1")} # NOT - {"字段名1", "正则表达式1", "字段名2": "正则表达式2", ... } # AND - 表达式函数 - 以上形式的列表,如:[ {"字段名1": "正则表达式1"}, {"字段名2": "正则表达式2"}, ... ] # OR 条件操作的列表多个条件操作的列表,如[条件操作1, 条件操作2, 条件操作3, ....]构成的列表。每次检查每个条件操作的条件,满足即执行器操作,否则无操作。之后继续检查下一条条件操作,除非某一步丢弃了事件。注意:整个列表必须用[]括起来,其中每个条件操作都需要用()括起来。 条件语法说明 固定条件标识:是使用某些预定义的标识,例如ANY、ALL等都标识所有,也就是任意事件均会匹配并执行后续操作。 键值对:{ key : value } 是对字段值用正则完全匹配,注意,其中的字段的值必须与正则表达式是完全匹配(从头到尾匹配)才能算满足条件。 例如:字段user的值为"i love python", 那么正则表达式"i love"或者"python"均无法匹配。 多个键值队的关系是AND关系,必须全满足才能执行配对的操作(列表)。 对正则用NOT调动后,逻辑关系变成了not 可以通过表达式函数来以返回的值作为判断条件(默认空字符串、None、布尔值False、数字0、空列表等表示不满足,其他情况表示都表示满足。 通过对多个逻辑用列表组合,表达了OR的意义,也就是只需要有一个满足,即执行配对的操作(列表) 注意OR、AND、NOT等等逻辑目前版本不能任意嵌套。 有复杂逻辑判断的,可以使用表达式函数。 表达式函数参考表达式函数 1.4.2. 样例 例子1:值匹配后操作字段result是failed或failure时,设置事件主题为login_failed_event: TRANSFORM_EVENT = {"result": r"failed|failure"}, {"__topic__": "login_failed_event"} 例子2:根据字段值判断再提取当字段request_body存在且值非空时,调用字段类操作函数JSON对字段request_body进行展开成多个值: TRANSFORM_EVENT = NO_EMPTY("request_body"), ("request_body", JSON) 这里使用了特定表达式函数NO_EMPTY表示存在字段request_body且非空。 例子3:高级判断再操作当字段valid的值是failed时,丢弃事件: TRANSFORM_EVENT = op_if(v("valid"), "failed"), DROP 例子4:多个条件操作多个操作按顺序执行: TRANSFORM_EVENT = [ (ANY, {"__topic__": "default_login"}), ( {"valid": "failed"}, {"__topic__": "login_failed_event"} ) ] 注意,多个条件操作使用[]进行括起来,其中每个条件操作,都使用()括起来。 1.5. 基于条件分派操作 1.5.1. 语法介绍 语法: DISPATCH_EVENT_占位符 = 条件操作列表 说明 形式与待条件的通用操作基本一致 其中多个条件操作的列表,每次检查每个条件操作的条件,不满足会持续检查下一个条件操作,满足即执行器配对的操作(列表),之后就不再执行后续的条件操作了。 1.5.2. 样例 例子4:条件分派按照字段http_status来设置不同的事件主题: DISPATCH_EVENT = [ ({"http_status": r"2\d+"} , {"__topic__": "success_event"}), ({"http_status": r"3\d+"} , {"__topic__": "redirection_event"}), ({"http_status": r"4\d+"} , {"__topic__": "unauthorized_event"}), ({"http_status": r"5\d+"} , {"__topic__": "internal_server_error_event"}), ] 注意,多个条件操作使用[]进行括起来,其中每个条件操作,都使用()括起来。 1.6. 常见事件操作的简化宏 1.6.1 保留/丢弃事件 语法对于满足条件的事件保留或丢弃 KEEP_EVENT = 条件 DROP_EVENT = 条件 说明 条件: 与带条件的通用操作中的条件一致,也可以是列表,参考条件 1.6.2 保留/丢弃字段 语法对于满足条件的字段名保留或丢弃 KEEP_FIELDS = 字符串或字符串列表 DROP_FIELDS = 字符串或字符串列表 字符串或字符串列表 字符串:这里的字符串指的是正则表达式,当字段名符合条件时保留或者丢弃字符串。 列表:表示用[]括起来的正则表达式字符串列表,如: ["abc", "xyz"] 提供了一些预定好的meta的字段名的标识,可以直接使用,例如F_TIME表示时间字段, F_META表示时间、主题等字段。 说明 条件: 与带条件的通用操作中的条件一致,也可以是列表,参考条件 因为日志服务中的事件还包含了隐藏的元字段:包括__time__、__topic__等,如果删除了__time__,那么事件时间将被重置为当前时间,使用KEEP_FIELDS_时需要特别注意,不要误删。 常用KEEP_FIELDS格式是: [F_TIME, F_META, F_TAGS, "f1", "f2" ] 1.6.3 重命名字段 语法 RENAME_FIELDS = {"现有字段名正则1": "新字段名1", "现有字段正则2": "新字段名2",} 说明 这里的现有字段名实际是一个正则表达式,当有多个字段匹配时,所有字段均会改为新字段名,新字段名的值是其中一个,具体哪个未知。则主要解决多种数据源的日志混合时,字段名统一简化用。 1.6.4 输出事件 将满足条件的事件输出 语法 OUTPUT = 条件 COUTPUT = 条件 说明 条件: 与带条件的通用操作中的条件一致,也可以是列表,参考条件 OUTPUT将满足条件的事件输出后,事件不再进行后续处理,(可以理解为被丢弃了)。 COUTPUT将满足条件的事件输出后,事件会继续后续处理,(可以理解为复制一份输出了)。 事件类操作函数`OUTPUT与COUTPUT`支持更多的行为定制。参考事件类操作函数 2. 表达式函数 返回特定值的表达式,一般是单个表达式函数或其调用组合,覆盖如下几大类,100多个并持续增加: 基本操作函数: 字段取值, 控制, 比较, 容器判断, 字段存在内容判断,多字段操作等 转换函数: 基础类型转换, 数字转换 算术函数: 基础计算, 多值计算比较, 数学计算, 数学参数等 字符串函数: 多字段操作, 编码/解码, 排序、倒叙、替换, 常规规整, 查找判断, 切分, 格式化, 字符集判断等 日期时间函数: 智能日期时间转换, 获取日期时间属性, 获取日期时间, 获取Unix时间戳, 获取日期时间字符串, 修改日期- 时间, 修改日期时间, 比较日期时间等 正则表达式函数: 字段提取, 匹配判断, 替换, 切分等 进一步详情,请参考用户手册。 3. 字段类操作函数 基于输入的字段的值,进行操作,注意:目前不支持字段类操作函数与表达式函数互操作。 覆盖如下几大类,并持续增加: 正则提取列:正则的完整支持,包括动态提取字段名等 CSV格式提取:CSV标准的支持 字典映射:直接字段映射 外部OSS多列映射:从外部OSS上的CSV关联对数据进行富化,支持增量刷新、宽匹配等。 外部数据库多列映射:从外部数据库关联对数据进行富化,支持动态刷新、宽匹配等。 外部Logstore多列映射:从外部logstore关联对数据进行富化,支持增量刷新、宽匹配等。 自动KV:自动提取KV,也支持自定义分隔符、auto-escape场景 JSON自动展开:支持自动展开JSON内容,包括数组,支持展开过程的定制。 JSON-JMES过滤:支持基于JMES的动态选择与计算后再处理。 分裂事件(基于JSON数组或字符串):基于字符串数组或JSON数组进行事件分裂 多列合并(基于JSON数组或字符串):基于字符串数组或JSON数组进行多字段合并 进一步详情,请参考用户手册。 提取字段的值检查与覆盖模式 关键字字符集: 执行此策略的方法有:REGEX(动态Key名),JSON、KV 默认:[\u4e00-\u9fa5\u0800-\u4e00a-zA-Z][\w\-\.]* 不符合规范的例子: 123=abc 1k=200 {“123”: “456”}等 设置覆盖模式,通过参数mode 支持的提取方式:REGEX、KV、CSV, Lookup, JSON ("msg",REGEX(r"(\w+):(\d+)",{r"k_\1": r"v_\2"}, mode="fill-auto") fill – 当原字段不存在或者值为空时 add –当原字段不存在时设置 overwrite – 总是设置 fill/add/overwrite-auto – (当新值非空时才操作) 默认:fill-auto 4. 事件类操作函数详情 对事件进行直接操作的函数, 覆盖如下几类: 多字段KV提取 事件Meta操作:字段丢弃、重命名 事件输出:复制输出、输出后丢弃、多目标配置、重载元meta、附加更多TAG等 注意:事件类操作也支持与特定表达式函数互操作,如被表达式函数返回。 进一步详情,请参考用户手册。 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
自由编排 通过一个Python兼容ETL语言进行自由编排,对各种逻辑进行复杂组合, 可以满足大部分数据加工的需求和自由度. 例如, 可以自由编排达到如下一个场景: 完整的加工功能 支持近30种全局步骤函数, 支持通过各种参数调节行为, 且可以接受其他表达式函数的调用组合的结果作为参数, 其中控制的函数不仅可以搭配表达式函数, 也可以搭配其他步骤函数操作. 控制: 支持基于条件判断后的流程分支, 包括if-else, if条件-操作配对组合, switch分派, compose组合等场景. 借助e_search等简单搜索语法可以对不同类型日志进行灵活的加工. 事件操作: 支持对事件进行丢弃, 保留, 分裂, 输出, 复制等 字段操作: 支持保留, 删除, 重命名字段等 字段赋值: 支持基于任意表达式组合结果设置字段的值 字段值提取: 支持基于正则表达式, GROK, KV, KV分隔符, CSV, TSV, PSV, syslog等方式提取字段中的多个值或键值对. 支持JSON提取并展开的完整攻略. 字段富化: 支持基于字典, 表格进行映射或搜索, 其中搜索表格的映射方式尤其强. 支持从规则配置, 外部OSS, RDS, Logstore等资源获取富化的维表信息. 支持基于全量, 增量或改动日志对外部资源进行自动刷新. 灵活的函数库 目前提供近200个内置的表达式函数, 以便转换事件或控制全局函数的行为,覆盖了主流的数据加工的需求,包括: 事件搜索: 提供类似Lucene语法的, 完整的正则表达式, 字符串, 泛字符, 数值比较, and/or/not等组合的条件过滤机制 基本操作函数: 字段取值, 控制, 比较, 容器判断, 多字段操作等 转换函数: 基础类型转换, 数字转换, 字典, 列表操作. 算术函数: 基础计算, 多值计算比较, 数学计算, 数学参数等 字符串函数: 多字段操作, 编码/解码, 排序、倒叙、替换, 常规规整, 查找判断, 切分, 格式化, 字符集判断等 日期时间函数: 智能日期时间转换, 获取日期时间属性, 获取日期时间, 获取Unix时间戳, 获取日期时间字符串, 修改日期时间, 修改日期时间, 比较日期时间等 正则表达式函数: 字段提取, 匹配判断, 替换, 切分 GROK支持: 支持GROK模式替换, 提供400种GROK内置模式. JSON与XML函数: 提取过滤等 Protobuf: 支持基于Protobuf配置对Protobuf格式的内容进行转换与提取 编解码: 支持SHA1/256/512等, MD5, HTML, URL, Base64等格式的文本进行单项或双向的编解码. 支持动态分发 支持根据业务需求, 将数据按照特定逻辑分发到不通的目标logstore. 目标logstore的名称甚至是动态计算或者外部第三方获取到的. 支持灵活富化 支持从本地资源, 外部资源(包括OSS, RDS, 日志服务Logstore)来获取, 支持从字典, 表格的常规映射到搜索表格的复杂映射. 外部资源加载支持自动刷新, snapshot 支持自定义UDF扩展 使用内置的200个函数(持续增加)基本可以完成大部分工作,因特殊场景, 不能满足需求的,可以提工单并获得及时支持。另一方面, 底层目前采用Python引擎,理论上任意Python的库稍加包装即可进入日志服务的数据加工,自定义UDF功能尚未全面开放,需提工单申请, 进一步参考 日志服务最佳实践汇总(持续更新) 完整DSL语法介绍与参考PDF下载(持续更新) 数据加工指南 介绍: 功能概述 概念 原理 快速开始: 快速开始(SLB日志加工实战) 控制台操作 源与目标访问秘钥配置 作业诊断指南 性能指南 成本优化指南 语法: DSL语法介绍 查询字符串语法 JMES语法介绍 管理配置: 子账号授权配置 欢迎扫码加入官方钉钉群获得实时更新与阿里云工程师的及时直接的支持:
2021年12月
2021年11月
2021年08月
2021年06月
2021年04月
2021年03月
2020年12月
2019年11月
2019年09月
2019年08月
2019年07月
2019年06月
你好,支持的, size传-1会试图拿所有:
https://aliyun-log-python-sdk.readthedocs.io/api.html#aliyun.log.LogClient.get_log
但数据量超大时,过于占用内存,推荐使用get_all_log,迭代方式效率更好;
https://github.com/aliyun/aliyun-log-python-sdk/blob/master/tests/sample.py#L96
API文档:
get_log: https://aliyun-log-python-sdk.readthedocs.io/api.html#aliyun.log.LogClient.get_log
get_all_log: https://aliyun-log-python-sdk.readthedocs.io/api.html#aliyun.log.LogClient.get_log_all
你运行时候是不是也是Import错误?