Tracing:以单次调用为控制粒度的全栈调用及通信追踪
Created By RV, and licensed with Creative Commons "CC BY-NC-ND 4.0"
目的与场景
在这一章,讲解如何开启Tracing,追踪一次跨节点服务调用的完整时间线。
“时间线”,通过由各个TimeEvent、Action组成的完整的、跨节点、跨整个通信链路的多分支时间线,可以详尽的追踪服务调用的完整过程。
Tracing是为AI而进行的特异化设计。
对于人类来讲,信息过载;对于AI来讲,很好。
一. 概念
1. Tracing
Kree4X的Tracing,以单次服务调用为粒度,自动追踪Caller与Callee之间的全栈调用及通信过程,形成完整的、包含主时间线、多分支时间线的完整全链路追踪信息。
详情参考:追踪:KreeX Tracing
2. 时间事件: Time-based Action Event
一个时间事件,其实质是一次函数或对象方法调用中,某个时间节点的现场快照,它记录了现场某个切面的瞬时状态。
详见:时间事件: Time-based Action Event
3. 时间线:Timeline
时间线Timeline,是指TimeEvent按照时间戳顺序排列、连接所形成的有向序列。
二. 开启Tracing
在下边的示例中,我们将重点讲解:
- 手工开启服务存根的Tracing
- 发起服务调用
- 调用完毕获取服务存根最后一个Tracer
- 使用Tracer,将服务调用完整的时间线输出为svg
import Kree4n from '@kree4js/kree4n'
import Trace from '@kree4js/tracing'
// 方法调用最后一个参数是动态注入的Context,携带了Tracer
callee.register('calc', {
add (a, b, ctx) {
// 获取tracer,开启一个新的逻辑“phase”
const phase = ctx.tracer?.phase('calc.add')
// 标记开始处理
phase?.trace(`Start Handling ${a} + ${b}`, '', 'calc.add.start', 'detail info')
// 业务操作
const result = a + b
// 标记处理结束
phase?.trace(`Done Handling ${a} + ${b}`, '', 'calc.add.done', 'detail info')
return result
}
})
// 获取服务存根,开启tracing
const calc = caller.service('calc')
// 为服务存根后续调用开启Tracing
calc.traceEnabled = true
// 调用服务
const result = await calc.add(10, 20)
// 导出tracing时间线为SVG
const tracer = calc.lastTracer
// svg格式输出,使用框架内置FileWriter写入器
tracer.output(new Trace.SvgFormatter(), new FileWriter('./tmp', '.svg'), Trace.OutputLevel.INFO)
时间线输出
输出的时间线,包含了完整的Caller端调用栈、通信栈、Callee端调用栈。
是一个多分支的,完整时间线结构。
完整的时间线svg,点击访问调用的完整时间线
图太宽了,不适合在此完整显示,下图仅是全图局部。

业务方法的时间线输出
输出的svg中,包含了add方法内部的Tracing时间线。如下图:

三. 须强调的细节
1. 输出的svg是可以交互的
鼠标移动到一个Action上,会显示一个Action的概要信息。
点击一个Action,会以此Action为中心自动对中,并显示Action详情。
点击一个分支时间线,会自动调整到分支时间线起始。
各种其他交互,……
2. 业务方法内部如何Tracing?
注意calc.add(a,b,ctx),最后一个ctx参数。
Callee端,方法被调用时,框架会自动注入一个Context参数。
使用ctx.tracer可以获取到Tracer对象,使用tracer api记录业务操作过程。
业务操作的Tracing,会被自动融合到整个完整的调用时间线中。
3. 默认全局关闭
全局尺度,tracing默认是关闭的。
全局、全量Tracing,性能、资源消耗角度,都是不可承受之重。
4. 以服务存根单次调用为控制粒度
服务存根手工traceEnabled = true后,后续调用会自动记录Tracing。
5. 没开启Tracing,调用出错时怎么记录?
Kree4X內建“自动重试、自动Tracing”机制。
使用服务存根,开启重试策略,当调用远程服务失败时,会在最后一次重试时,自动Tracing。
中级篇,在“重试策略”章节,会完整讲解整个过程。
6. Tracing时间线输出格式
示例中,使用的是SvgFormatter,输出为svg可交互矢量图格式。
系统还內建有JsonFormatter、TextFormatter,可以输出为JSON、纯文本格式。
示例中,使用FileWriter,将时间线输出为文件。
系统还內建有ConsoleWriter,将时间线输出到控制台;LoggerWriter,将时间线输出到日志器logger。
四. 涉及到的API:
1. 开启Tracing traceEnabled
在服务存根上设置 traceEnabled = true,该服务的所有后续调用都会被追踪。
/**
* 获取或设置trace启用标志。
* 设为true时,本服务集群上之后的所有调用都会被追踪。
*/
ServiceStub.traceEnabled: boolean
2. 获取最后一个Tracer lastTracer
调用完成后,通过服务存根的 lastTracer 获取本次调用的Tracer对象。
/**
* 获取最近一次服务调用的tracer。
* 若尚未发生过调用或追踪未启用,返回undefined。
*/
ServiceStub.lastTracer: Tracer
3. 在服务实现中获取Tracer ctx.tracer
服务方法的最后一个参数 ctx 包含 tracer,可通过 tracer.phase() 获取TracePhase。
/**
* 在服务实现中,最后一个参数是由框架注入的上下文。
* 通过ctx.tracer访问本次调用的tracer。
*/
ctx.tracer: Tracer
4. 创建TracePhase tracer.phase()
创建一个TracePhase,提供tracing的逻辑分段。
/**
* Create a TracePhase to avoid tracer.trace(phase, ...)
* @param {string} name - 阶段名称。
* @returns {TracePhase} 追踪阶段实例。
*/
phase(name): TracePhase
5. 记录追踪事件phase.trace()
在当前Tracer的时间线上追加一个时间事件。
/**
* 在Tracer主时间线末尾追加一个基于时间的动作事件。
* @param {string} summary - 事件摘要。
* @param {any} actor - 执行该动作的行为者。
* @param {string} action - 动作名称。
* @param {...any} args - 与该动作相关的附加参数。
* @returns {TimeEvent} 创建的时间事件。
*/
phase.trace(summary: string, actor: any, action: string, ...args: any[]): TimeEvent
6. 输出Tracing时间线tracer.output()
将Tracer的时间线格式化并输出。
/**
* 输出tracer的时间线。
* @param {Formatter} formatter - 格式化器(SvgFormatter、JsonFormatter、TextFormatter)。
* @param {Writer} [writer] - 写入器(FileWriter、ConsoleWriter、LoggerWriter)。
* @param {any} [level] - 输出级别(如Trace.OutputLevel.INFO)。
* @returns {string|any} 格式化后的结果。
*/
output(formatter, writer?, level?): string | any
五. 可运行代码
完整示例代码,参见:05-tracing.mjs