☰
Webspoon全局异常捕获实战:日志表与错误流搭建可复用框架
2026/10/6 3:46:29 网站建设 项目流程

上一课我们讲了 kettle 里的局部错误处理,简单说就是给某个步骤开启错误流,把异常行单独捞出来。发出去之后不少朋友留言问,webspoon 里面到底怎么把整个任务里的错误都收拢起来,而不是每个步骤都去接一根红线。这一课就专门把全局异常/错误捕获这套东西讲透。内容会比较长,建议你先收藏再对着操作。

这个续篇咱们不重复基础概念,直接回答三个问题:全局异常抓的是哪些东西、webspoon 里有哪些现成的抓手、怎么搭出一套可复用的全局错误捕获框架。最后把我自己踩过的坑一并列出来,方便你直接对照排查。

1. 先理清“全局异常”到底要抓哪几类

很多朋友一上来就问我“全局异常捕获怎么写”,说实话这个问题的前提没理清。全局异常并不是把每个步骤的错误流都接出来就叫全局,先把异常分清楚,才知道框架该往哪个方向搭。

1.1 业务数据错误:不致命但要留证

这类错误是最常见的,比如订单金额字段出现了负数、日期格式不对、客户编号超过了目标表字段长度、主键在目标表里冲突。它们的特点是“行级”错误,数据本身有问题,但整个转换还能继续跑。

我见过不少团队的做法是:表输出步骤校验失败后,整个作业直接失败,调度告警响一晚上。但其实这种错误更应该做的是把坏行剥离出来,记录到错误日志表,同时让正常数据继续流转。你想想,一次同步一百万行,里面有两百行脏数据,因为这两百行让整批任务回滚重跑,时间成本和资源成本都很高。

所以对业务数据错误,全局框架要做的事情是三个:识别、留痕、放行正常数据。

1.2 步骤执行异常:必须立刻暴露

这类错误发生在步骤本身,比如 SQL 语句写错、目标表不存在、数据库连接超时、驱动类没加载。它的危害是让整个转换的流程中断,后续步骤全部拿不到数据。

步骤执行异常不能再“放行”,必须立刻暴露。这里有个容易被忽视的点:在 webspoon 里,步骤执行异常的表现形式和桌面版不完全一样。桌面版本地跑的时候,日志窗口会固定弹出错误信息;webspoon 里如果你的日志级别设置得不够细,或者被浏览器缓存干扰,可能看到的就是一个笼统的“失败”。

所以全局框架在设计时,必须把步骤级的技术异常和行级的数据错误分开对待。前者交给作业控制层处理,后者交给数据校验层处理。

1.3 作业环境故障:属于“看不见的敌人”

环境故障包括内存溢出、磁盘空间写满、并发任务抢占数据库连接池、服务器重启导致进程中断。这类错误最麻烦,因为它的报错信息往往不体现在具体的某一条数据上,而是直接让整个运行环境崩溃。

我遇到过一次典型的案例:webspoon 跑一个多小时的增量同步,跑到最后二十分钟时内存溢出,整个流程死掉,重新打开后台一看日志全丢了。这时候才发现,错误日志没有持久化,实时日志又因为服务重启清空了,环境故障到底出在哪个环节根本无从查起。

这让我后来定下一个原则:凡是要上生产调度的 ETL 任务,错误信息必须落库,不能依赖浏览器里那点实时输出。这是全局异常捕获框架能不能扛住环境故障的关键。

2. webspoon 里的捕获机制该怎么选

搞明白要抓哪些异常之后,再来看 webspoon 里能用的机制。Kettle 本身提供了不少捕获能力,但 webspoon 环境下有些侧重和桌面版不一样,得先盘一盘。

2.1 桌面版和 web 版存在哪些差异

先说结论:webspoon 的核心引擎和桌面版 PDI 是同一套,不能说能力有缺失,但交互方式和使用习惯确实不同。

