异步跟踪

本指南介绍了如何向代码中添加异步跟踪功能。

前提条件

在开始之前,请确保您已完成以下操作:

添加异步跟踪

当操作跨多个线程时,可以使用一组异步跟踪函数。

例如,在多线程服务器中,请求由一个线程处理,然后在操作正在进行时放回到队列中。一段时间后,另一个线程收到操作已完成的通知,并“选择”了对该请求的处理。异步跟踪的目标是允许这些不相交的跟踪事件建立关联。

异步跟踪会将同一代码路径用于多个不同的处理流程。在前面的示例中,我们想知道特定函数的运行时长,或者某个值在给定时间点的运行时间。通过异步跟踪,我们希望跟踪相同的数据,但希望跟踪的是逻辑处理流,而不是基于程序位置的流。

在队列处理示例中,接收请求的代码会使用“Nonce”标记每个请求,“随机数”是请求之后的唯一值。此 Nonce 可以使用 TRACE_NONCE() 生成,它只是递增全局计数器。

我们来看看具体的运作方式。 首先,您需要声明一个用于存放 Nonce 的位置。这通常位于请求本身的上下文结构中:

typedef struct {
...
    // add the nonce to your context structure
    trace_async_id_t async_id;
} my_request_context_t;

当请求到达时,您将提取一个 Nonce 并启动异步跟踪流程:

// a new request; start asynchronous tracing
ctx->async_id = TRACE_NONCE();
TRACE_ASYNC_BEGIN("category", "name", ctx->async_id, "key", TA_STRING("value"));

您可以使用 TRACE_ASYNC_INSTANT() 宏定期记录跟踪事件(类似于我们在上面使用 TRACE_INSTANT() 宏执行的操作):

TRACE_ASYNC_INSTANT("category", "name", ctx->async_id, "state", TA_STRING("phase2"));

并通过 TRACE_ASYNC_END() 进行清理:

TRACE_ASYNC_END("category", "name", ctx->async_id);

请勿将“async”的用法与在进程中运行的异步循环混淆;它们并不相关。

流跟踪

异步跟踪旨在在同一进程内进行跟踪,但可能通过不同的线程进行跟踪。

有一种称为“流”跟踪的更高级别的跟踪机制,专门用于在进程或抽象层之间使用。

您调用 TRACE_FLOW_BEGIN() 来标记“流程”的开始。与 TRACE_ASYNC_BEGIN() 一样,您可以传入 Nonce 以标识这一特定数据流。流 ID 是一个无符号的 64 位整数。

然后,您可以(可选)调用 TRACE_FLOW_STEP() 以指示该流程中的跟踪操作。

操作完成后,使用 TRACE_FLOW_END() 结束该 flow。

例如,您可以在客户端和服务器之间使用流,以端到端的方式跟踪来自客户端、通过服务器和返回至客户端的请求。