上下文
为了使 OpenTelemetry 能够正常工作,它必须存储和传播重要的遥测数据。例如,当收到请求并启动一个 span 时,它必须可供创建其子 span 的组件使用。为了解决这个问题,OpenTelemetry 将 span 存储在 Context 中。本文档描述了 JavaScript 的 OpenTelemetry 上下文 API 以及它是如何使用的。
更多信息
上下文管理器
上下文 API 的工作依赖于上下文管理器。本文档中的示例将假定您已经配置了一个上下文管理器。通常,上下文管理器由您的 SDK 提供,但也可以像这样直接注册一个:
import * as api from '@opentelemetry/api';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
const contextManager = new AsyncHooksContextManager();
contextManager.enable();
api.context.setGlobalContextManager(contextManager);
根上下文
ROOT_CONTEXT 是空上下文。如果没有活动上下文,则 ROOT_CONTEXT 是活动的。活动上下文的解释如下 活动上下文。
上下文键
上下文条目是键值对。可以通过调用 api.createContextKey(description) 来创建键。
import * as api from '@opentelemetry/api';
const key1 = api.createContextKey('My first key');
const key2 = api.createContextKey('My second key');
基本操作
获取条目
使用 context.getValue(key) 方法来访问条目。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('some key');
// ROOT_CONTEXT is the empty context
const ctx = api.ROOT_CONTEXT;
const value = ctx.getValue(key);
设置条目
使用 context.setValue(key, value) 方法来创建条目。设置上下文条目会创建一个新的上下文,其中包含前一个上下文的所有条目,以及新的条目。设置上下文条目不会修改前一个上下文。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('some key');
const ctx = api.ROOT_CONTEXT;
// add a new entry
const ctx2 = ctx.setValue(key, 'context 2');
// ctx2 contains the new entry
console.log(ctx2.getValue(key)); // "context 2"
// ctx is unchanged
console.log(ctx.getValue(key)); // undefined
删除条目
通过调用 context.deleteValue(key) 来移除条目。删除上下文条目会创建一个新的上下文,其中包含前一个上下文的所有条目,但移除了由该键标识的条目。删除上下文条目不会修改前一个上下文。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('some key');
const ctx = api.ROOT_CONTEXT;
const ctx2 = ctx.setValue(key, 'context 2');
// remove the entry
const ctx3 = ctx.deleteValue(key);
// ctx3 does not contain the entry
console.log(ctx3.getValue(key)); // undefined
// ctx2 is unchanged
console.log(ctx2.getValue(key)); // "context 2"
// ctx is unchanged
console.log(ctx.getValue(key)); // undefined
活动上下文
重要提示:这假设您已配置了上下文管理器。没有上下文管理器,api.context.active() 将始终返回 ROOT_CONTEXT。
活动上下文是 api.context.active() 返回的上下文。上下文对象包含条目,这些条目允许跟踪单个执行线程的跟踪组件相互通信,并确保跟踪成功创建。例如,当创建一个 span 时,它可能会被添加到上下文中。稍后,当创建另一个 span 时,它可能会使用上下文中的 span 作为其父 span。这通过使用诸如 node 中的 async_hooks 或 AsyncLocalStorage,或者 Web 上的 zone.js 等机制来实现,以便通过单个执行传播上下文。如果没有活动上下文,则返回 ROOT_CONTEXT,它只是一个空上下文对象。
获取活动上下文
活动上下文是 api.context.active() 返回的上下文。
import * as api from '@opentelemetry/api';
// Returns the active context
// If no context is active, the ROOT_CONTEXT is returned
const ctx = api.context.active();
设置活动上下文
可以通过使用 api.context.with(ctx, callback) 使上下文变为活动状态。在 callback 执行期间,传递给 with 的上下文将由 context.active 返回。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('Key to store a value');
const ctx = api.context.active();
api.context.with(ctx.setValue(key, 'context 2'), async () => {
// "context 2" is active
console.log(api.context.active().getValue(key)); // "context 2"
});
api.context.with(context, callback) 的返回值是 callback 的返回值。callback 始终是同步调用的。
import * as api from '@opentelemetry/api';
const name = await api.context.with(api.context.active(), async () => {
const row = await db.getSomeValue();
return row['name'];
});
console.log(name); // name returned by the db
活动上下文执行可以嵌套。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('Key to store a value');
const ctx = api.context.active();
// No context is active
console.log(api.context.active().getValue(key)); // undefined
api.context.with(ctx.setValue(key, 'context 2'), () => {
// "context 2" is active
console.log(api.context.active().getValue(key)); // "context 2"
api.context.with(ctx.setValue(key, 'context 3'), () => {
// "context 3" is active
console.log(api.context.active().getValue(key)); // "context 3"
});
// "context 2" is active
console.log(api.context.active().getValue(key)); // "context 2"
});
// No context is active
console.log(api.context.active().getValue(key)); // undefined
示例
这个更复杂的示例说明了上下文是如何不被修改的,而是创建新的上下文对象的。
import * as api from '@opentelemetry/api';
const key = api.createContextKey('Key to store a value');
const ctx = api.context.active(); // Returns ROOT_CONTEXT when no context is active
const ctx2 = ctx.setValue(key, 'context 2'); // does not modify ctx
console.log(ctx.getValue(key)); //? undefined
console.log(ctx2.getValue(key)); //? "context 2"
const ret = api.context.with(ctx2, () => {
const ctx3 = api.context.active().setValue(key, 'context 3');
console.log(api.context.active().getValue(key)); //? "context 2"
console.log(ctx.getValue(key)); //? undefined
console.log(ctx2.getValue(key)); //? "context 2"
console.log(ctx3.getValue(key)); //? "context 3"
api.context.with(ctx3, () => {
console.log(api.context.active().getValue(key)); //? "context 3"
});
console.log(api.context.active().getValue(key)); //? "context 2"
return 'return value';
});
// The value returned by the callback is returned to the caller
console.log(ret); //? "return value"