桌面版跑转换时,日志输出到本地控制台,你可以随便滚动翻看,还能双击错误日志直接定位到具体步骤。webspoon 的日志在浏览器页面上展示,默认情况下刷新页面之后运行记录就没了,想回看历史日志只能靠服务端落盘,或者日志表持久化。这一点对错误捕获的选型影响很大。

另外,webspoon 右键菜单依赖浏览器兼容性。我建议用 Chrome 或 Edge 操作,遇到某些国产浏览器,右键菜单偶尔会失灵,连“错误处理”那个选项都弹不出来。真碰上了别以为是功能缺失,换个浏览器试试就好。

还有一个实践上的差异:webspoon 的 lib 目录和桌面版的 lib 目录不是同一个地方。桌面版装个驱动直接丢进 lib,重启客户端完事;webspoon 需要把驱动放对服务端目录,比如 UCanAccess 这类 Access 驱动、一些国产数据库驱动,放错了位置加载不到,报错信息又不会提示“驱动没找到”,而是显示连接超时,很容易绕晕。

2.2 日志通道、数据库日志表、错误流三板斧

Kettle 的捕获机制说到底就三个抓手,理解了它们,整个全局框架的逻辑就清晰了。

第一个是日志通道,英文叫 Log Channel。Kettle 里每次运行转换或作业,都会生成一个唯一的 channel id,日志会按照这个通道归类。你可以把它理解为快递单号,所有跟这次运行相关的日志都能通过这个单号查出来。在 webspoon 的运行结果面板里,展开每个步骤就能看到对应的通道 ID。

第二个是数据库日志表。作业或转换可以配置一个“日志表”标签页,把运行日志写入数据库。这一步非常关键,它能把运行状态变成可查询的“事实数据”。常用的表包括作业日志表、步骤日志表、日志通道历史表等。配置路径一般是:作业属性 → 设置 → 日志表,勾选要记录的表,然后指定数据库连接。

第三个是错误流,也就是步骤级 Error Handling。在某一步上右键,选择“错误处理”并启用,系统会为这个步骤额外生成一个错误出口。这个出口里走的是被判定为失败的行数据,你可以把它接到写日志步骤,或者接到表输出步骤落库。

这三板斧组合起来,“行级错误”走错误流,“步骤级错误”走日志表,“运行环境状态”走日志通道。各司其职,不冲突。

2.3 我最终选择的组合

个人做集成的习惯是:分层搭配,而不是迷信某一种方案。我自己在 webspoon 里常用的组合如下:

数据校验层用“字段校验”加“过滤记录”,把业务数据错误拦截在流内;步骤异常走数据库日志表,把执行状态落库;作业失败分支接一个专门写错误登记的子作业,把环境级异常和步骤级异常汇总成一条统一的错误记录。整体框架并不复杂,但每个层次的出口都明确。

这套组合唯一的代价是要维护一张自定义的错误日志表。但换来的是所有任务、所有节点的错误都能在一个地方查,不用再去翻服务端日志文件。后面第三部分我就按这个思路带你搭一遍。

3. 手把手搭一个全局错误捕获框架

下面开始实操。假设你已经在 webspoon 里建好了数据库连接和一个测试用的转换,我们来搭建框架的核心部分。我以 MySQL 为例,其他数据库思路一样。

3.1 第一步:设计统一的错误日志表

先建表。这张表是全局错误捕获的“收口点”,一定要设计好,后面改起来麻烦。我建议至少包含以下字段。

