
上一课我们把webspoon的全局日志配置捋了一遍这一课接着往深处走专门来聊全局异常/错误捕获的落地玩法。先说说我为什么对这个话题有执念。前几天一个朋友排查ETL任务凌晨两点从商户系统拉订单增量webspoon上跑着跑着突然失败项目只配了转换内部的错误处理作业层面没有任何兜底第二天一看订单表少了四千多行漏数了大半天才被业务部门发现。这种场景你一定不陌生尤其是从传统kettle桌面版迁移到webspoon的同学经常觉得网页版比客户端“难抓错”。问题不是webspoon不能捕获异常而是你还没把那套全局捕获的思路理清楚。1. webspoon为什么“异常不吭声”先想清楚这两点再配置1.1 桌面版kettle和webspoon的异常呈现差异到底差在哪用kettle桌面版做ETL开发跑转换时控制台直接打印堆栈步骤图标变红弹窗提示失败行数日志滚动起来一目了然。但webspoon把整个引擎搬到了浏览器和服务端任务通过API提交你看到的是一个执行状态具体失败原因藏在某个需要手动拉取的日志片段里。这个差异决定了异常捕获策略必须重新设计。差异维度kettle桌面版webspoon错误展示控制台实时打印步骤红黄变色界面只显示执行状态错误需查日志或日志表错误定位本地文件日志上下文全服务端日志需通过任务ID/时间过滤任务生命周期手动运行退出即结束服务端异步执行可脱离浏览器持续跑恢复手段重启转换手动调整依赖作业调度、重试机制、外部监控很多人第一次用webspoon跑转换失败第一反应是去页面上找报错弹窗结果什么都没看到然后就开始怀疑是不是转换本身没跑过其实是没找到正确的日志入口。在webspoon里一次执行会生成一个执行对象日志记录在服务端的内存缓冲区和日志表里如果服务重启或者日志老化你连失败现场都找不回来。所以webspoon环境下的全局异常捕获第一步不是“怎么写错误处理分支”而是“怎么保证一个失败的完整现场可以被记录、追溯、通知”。1.2 全局捕获不是加一个try-catch而是四个层面收口很多团队认为全局异常/错误捕获就是给转换加一个错误处理分支把错误行写到一个表里。实际上这只能算“局部捕获”。真正的全局捕获需要考虑四个层面任务调度层面、转换执行层面、数据存储层面、通知反馈层面。任务调度层面解决的是“作业失败了后续流程怎么继续”的问题。转换执行层面解决的是“哪一行数据因为什么原因失败”的问题。数据存储层面解决的是“错误信息存在哪里、能回溯多久、能不能统计分析”的问题。通知反馈层面解决的是“出了错之后相关责任人在睡梦中还是第一时间收到告警”的问题。这四个层面组合起来才叫“全局”。我只见过少数团队能把四层完整打通。大部分项目只做了转换层的错误行重定向作业层没有失败分支也没有任何告警错误日志写完就躺在数据库里没人看这种捕获等于白做。所以这一课我会从这四个层面分别展开按照我建议的顺序去配置。2. 捕获异常之前先把这三个基础配置做扎实2.1 日志级别和日志表决定了你能看到多少细节在webspoon上配置异常捕获之前先检查每个作业和转换的日志级别。webspoon沿用了kettle的日志级别体系常用级别有ROWS记录行级日志、DEBUG调试级、BASIC基本、ERROR仅错误等。日志级别详细程度适用场景ERROR只记录错误信息生产环境日常运行减少日志量BASIC按步骤汇总处理行数、耗时默认推荐兼顾性能与排查DEBUG打印每行数据细节、内部状态本地开发调试定位复杂问题ROWS逐行打日志信息量极大极少数需要逐行追踪的场景我给出的建议是生产环境跑批用BASIC或者ERROR定位问题时临时切到DEBUG不要长期用DEBUG因为日志量会严重影响webspoon的服务端性能。这里要画一个重点——webspoon界面上看到的日志和执行结果并不等于所有日志都持久化保存了。如果不开日志表webspoon运行时候的日志只存在内存中任务一结束你可能再也找不到细节。建议在webspoon中配置数据库日志仓储开启Kettle日志表、步骤日志表、性能日志表把每一次执行的关键记录落库。日志表的类型要注意区分作业日志表存作业执行记录步骤日志表存每个步骤的启动时间、处理行数、运行状态性能日志表记录步骤级别的性能指标。生产环境建议三种都开你会发现在排查问题时有一个历史日志库是多么幸福的事。很多问题的征兆在失败前一天就出现了比如某步骤处理行数突然从一万变成零没有历史日志你根本发现不了这个趋势。2.2 作业项与转换步骤的错误处理分支别搞混层级kettle/webspoon的异常捕获在作业和转换两个层级上是分开的。很多初学者把这两个层级的错误处理设置混在一起导致配置不生效。转换层级上的错误处理是“行级处理”。你在“表输入”或者“文本文件输入”这类步骤上可以开启错误处理分支把处理失败的行导入到另一个步骤中做后续处理。启用错误处理后重定向行里会附带几个隐藏字段最典型的是错误数、错误描述、原始行数据。这里的底层原理是webspoon使用的引擎在执行步骤时一旦某行处理失败会按照错误处理tab的配置把失败行连同错误信息发送到目标步骤而不是中断整个转换。这类似于程序里把异常当作数据流继续向后传递。作业层级上的错误处理是“任务级处理”。作业中的一个作业项失败后可以设定跳转到某个指定的作业项继续执行。比如“执行SQL脚本”作业项失败后跳转到“发送邮件”作业项进行告警。更复杂一点还可以用“作业项是否成功”的结果变量做判断让作业自己决定是重试、跳过、还是终止。作业级别的错误处理才真正决定了一次ETL整体失败时的行为而这个层级往往被忽略。2.3 步骤是否支持错误处理提前摸底再动手在给转换里填错误处理分支之前建议你先把项目里用到的步骤过一遍确认哪些步骤支持错误处理、哪些不支持。根据实际使用经验表输入、文本文件输入、CSV文件输入、表输出、插入更新、删除、更新这类和外部系统交互的数据步骤基本都支持行级错误处理。而“写日志”“设置变量”“空操作”这种起辅助作用的步骤有些只提供作业级处理不能做行级重定向这个机制在webspoon中也是通用的。我遇到过最典型的情况是团队在“表输出”步骤上配置了错误处理想把写入失败的记录发给“写日志”步骤结果发现错误处理tab里的目标步骤下拉框是空的检查了半天才发现“写日志”步骤没有接收错误行的能力换成一个“表插入/更新”的步骤就好了。所以动手配置前先把你当前流程里所有步骤的右键菜单、属性面板翻一遍心里有数再开始做全局设计。3. 实战在webspoon里搭一套“全局异常/错误捕获”流程3.1 从零准备建一张能存“现场”的错误日志表我先说明一下这一课讲的完整流程是我建议的实践方案组合架构上没有绝对的标准答案但下面这几个核心环节一定绕不开错误日志表设计、转换级错误重定向、作业级失败分支与通知。我们先从存储层开始。全局异常捕获的底座是一张设计合理的错误日志表。我见过最糟糕的错误日志表只有一行“错误描述”错误堆栈、出错步骤、输入数据全都没有事后根本没法定位。这里分享一下我长期使用的建表结构以MySQL为例你在实际项目里可以按需调整。CREATE TABLE etl_error_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, job_name VARCHAR(128) COMMENT 作业名称, trans_name VARCHAR(128) COMMENT 转换名称, step_name VARCHAR(128) COMMENT 出错步骤名称, error_code VARCHAR(50) COMMENT 错误码, error_desc TEXT COMMENT 错误描述, input_row TEXT COMMENT 出错的原始行数据, error_time DATETIME COMMENT 错误发生时间, retry_count INT DEFAULT 0 COMMENT 重试次数, status VARCHAR(20) DEFAULT new COMMENT 状态new/resolved/ignored );这张表里有两个字段容易被忽略。一个是input_row它保存出错的那条原始数据在排查时几乎是决定性的很多脏数据问题没有这个字段就无解。另一个是retry_count可以配合作业重试逻辑记录重试了几次这个字段能帮你看出来哪些错误是偶发、哪些是稳定必现的。还要提醒的是字段类型。error_desc一定要用TEXT或者LONGTEXT因为webspoon跑复杂转换时一段错误堆栈可能几百上千字符用VARCHAR(200)存会被截断截断后你看不到完整错误原因反而更花时间。字符集建议统一使用utf8mb4在webspoon里配置数据库连接时也保持一致避免中文错误信息写入后变成乱码。3.2 转换级捕获错误行重定向与错误明细记录存储层准备好之后我们来做转换层的错误行重定向。假设这样一个场景从文本文件读取订单数据经过“字段拆分”环节时数据里混入了几条长度异常的行导致解析失败正常流程是把解析成功的数据写入“表输出”步骤但失败的行不能直接丢弃我们需要把它们捕获到etl_error_log表。建议的转换结构是这样文本文件输入读取订单原始数据字段拆分解析订单明细字段开启错误处理表输出写入订单目标表错误处理分支把字段拆分步骤的失败行经过“值映射”后写入etl_error_log表在webspoon中配置时你需要在“字段拆分”步骤的“错误处理”tab里启用错误处理指定目标步骤为错误处理分支的下游步骤。启用了之后webspoon会把这条数据连同错误信息一并传递。不同步骤提供的错误信息字段名略有差异一般情况下你会得到类似“错误数量”“错误描述”“错误行数据”这几个字段。如果你的目标步骤是“表输出”需要把这些错误字段一一映射到etl_error_log表的对应列上。这里有一个非常容易踩的坑错误处理分支里的数据仍然带着原数据流的字段映射到目标表时一定要把错误描述、错误行数据这些字段安排到位。如果直接把原数据字段全部灌进错误表很可能出现字段对不上导致错误日志写不进去本来想记录错误结果连错误都记不上那时你会很崩溃。配置完成之后建议先造几条错误数据做一次端到端验证。故意在测试文件里加一行超长字段然后运行转换检查目标订单表里没有这条脏数据而etl_error_log表里多了一条记录里面有完整的错误描述和原始数据文本。这一步通过之后转换层的捕获才真正算数。3.3 作业级捕获失败分支、通知联动与重试机制转换级捕获做完只是完成了一半。因为很多时候转换本身能跑完但结果有误或者作业中某个作业项失败了整个作业不会自动通知任何人。所以我们需要在作业层级设计一套兜底流程。我的建议是在作业里增加一套“主流程-失败告警-重试”的组合结构。主流程就是常规的ETL作业比如“转换A-更新数据表-归档文件”。在这条主流程的每个关键作业项上设置“失败”时跳转到告警流程。告警流程可以用“发送邮件”作业项实现。webspoon的邮件步骤配置和桌面版一致需要指定SMTP服务器、端口、认证方式。生产环境一般用企业邮件网关注意如果网关启用了SSL端口通常是465或者587并且需要在数据库连接和认证信息里都做对应配置。邮件主题可以把作业名、失败时间拼接进去正文可以写清楚“这个作业失败了影响范围还可能包括后续的报表任务”让收件人不用登录系统就能判断紧急程度。重试机制更高级一点。我在实际项目中用过一个方案流程是“作业项A”失败后跳到“判断重试次数”步骤判断条件写在JavaScript代码步骤里如果重试次数低于3就更新计数并跳回“作业项A”重新执行如果重试次数达到上限就跳转到告警步骤发邮件。这样处理和数据库连接闪断、临时文件被占用这类瞬时故障时非常有效跑批任务往往就是需要这种“自己站起来再试一次”的能力。需要注意的是重试逻辑一定不能引发数据重复。重试的作业项如果是幂等的比如全量覆盖写入重试没问题。但如果是增量写表、先删后插这类操作重试时就要额外小心建议重试的对象做成整作业重跑并在重跑前先清理上一次的中间状态。3.4 通知反馈层的补充方案除了邮件还能做什么告警不止邮件一种。在webspoon的流程中如果企业内部有IM机器人接口可以通过“HTTP POST”步骤把错误信息发送到群机器人有短信平台的同样可以封装一个接口调用步骤。我的经验是邮件适合记录详情IM或者短信适合做第一时间的强提醒两者组合是最理想的。在设计通知内容时给自己留个线索。不要只写“作业失败”四个字至少要把作业名称、失败步骤、错误摘要、发生时间、重试次数这几个字段带上。我见过很多告警邮件是“作业失败请检查”这种通知等于没通知。你有那么多的错误日志字段随手拼进邮件正文里收件人自己就能判断问题严重性你少花很多沟通成本。4. 常见问题与排查技巧实录4.1 webspoon里转换失败但日志没有输出这是我被问得最多的问题。作业在webspoon里显示失败但日志区域干干净净什么都没有。排查思路是先检查日志级别是否被设置成ERROR或更低再检查是否开启了日志缓冲最后检查日志表是否成功记录。如果你没有配置任何日志表任务执行完后的详细日志会随着任务对象释放而丢失所以显示失败但没日志的情况非常常见。我的建议是把“作业日志表”和“转换日志表”配置好然后每次执行后直接去日志表里查。webspoon中提供了“日志记录”相关的数据库连接配置你可以指定每个转换在执行时把日志写入哪张表。写一次日志表配置之后所有任务都能复用。日志表记录的内容包括执行ID、开始时间、结束时间、状态、日志文本排查问题时按执行ID过滤非常干净。另外注意webspoon如果长时间不清理日志表日志数据会膨胀得很厉害。我的经验是定期用事件调度去清理90天前的日志数据毕竟日志是用于排障的留太久没有意义还占数据库空间。4.2 错误处理分支没生效的三种原因转换里很努力地配置了错误处理tab但失败的行就是没有进入错误分支。我整理了三种最常见的情况第一该步骤本身不支持行级错误处理比如某些被动接收数据的步骤压根没有错误处理的能力第二错误处理目标步骤配置了但是目标步骤本身没有正确配置字段映射导致错误信息写不进去看起来像是“吞掉”了错误第三日志级别设置成ERROR后你从日志上看不到任何提示误以为分支没生效其实数据已经进了错误表只是日志没打印。还有一种特别隐蔽的情况某些步骤在成功处理行时也会把行发送到主输出错误处理分支只有在该步骤内部驱动到“错误流”时才会触发。如果你在普通流程里看不到错误分支的数据建议在测试环境中带入一条必然失败的数据比如非法的日期字符串这样很容易验证分支通不通。4.3 我踩过的坑字段截断、字符集和任务悬挂我在维护一个支付报表ETL项目时曾遇到错误日志表里中文全部变成问号错误描述也搜不到排查了很久才发现是webspoon服务端与MySQL连接时字符集不一致表中的字段是utf8mb4但数据库连接参数里没有追加useUnicodetruecharacterEncodingutf8。JDBC连接串里这些东西不显眼但出了问题极难发现。建议在初始搭建webspoon环境时就把所有数据库连接的字符集参数统一确认好。另一个坑是webspoon服务重启后正在执行的作业状态变成悬挂看起来一直处于运行中。碰到这种情况靠账号处理已经来不及了。解决思路不是每次手动重启而是在作业设计时让重复运行的任务具备“先检查状态位再决定是否继续”的机制。作业启动时先查一下有没有同名单据还在运行如果有就正常退出没有才继续执行。至于字段截断这个是新手最容易忽视的。错误日志中的错误描述字段千万不要定义成VARCHAR(100)至少TEXT起步。同理抓取原始输入行的字段最好也预留足够空间因为一条业务数据拼成文本后可能很长。我在线上系统里见过因为字段上限不足导致错误日志写入失败最后错误记录丢失的场景这种问题真的让人有一种想锤键盘的冲动。再补充一个关于webspoon的性能心得错误处理分支里的步骤尽量做到轻量。不要让错误分支去执行大型计算、多表关联这类逻辑因为错误行本身需要快速落库。错误处理分支如果太重会把整个转换拖慢甚至影响主流程性能。我的一般做法是错误分支里只做字段映射和表输出把更复杂的分析放到事后单独处理。最后分享一个小技巧实际操作里我发现一个很有用的习惯每次给错误日志表增加字段我都会把版本迭代记录下来。最初我只有error_desc一个字段后来发现要定位到底是哪一步出的问题加了step_name又发现有些错误是偶发的需要看原数据于是加了input_row再后来需要协调多套任务加了job_name和trans_name。这套演进是一个自然过程你不需要一开始就设计得完美无缺但要预留扩展空间保证以后加字段不会破坏现有流程。全局异常/错误捕获这件事难的不是配置某个步骤而是你有没有把每一次失败当成一个可以描述、可以追踪、可以复盘的事件来处理。我见过太多ETL项目上线前信心满满跑几天之后被各种异常搞得焦头烂额原因就是没有一套全局的兜底设计。这一课的思路不复杂但值得你把现有项目里的作业和转换翻出来逐步打上这些补丁。下一课有空的话我准备聊聊webspoon里的调度监控和性能分析到时候可以把这次讲的错误捕获流程和监控结合起来做个完整的看板。