上下文
OpenTelemetry 通过存储和传播遥测数据来工作。例如,当一个已检测的应用程序收到一个请求并开始一个 span 时,该 span 必须对创建一个子 span 的组件可用。为了满足这一需求,OpenTelemetry 将 span 存储在活动上下文中。
PHP 执行上下文
上下文 API 在单个 PHP 执行上下文中是全局可用的,并且当前执行上下文中只能有一个 活动上下文。
存储
上下文可以存储值(例如 Span),并使用 Storage 来跟踪存储的值。默认情况下,会使用通用的 ContextStorage。PHP 的 OpenTelemetry 支持其他上下文存储,以应对不常见的用例,例如使用 fibers 进行异步或并发执行。
上下文键
值以键值对的形式存储在上下文中。上下文键用于从上下文中存储和检索值。
可以通过调用 OpenTelemetry\Context\Context::createKey() 来创建键,例如
use OpenTelemetry\Context\Context;
$key1 = Context::createKey('My first key');
$key2 = Context::createKey('My second key');
活动上下文
活动上下文是 Context::getCurrent() 返回的上下文。上下文对象包含允许遥测组件相互通信的条目。例如,当创建一个 span 时,它可以被激活,这将创建一个新的活动上下文并存储该 span。之后,当创建另一个 span 时,它可以将活动上下文中的 span 用作其父 span。如果没有上下文是活动的,则返回根上下文,它只是一个空的上下文对象。
use OpenTelemetry\Context\Context;
// Returns the active context
// If no context is active, the root context is returned
$context = Context::getCurrent();
设置和获取上下文值
通过使用 $context->with($key, $value) 方法将值存储在 Context 中。设置上下文条目会创建一个新的上下文,并在其存储中包含新条目,其中包含 $value。
上下文是不可变的。设置上下文条目会创建一个新的上下文,并在其存储中包含新条目:$context->with($key, $value)。使用 $context->get($key) 检索值,例如
use OpenTelemetry\Context\Context;
$key = Context::createKey('some key');
// add a new entry
$ctx2 = Context::getCurrent()->with($key, 'context 2');
// ctx2 contains the new entry
var_dump($ctx2->get($key)); // "context 2"
// active context is unchanged
var_dump(Context::getCurrent()->get($key)); // NULL
如果在当前上下文中找不到该值,则会检查每个父级,直到找到该键,或者达到根上下文。
激活上下文
可以通过调用 $context->activate() 来激活上下文。
use OpenTelemetry\Context\Context;
$key = Context::createKey('my-key');
$ctx = Context::getCurrent();
$ctx2 = $ctx->with($key, 'context 2');
$ctx2->activate();
assert($ctx2 === Context::getCurrent());
作用域
$context->activate() 的返回值是 Scope。您必须调用 detach() 来停用该作用域,这会重新激活先前活动的作用域。
$scope->detach() 的返回值是一个整数。返回值为 0 表示作用域已成功停用。非零值表示该调用是意外的。这可能发生在与作用域关联的上下文是
- 已经停用
- 不是当前执行上下文的一部分
- 不是活动上下文
DebugScope
为了帮助开发人员定位上下文和作用域问题,我们提供了 DebugScope。在启用了断言的 PHP 运行时中,激活的 Context 会被包装在 DebugScope 中。DebugScope 会跟踪作用域激活的时间,并且其析构函数会在作用域未被停用时触发错误。错误输出包含一个回溯,显示激活上下文的代码。
以下代码将触发一个错误,抱怨作用域未被停用,并提供作用域创建位置的回溯。
use OpenTelemetry\Context\Context;
$key = Context::createKey('my-key');
$scope = Context::getCurrent()->with($key, 'value')->activate();
//exit without detaching $scope
在某些情况下,这可能会出现问题,尤其是在可能 exit 或 die 的旧应用程序中。在这种情况下,活动 span 将不会完成并导出,而 DebugScope 会发出响亮的警告。
如果您了解 DebugScope 抱怨的原因并接受风险,则可以通过将 OTEL_PHP_DEBUG_SCOPES_DISABLED 设置为真值来完全禁用该功能。
嵌套上下文
活动上下文执行可以嵌套。这就是跟踪可以具有嵌套 span 的方式。
use OpenTelemetry\Context\Context;
$key = Context::createKey('my-key');
var_dump(Context::getCurrent()->get($key)); //NULL
$scope2 = Context::getCurrent()->with($key, 'context 2')->activate();
var_dump(Context::getCurrent()->get($key)); //'context 2'
$scope3 = Context::getCurrent()->with($key, 'context 3')->activate();
var_dump(Context::getCurrent()->get($key)); //'context 3'
$scope3->detach(); //context 2 is active
$scope2->detach(); //original context is active
var_dump(Context::getCurrent()->get($key)); //NULL
异步环境中的上下文
对于异步 PHP 编程,例如 Swoole 或基于 Fiber 的 Revolt 事件循环,可以有多个活动上下文,但每个执行上下文仍然只有一个活动上下文。
对于基于 Fiber 的实现,Context 与活动 Fiber 相关联,并通过挂钩 PHP 的 Fiber 初始化、分叉和销毁处理程序来适当地进行分叉、切换和销毁。
对于其他异步实现,可能需要自定义上下文存储才能正确互操作。请查看 注册表 以获取存储实现。