CREATE TABLE etl_error_log ( log_id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '自增主键', exec_id VARCHAR(64) NOT NULL COMMENT '执行批次ID,同一次运行保持一致', task_name VARCHAR(200) NOT NULL COMMENT '作业或转换名称', step_name VARCHAR(200) NULL COMMENT '出错步骤名称', error_type VARCHAR(20) NULL COMMENT '错误类型:DATA/TECH/ENV', error_code VARCHAR(50) NULL COMMENT '业务错误码,如 BUS_001', error_message VARCHAR(1000) NULL COMMENT '错误描述', data_snapshot TEXT NULL COMMENT '原始数据JSON快照', server_name VARCHAR(100) NULL COMMENT '运行节点,分布式时定位用', retry_count INT DEFAULT 0 COMMENT '重试次数', error_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '错误发生时间', KEY idx_exec_id (exec_id), KEY idx_error_time (error_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ETL全局错误日志表';

这里重点说几个字段的用意。exec_id非常关键,同一次任务运行会可能产生多条错误记录,通过它可以把这些记录归拢到同一个批次下,排查时直接看这个批次所有错误就够了。data_snapshot是调错神器,把出错那一行数据存成 JSON,事后分析不需要重新跑一遍流程。server_name在集群部署时尤其有用,能在多台节点里快速定位是哪台机器出了事。

如果你已经有现成的运维监控体系,表里还可以加alert_status这样的字段,标记这条错误是否已经触发过告警,避免重复告警轰炸。

3.2 第二步:封装全局错误处理子转换

框架不能被业务转换绑死,所以我把写日志表的逻辑单独封装成一个子转换,取名ETL_ERROR_WRITER。它的输入是错误流上的字段,输出就是向etl_error_log表插入一条记录。

子转换内部结构很简单:表输入不需要,直接用“自定义常量”或“获取系统信息”步骤生成exec_id,然后接一个“表输出”步骤写入错误日志表。真正要注意的是健壮性:这个子转换本身不能出问题,否则主流程挂了,错误也写不进去,框架就白搭了。

我在这踩过坑。一开始我用普通表输出直接写,结果错误流里的字段长度超过表字段长度,子转换自己报错了,主流程反而因为错误处理分支失败而卡住。后来我在子转换里做了三件事:所有写入字段在进入表输出前统一用“字符串操作”或“字段选择”做长度截断、类型转换,强制不超长;把连接池配置独立出来,不和主流程共用同一个连接;子转换外面再加一层作业保护,如果连子转换都失败了,至少往一个“死信表”里写一条最基础的信息。

如果你需要在错误记录里保存整行原始数据,我建议用 JavaScript 步骤先拼 JSON 字符串,而不是直接用“JSON 输出”步骤,因为后者的字段映射在错误流场景里容易产生嵌套层级,反而不直观。示例代码如下。

// 把原始行拼成JSON快照,注意字段名要和前面输入一致 var snapshot = "{"; snapshot += "\"order_id\":\"" + order_id + "\","; snapshot += "\"order_amount\":\"" + order_amount + "\","; snapshot += "\"customer_id\":\"" + customer_id + "\""; snapshot += "}";

需要提醒的是,webspoon 里 JavaScript 步骤用的脚本引擎版本有限,部分 ES6 的新语法不一定支持,尽量用最简单的字符串拼接,别用模板字符串和箭头函数,出了问题排查起来很吃力。

3.3 第三步:接入主作业与日志配置

子转换封装好之后,接下来把它接入主流程。这里要说清楚“转换级”和“作业级”两个层面。

转换级接入:在业务转换里,对需要监控的步骤启用错误处理,建议从“表输入”和“表输出”这两个步骤入手。右键步骤,找到“错误处理”,启用后给错误流命名,比如ERROR_OUT。然后把错误流接到ETL_ERROR_WRITER子转换上。这样只要这一步有数据级错误,就会走子转换落库。

作业级接入:新建一个作业,把主转换作为一个作业项拖进去。此时你会发现作业项上有“成功”和“失败”两个出口。失败出口不要闲着,接一个“写日志”步骤或者一个小作业,负责登记作业级异常。我自己习惯在失败出口再接一个“执行 SQL 脚本”步骤,插入一条错误登记记录。

INSERT INTO etl_error_log (exec_id, task_name, step_name, error_type, error_code, error_message) VALUES ('${exec_id}', '${job_name}', '${step_name}', 'TECH', '${error_code}', '${error_message}');

注意,这些${}变量能不能正确解析,取决于你在作业里有没有定义它们。你可以在作业的开始位置用“设置变量”步骤,把当前时间戳生成一个 UUID 之类的值赋给exec_id,这样同一批次的错误记录就能关联起来。

还有一个容易漏掉的点:作业属性里的“日志表”标签页一定要配置。选中作业,右键编辑,在“日志表”里勾选“作业日志记录”并选择数据库连接。这样即使自定义错误登记没跑成功,官方日志表里至少留了一份底,不至于全无痕迹。

3.4 第四步:在 webspoon 里实际跑一次验证

框架搭建完成后,必须实测验证。我习惯造一条脏数据来测。

比如源数据里有一个订单金额为负的记录,正常情况下“表输出”会报约束错误。你在 webspoon 中点击运行,等任务跑完后,不要只看任务是否成功,先去数据库里查错误日志表。

SELECT exec_id, task_name, step_name, error_type, error_code, error_message, error_time FROM etl_error_log ORDER BY log_id DESC LIMIT 10;

如果能看到刚才那条脏数据的错误记录,说明转换级捕获生效。接着,故意把表输出里目标表名改成一个不存在的表,再跑一次,看作业失败分支是否成功登记了一条 TECH 类型错误。两条都通过,这套全局框架就可以投入使用。

这里补充一点 webspoon 的操作细节:运行任务时,在弹窗里把日志级别调到“基本日志”或者“详细日志”,方便观察过程。但正式调度时千万不要用“行级日志”,数据量大时浏览器会被日志刷爆,我见过有人线上用行级日志,跑十分钟直接卡死。

4. 三种错误分流写法对比,别再只会一种

框架搭好了,但很多朋友在写具体分流逻辑时容易纠结。我把实战里用的三种写法做了对比,你可以按场景选。

4.1 步骤级 Error Handling:快速、局部

这是最直接的写法。在某一步骤右键启用错误处理,错误流单独接出去。它的价值在于配置快,代码都不用写,适用于单个高风险的步骤。

但它的缺点也明显:每个步骤都要单独配,业务一复杂,转换里到处都是红箭头,整个图看起来像蜘蛛网。后续维护排查时,你根本分不清哪根红线对应哪个错误类型。我建议只在表输入、表输出、字段校验这三个最关键的步骤上用,其余步骤不要滥用。

4.2 校验 + 条件分流:数据质量场景首选

如果错误主要是数据质量原因,我强烈建议先把异常识别独立出来。做法是:在数据流中间加一个“字段校验”步骤,把规则集中配置,然后用 Switch/Case 或过滤记录把合规数据和异常数据分成两个分支。

这种方式最大的好处是规则可视化,业务人员也能看懂。你可以在校验步骤里给不同规则配置错误码,比如金额非法用BUS_001,日期非法用BUS_002,这样错误日志表里的error_code就有了业务含义,后续做质量报表也方便。

缺点是只能捕获你所定义规则以内的错误,数据库层面的约束异常、连接异常它管不着,所以通常要和作业级分支搭配使用。

4.3 作业级失败分支:全局兜底

上面的写法再多,终究管不到“作业启动失败”这类整体性问题。所以每个 ETL 调度任务,我都要求在作业层面留一个失败兜底分支:只要作业项失败,就触发错误登记,有条件的话再发一个通知。

有些朋友问“通知怎么发”,webspoon 里可以用“发送邮件”作业项,或者用“HTTP 客户端”调企业微信、钉钉机器人接口。我建议错误登记和通知不要放在同一个作业项里,错误登记必须保证执行,通知丢了还能事后补查,登记丢了就真的什么都找不回来了。

选型其实不冲突,我自己项目中就是三层都用:数据级走校验分流,关键步骤走 Error Handling,作业失败走既兜底保护。下面这张表供你快速决策。

方式配置位置捕获范围优点缺点
步骤级 Error Handling单步骤行级数据错误配置快、直接步骤一多图就很乱
校验 + 条件分流转换中间规则内数据错误规则可配置、错误码规范覆盖不到系统异常
作业级失败分支作业出口步骤级与环境级全局兜底、可接通知拿不到具体行数据

5. 踩坑实录与排查速查表

这一部分是我的实战总结,每一条都有血泪代价。不要觉得坑小事小,生产环境翻车往往就出在这些细节上。

5.1 错误日志表一条都没写入

框架搭完,跑完任务发现错误日志表是空的。先别急,按下面这个顺序排查。

第一步,确认错误流有没有真正接上。很多人建好了错误流入口,但表输出步骤自己也有一个连接属性,如果错把错误流接到了正常输出上,等于什么都写不进日志表。第二步,确认字段名是否匹配。错误流里带的字段可能和你子转换里预期的字段不一致,字段不匹配时表输出直接报错,但日志表不会有记录。第三步,确认数据库连接的自动提交设置。某些连接池配置下,表输出步骤执行完事务没有提交,数据就一直停在缓存里。

5.2 全局错误子转换自己挂了

这个问题最讽刺:错误捕获框架本身成了最不可靠的环节。我遇到的真实场景是,错误记录里某个字段值太长,导致子转换的表输出步骤报“字段超长”,然后子转换也进入错误状态,主转换反而被这个错误分支拖死。

解决方案前面提过:在子转换入口处强制截断字段长度、转换类型,错误落库用最简单的 INSERT,不做任何复杂逻辑。如果还担心,就在作业失败出口再套一个“写日志”步骤,把错误信息先打出来,至少留个现场。

5.3 webspoon 跑大转换内存溢出

webspoon 进程在默认配置下内存并不大,尤其数据量大或开启行级日志时,很容易 OOM。官方社区里推荐的调整方式是在启动脚本里加大 JVM 堆内存。

# 以jetty方式运行的webspoon,常见调整方式如下 export JETTY_OPTS="-Xms512m -Xmx2048m -Dfile.encoding=UTF-8"

改完重启服务。如果你的 webspoon 部署在 Tomcat 下,对应调整CATALINA_OPTS。另外,如果你不需要实时看日志,尽量把运行配置改成“远程执行”,让任务在独立的 Carte 引擎上跑,减少浏览器和服务端的内存双向压力。

5.4 日志中文乱码

webspoon 里遇到的乱码多半不是转换流程问题,而是编码环境不一致。统一在kettle.properties里设置默认编码:

KETTLE_DEFAULT_ENCODING=UTF-8

数据库连接串上也要注意,比如 JDBC URL 追加useUnicode=true&characterEncoding=UTF-8,目标表建议直接用 utf8mb4。如果你处理的是 GBK 来源的老系统数据,必须在表输入步骤里明确指定输入编码,不能只靠全局配置。

5.5 常见问题速查表

现象可能原因解决方法
错误日志表没记录错误流未接入或字段不匹配检查错误流连线,逐字段对齐输入输出
错误流进入死循环子转换失败又触发分支回来给错误子转换单独设连接池,避免回环
作业失败但没登记作业日志表未配置在作业“日志表”标签页配置数据库连接
webspoon 卡死无响应内存溢出或行级日志调大 JVM 堆,生产用基本日志
中文乱码编码不统一设置 KETTLE_DEFAULT_ENCODING,URL 加编码参数
自定义驱动加载失败驱动放错目录确认驱动放到 webspoon 服务端 lib 目录并重启

6. 最后分享一点个人经验

我做了这些年 ETL,越来越觉得“全局异常捕获”不是某一个步骤能搞定的,它更像一个分层的容器:数据校验管行级错误,日志表管运行痕迹,作业分支管全局兜底。三件事缺一不可,但也不需要每个步骤都接红线。少即是多,把关键节点的错误管住,比把整个转换画成一张蜘蛛网有用得多。

还有一个小技巧,我在每次上线新任务之前,都会刻意造两类故障数据做“故障演练”:一类是业务脏数据,一类是故意写错的 SQL。前者验证错误流能不能正确落库,后者验证作业失败分支能不能正确触发。这个习惯帮我拦下了不少线上问题,建议你也试试。

这一课的内容就是这些了。下一篇我们会继续聊聊基于这套错误日志表做任务运行质量看板的事,到时候你会发现,全局错误捕获不只是用来“查错”的,还能帮你反推数据质量和调度稳定性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询