Instana Node.js API
您可以将 Instana Node.js API 与所有 Instana Node.js Tracer 软件包一起使用,以实现高级监控配置、自定义跟踪或特定集成。
- 初始化Instana Node.js 收集器
- 使用多个Instana收集器软件包
- 访问Instana API
- 验证追踪状态
- 在初始化后设置记录器
- 访问当前活动的范围
- 启用跟踪,无需输入有效区间
- 使用 SDK 手动创建 Spans
- 手动复原 Async 上下文
- OpenTracing 集成
- 相关话题
初始化 Instana Node.js 收集器
如 安装页面中所述, @instana/collector 的主要导出是用于初始化 Instana Node.js 收集器的函数。 即,当您使用 require 或 import装入模块时,它将返回需要由应用程序代码调用的函数,然后再需要或导入任何其他模块。 对于使用 CommonJS 模块系统或 ESCMAScript 模块系统的 Node.js 应用程序,以下部分对此进行了更详细的说明。
CommonJS
您只需要调用一次由 @instana/collector 导出的初始化函数,但必须在 Node.js 应用程序的开头,在执行任何其他 require 语句之前以及在执行任何其他代码之前执行此操作:
require('@instana/collector')();
初始化函数将返回对自身的引用,如果您想要访问 Instana Node.js 收集器提供的其他导出,那么该引用将相关。 即,以下两个片段是等效的:
const instana = require('@instana/collector')();
等同于:
const instana = require('@instana/collector');
instana();
初始化函数接受一个可选参数,即配置对象:
const instana = require('@instana/collector')({
// ... configuration object, see configuration documentation
});
有关配置对象的详细信息,请参阅配置页面。
有关安装和初始化 Instana Node.js 收集器的更多信息,请参阅安装 Node.js 收集器和验证收集器集成。
ESCMAScript 模块
对于使用 ECMAScript 模块系统的Node.js应用程序,通过Node.js载入标志初始化 InstanaNode.js收集器。
如果你想在基于ECMAScript模块的 "Node.js应用程序中使用Instana "Node.jsSDK,你可以通过以下方式获得Instana API的引用:
import instana from '@instana/collector';
要获取 express 应用程序中的当前跨度,请运行下例所示命令:
import instana from '@instana/collector';
...
const app = express();
app.get('/', (req) => {
const span = instana.currentSpan();
console.log(span);
console.log(span.getTraceId());
})
您无需再次初始化 InstanaNode.js收集器,因为收集器是通过启动时提供的Node.js加载标志 "--import或 "--experimental-loader初始化的。
使用多个 Instana 采集器软件包
在 Kubernetes 环境下,您可能有多个Instana收集器在运行。
Kubernetes
使用 Instana AutoTrace 网络钩子 时,Node.js 收集器会自动安装到 /opt/instana/instrumentation/nodejs/node_modules/@instana/collector 目录中。
如果要在基于 Kubernetes的 Node.js 应用程序中使用 Instana Node.js SDK ,建议您在项目中 安装 @instana/collector 作为直接依赖关系。
const instana = require('@instana/collector')();
Instana Node.js 收集器的初始化需要一些时间。 要立即使用 SDK 开始创建跨度,请添加简短延迟 (例如, 500 毫秒) ,以确保 Instana Node.js 收集器已就绪并已建立与 Instana 主机代理程序的连接。
访问 Instana 应用程序接口
大多数应用程序只需初始化 Instana 收集器即可进行监控。 不过,在某些情况下,您可能需要与 Instana API 的其他功能或部分进行交互,而不仅仅是初始化收集器。 这些情况可能包括需要高级监控配置、自定义跟踪或特定集成的场景。
// At the start of your main module/application entry point, the function
// returned by require('@instana/collector') needs to be called to initialize
// the Instana Node.js collector. This needs to happen before anything else is
// required or imported.
const instana = require('@instana/collector')();
...
// Use the instana reference acquired by the require statement to access its
// API, for example:
instana.setLogger(...);
// or:
instana.currentSpan();
// or:
instana.opentracing.createTracer();
// ...
如果需要在多个文件中引用 @instana/collector ,那么无需再次初始化 @instana/collector 。 可以在所有文件中使用 const instana = require('@instana/collector'); 而不是 const instana = require('@instana/collector')(); ,但应用程序的主模块和应用程序入口点中的文件除外。
验证追踪状态
要验证是否启用了跟踪,可以使用 instana.isTracing() 方法来检查跟踪的状态。 在需要根据跟踪状态有条件地执行代码的场景中,可以使用此方法。 但是,在大多数情况下,不需要检查是否启用了跟踪,因为 Instana 会自动处理跟踪。 您可以使用此方法进行调试,也可以在需要确保显式控制跟踪的复杂环境中使用此方法。
const instana = require('@instana/collector')();
if (instana.isTracing()) {
console.log('Tracing is enabled.');
} else {
console.log('Tracing is disabled.');
}
初始化后设置记录仪
使用 instana.setLogger(logger) 向 @instana/collector 提供自定义记录器,而不是默认的pino记录器。
如前所述,在需要/导入任何其他包之前,您需要立即调用初始化函数(由 require('@instana/collector')返回),否则 Instana 的自动跟踪将仅部分工作。 尤其是,这要求您先初始化 Instana,然后再需要日志记录包(例如 bunyan、pino 或 winston)。 如果在初始化 Instana 之前需要日志记录包,那么您将不会在 Instana 中看到日志消息。
另一方面,您可能希望将自己的记录器传递到 Node.js 收集器,以便其日志消息具有相同的格式,并且写入到与应用程序的其余日志消息相同的日志文件/目标。 如果您要将记录器传递到收集器的初始化函数,那么在初始化 Instana 之前需要该日志记录包。 要解决此循环依赖关系,@instana/collector提供函数setLogger以先初始化没有定制记录器的 Instana,然后再设置该记录器。
要提供具体示例,不支持以下内容:
// WRONG
const instana = require('@instana/collector');
// The bunyan package will not be instrumented by Instana, because it is
// required *before* Instana has been initialized.
const bunyan = require('bunyan');
const logger = bunyan.createLogger(...);
// Now Instana is initialized, after the logging package has already been
// required. This is too late!
const instana = instana(); // TOO LATE!
instana.setLogger(logger);
而是先初始化 Instana,而不需要记录器,然后再需要其他任何内容。 然后设置 Instana 稍后应该使用的记录器(如果需要并初始化了该记录器):
// Correct: Call the initialization function immediately.
// (Pay attention to the extra pair of parantheses at the end of the line.)
const instana = require('@instana/collector')();
// Require and initialize your logging package.
const bunyan = require('bunyan');
// Create your logger(s).
const logger = bunyan.createLogger(...);
// Set the logger Instana should use.
instana.setLogger(logger);
Instana(在初始化过程中)输出的日志的前几行将使用Instana的默认pino记录器进行记录,但 instana.setLogger(logger) 调用后的所有内容都将使用您设置的记录器进行记录。 此外,您的应用程序的日志输出将正确显示在 Instana 的仪表板的“日志消息”选项卡中(请注意,我们仅显示严重性至少为“WARN”的日志调用)。
如果将 Bunyan 或 Pino 记录器传递到setLogger,那么 Node.js 收集器将创建具有相同日志级别和目标流的给定记录器的子代。 只要其他日志记录模块为日志级别debug、info、warn和error提供功能,那么这些模块也受支持。 在此情况下,Node.js 收集器将不会创建子记录器,而只是按当前方式使用给定记录器。
请注意,您负责在您提供给@instana/collector的记录器上设置期望的日志级别。 如果您看到@instana/collector的意外调试日志,请确保在已将级别设置为info或warn的记录器中进行传递。
访问当前活动跨度
Instana 的自动跟踪可以为您处理支持的库的所有内容,无需进行干预。
但是,应用程序代码被授予对收集器的内部状态的有限只读访问权。 出于此目的,可使用instana.currentSpan()获取当前活动范围的句柄。 当前没有任何范围处于活动状态时,此方法将返回哑元句柄。 此方法所返回的范围句柄提供以下方法:
span.getTraceId():返回该范围的跟踪标识。 (自:v1.62.0)
span.getSpanId():返回该范围的范围标识。 (自:v1.62.0)
span.getParentSpanId():返回该范围的父范围标识。 (自:v1.62.0)
span.getName():返回该范围的名称。 (自:v1.62.0)
span.isEntrySpan():确定该范围是否为条目范围(服务器范围)。 (自:v1.62.0)
span.isExitSpan():确定该范围是否为出口范围(客户机范围)。 (自:v1.62.0)
span.isIntermediateSpan():确定该范围是否为中间范围(本地范围)。 (自:v1.62.0)
span.getTimestamp():返回跨度的开始时间戳记(开始时间:v1.62.0)。
span.getDuration():返回该范围的持续时间。 如果该范围尚未完成,那么此方法将返回 0。 请注意,由于 instana.currentSpan() 返回当前活动范围(根据定义,该范围尚未完成),因此几乎始终是这种情况。 仅当已使用 span.disableAutoEnd() 和 span.end() 时,这将返回大于 0 的持续时间 (请参阅以下内容)。 (自:v1.62.0)
span.annotate(path, value):将注释(也称为标记或定制标记)添加到范围中。 path 可以提供为以点分隔的字符串或字符串数组。 即,以下两个调用是等效的:
span.annotate('sdk.custom.tags.myTag', 'My Value')和span.annotate(['sdk', 'custom', 'tags', 'myTag'], 'My Value').span.annotate(['sdk', 'custom', 'tags', 'service'], 'dummy service').
请注意,定制标记应始终以sdk.custom.tags为前缀。 您还可以使用 annotate 覆盖标准标记,例如 HTTP 路径模板(示例:span.annotate('http.path_tpl', '/user/{id}/details')),但建议不要这样做,除非有非常充分的理由干扰 Instana 的自动跟踪。 (自:v1.97.1)
span.markAsErroneous(message, [customErrorMessagePath]): 通过将范围错误计数设置为 1 ,将范围标记为错误。 消息自变量解释了为何将范围视为错误。 如果在不使用参数的情况下调用此方法,那么将自动设置缺省错误消息。 为错误消息提供自定义路径(customErrorMessagePath)是可选的,因为 Node.js Tracer 会自动处理该路径。
示例:
- 将范围标记为错误:
const instana = require('@instana/collector')();
...
const span = instana.currentSpan();
// Mark the current span as erroneous with the default error message.
span.markAsErroneous();
// Mark the current span as erroneous with a custom error message.
span.markAsErroneous('Custom error message');
- 使用自定义路径将 HTTP 跨度标记为错误:
const instana = require('@instana/collector')();
app.get('/example', (req, res) => {
...
const span = instana.currentSpan();
// Mark HTTP span as erroneous with a Custom Error Message and Custom Path
span.markAsErroneous('Custom error message', 'http.error');
...
});
span.markAsNonErroneous([customErrorMessagePath]): 将范围标记为无错误。 如果先前通过自动跟踪检测或调用 span.markAsErroneous 方法将范围标记为错误,那么此方法可能有助于使范围无错误。 如果在调用 span.markAsErroneous 方法时使用了 customErrorMessagePath 参数,请提供 span.markAsNonErroneous 方法的相同路径。 否则,可以省略 customErrorMessagePath 参数。 (自: v2.25.2)
span.getErrorCount(): 返回在处理与当前活动范围关联的请求期间发生的错误数。 如果范围已标记为错误,那么此方法将返回 1 ,否则返回 0。 仅当当前范围是批处理范围时,才会发生大于 1 的错误计数。 (自:v1.62.0)
span.disableAutoEnd():请参阅下一部分。 (自:v1.55.1)
span.end(errorCount):请参阅下一部分。 (自:v1.55.1)
手动结束跨度(报文代理条目)
我们之前提到,Instana 的自动化跟踪可以为您处理支持的库的所有问题,并且无需进行干预。 这条规则有一个例外:从消息代理处消费消息时触发的跟踪操作 ( Kafka, RabbitMQ, NATS, NATS streaming, Google Cloud PubSub )。 由于在使用消息代理的消息时没有响应或回复的概念,因此在特定消息触发的所有操作都已完成时,没有任何事件可以告知 Instana Node.js 收集器(与始终具有关联响应的入局 HTTP 请求相反,该请求会对事务结束进行标定)。
进程接收到新消息时,它将启动条目范围。 Instana 的跟踪功能需要一些表示此范围已完成的事件。 仅当在启动条目范围到完成条目范围之间触发其他调用时,才会将这些调用分配给同一跟踪。 因此,当入局消息的处理完成时,应用程序代码需要告知 Instana。 为此,可以使用当前活动跨度的句柄,该句柄可通过 instana.currentSpan() 获取(参见访问当前活动跨度 )。 该句柄提供了两种与此用例相关的方法:
span.disableAutoEnd():禁用自动完成范围,并将此范围标记为稍后通过调用 span.end() 手动完成的范围。
span.end(errorCount):完成先前调用了 span.disableAutoEnd() 的范围。 errorCount 自变量是可选的。 如果处理消息时发生错误,请传递1。 如果未传递任何内容,那么 errorCount 将缺省为 0。
以下是 RabbitMQ 的示例所示:
channel.consume(queueName, function(msg) {
var span = instana.currentSpan();
span.disableAutoEnd();
// The setTimeout is a placeholder for any number of asynchronous operations
// that are executed when processing this particular message. It could also be
// database access calls or outgoing HTTP calls or really anything else.
setTimeout(function() {
// call span.end when processing of the incoming message has finshed. Make
// sure to also call in case an error happens while processing the message.
span.end();
}, 5000);
});
请注意,如果您调用 span.disableAutoEnd() 但忘记对其调用 span.end(),那么该范围将永远不会发送到后端。
另请注意,对于 Kafka、RabbitMQ 和 NATS 之类的消息代理程序,如果未在消息处理功能中同步调用span.disableAutoEnd(),那么将在消息处理功能返回后立即自动结束并传输范围。 这将破坏该跟踪,即,处理该消息时执行的操作(数据库访问、出局 HTTP 调用、发送其他消息)不会显示为 Instana 中的调用。
发送/发布向消息代理发送消息时,不需要执行上述任何操作。
在没有活动条目跨度的情况下启用跟踪功能
对于 Instana 收集器4.0及更高版本,即使没有入口跨度,也可以捕捉出口跨度。 要启用此功能,请设置 "INSTANA_ALLOW_ROOT_EXIT_SPAN环境变量或通过跟踪器配置进行配置。 有关如何配置此功能的更多信息,请参阅允许根退出跨距而不进入跨距。
对于 Instana 收集器4.0 之前的版本,InstanaNode.js收集器只在有活动入口跨度时捕获出口或中间跨度。 有关更多信息,请参阅 "当没有活动入口跨度时,跟踪处于非活动状态"。
使用 SDK 手动创建跨度
收集器会自动记录广泛使用的 API,以便进行开箱即用的跟踪,但在某些情况下,您可能需要更多的深入了解。 例如:
自定义库或框架:您可以使用 SDK 来捕获应用程序中未自动仪器化的区域的跨度,从而提供可能被忽略的洞察力。
中间跨度:您可以创建跨度来明确划分代码中的重要部分,以便更深入地跟踪特定操作。
内部触发的工作:对于自行启动工作的应用程序(如由 "
setInterval、"setTimeout或类似方法触发的计划作业),您可以使用 SDK 启用对该活动的跟踪,即使没有来自外部的传入请求。
使用 SDK 创建的范围与 Instana 的自动跟踪功能无缝集成。
术语
SDK 提供了用于创建条目范围、中间范围和出口范围的功能。 简言之:
- 条目范围表示正在监视的应用程序调用到。 这些调用可以是应用程序从其他服务接收的 HTTP 请求,也可以是应用程序从队列中获取的信息。 (当然,HTTP 请求已包含在自动跟踪中。) 通常,条目范围表示在 Node.js 应用程序内触发处理的内容。 跟踪总是需要从入口跨度开始(通过自动仪器或 SDK 创建)。 Node.js 跟踪器保持非活动状态,不会捕获中间或退出跨度,除非活动入口跨度退出。 更多信息,请参阅 "在没有活动入口跨度的情况下,跟踪处于非活动状态"一节。
- 出口范围表示应用程序进行的调用。 这些可能是应用程序制造或数据库调用的 HTTP 请求(并由其他服务进行响应)。 (同样,自动检测已经涵盖了出局 HTTP 请求和许多常用数据库。)
- 中间跨度是指监视的应用程序内部所发生的情况,即,它们不会像进入跨度和退出跨度那样进入或离开流程。 如果您想要提供自动检测未提供的其他属性(称为标签),那么还可以使用中间跨度自动合并创建的跨度。
有关术语的更多详细信息,尤其是跨度与您将在 Instana UI 中看到的调用的关系,请参阅我们的跟踪文档。
在开始使用 SDK 实现定制跟踪之前,还有一个关于跟踪最佳实践的章节值得阅读。
最后还有一个演示应用程序,您可以在本地启动并检查以帮助您开始使用 Instana Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk。
基于回调的应用程序接口、基于承诺的应用程序接口和异步应用程序接口
SDK 提供了三种不同类型的 API ,一种基于回调 (instana.sdk.callback) ,一种基于 promise (instana.sdk.promise) ,一种用于使用 async/await 样式 (instana.sdk.async) 的代码。 哪一个用的纯粹是味道的问题。 基本上,所有三个 API 都提供了各种方法来启动跨度和完成跨度。 您需要直接启动该跨度,然后再执行您希望该跨度表示的工作,并在该工作完成后完成该跨度。
使用回调 API 时,您需要在启动新范围时传递回调,并在该回调内执行与该范围关联的所有工作(或者在该回调过渡触发的任何异步操作的回调中)。 以下是一个示例:
instana.sdk.callback.startEntrySpan('my-custom-span', () => {
// The actual work needs to happen inside this callback (or in the callback
// of any asynchronous operation transitively triggered from this callback).
...
doSomethingAsynchronous((err, result) => {
if (err) {
instana.sdk.callback.completeEntrySpan(err);
logger.error(err);
return;
}
instana.sdk.callback.completeEntrySpan();
logger.info('Yay! 🎉', result);
});
});
请注意,将立即以同步方式调用提供给 startXxxSpan 的回调,而不使用自变量。 可以在该回调内触发其他异步操作。
使用 promise API 时,所有启动新范围的方法都将返回 promise。 与该范围关联的所有工作都需要发生在该 promise 链中(即,在 startXxxSpan 返回的 promise 的 then 中,或者在 promise 链下的任何 then 处理程序中)。 以下是一个示例:
instana.sdk.promise.startEntrySpan('my-custom-span').then(() => {
// The actual work needs to happen inside the promise chain, that is, either
// here or in any `then` handler further down the promise chain.
return anotherPromise();
}).then(result => {
instana.sdk.promise.completeEntrySpan();
logger.info('Yay! 🎉', result);
}).catch(err => {
instana.sdk.promise.completeEntrySpan(err);
logger.error(err);
});
请注意,从不拒绝 startXxxSpan 返回的 promise,它将在没有值的情况下立即解析。 可以在其 then 处理程序内触发其他异步操作。
对于 async/await 样式代码,SDK 提供 instana.sdk.async(从 V1.81.0 开始):
await instana.sdk.async.startExitSpan('my-custom-span');
try {
await someOtherAsynchronousOperation();
instana.sdk.async.completeExitSpan();
} catch (err) {
instana.sdk.async.completeExitSpan(err);
logger.error(err);
}
在后台,instana.sdk.async只是instana.sdk.promise的别名,因为 async/await 代码是通过 Node.js 运行时中的 Promise 处理的。
对于所有三个 API 类型模板,completeXxxSpan方法都相同。
如果前面的示例还不够,请务必跳转到我们的完整、可运行的演示应用程序,以展示 Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk.
API 方法
SDK 的方法可接受以下公共参数:
name:该范围的名称。 启动新范围时,此参数是必需的。 它应该是一个简短的、不言自明的字符串。tags:此范围的其他元数据的可选 JS 对象。 您可以在开始范围或完成范围时提供标记,或者同时提供这两者。 如果在开始和完成范围时提供标记,那么将合并这两个对象。 这些标记将显示在 Instana UI 中。 您需要确保不会将任意大对象添加到您创建的范围中。 应该使用短键值对。 如果范围变得太大,那么可能会删除部分范围,而不会将其发送到 Instana 代理程序。error:当执行与当前范围关联的工作时发生错误时,可以在完成该范围时将此错误附加到该范围。traceId:这仅与条目范围相关,并且用于使条目范围成为已在另一个过程中启动的现有跟踪的一部分。 如果您提供了traceId,那么还需要提供parentSpanId。parentSpanId:这仅与作为已在另一个过程中启动的现有跟踪的一部分的条目范围相关。 它用于引用触发了此条目范围的出口范围。 如果您提供了parentSpanId,那么还需要提供traceId。
所有三个 API 都提供了以下方法:
instana.sdk.callback.startEntrySpan(name [, tags[, traceId, parentSpanId]], callback),
instana.sdk.{promise|async}.startEntrySpan(name [, tags[, traceId, parentSpanId]]):
启动条目范围。 您需要为该范围提供name。 您可以选择提供tags对象。traceId和parentSpanId也是可选的,但您需要同时提供这两个标识或根本不提供任何标识。instana.sdk.callback.completeEntrySpan([error, tags])或
instana.sdk.{promise|async}.completeEntrySpan([error, tags]):
完成条目范围。 可能提供了错误和其他标记。 如果要提供其他标记但没有错误,请传递null作为第一个参数。instana.sdk.callback.startIntermediateSpan(name[, tags], callback),
instana.sdk.{promise|async}.startIntermediateSpan(name[, tags]):
启动中间范围。 您需要为该范围提供name,并且您可以选择提供tags对象。 该函数调用将返回已启动的范围。instana.sdk.callback.completeIntermediateSpan([error, tags, span])或
instana.sdk.{promise|async}.completeIntermediateSpan([error, tags, span]):
完成中间范围。 可以提供错误、附加标签和要完成的范围。 如果要提供其他标记但没有错误,请传递null作为第一个参数。instana.sdk.callback.startExitSpan(name[, tags], callback)
,instana.sdk.{promise|async}.startExitSpan(name[, tags]):
启动出口范围。 您需要为该范围提供name,并且可以选择性地提供tags对象。instana.sdk.callback.completeExitSpan([error, tags])
,instana.sdk.{promise|async}.completeExitSpan([error, tags]):
完成出口范围。 可能提供了错误和其他标记。 如果要提供其他标记但没有错误,请传递null作为第一个参数。instana.sdk.callback.bindEmitter(emitter),
instana.sdk.{promise|async}.bindEmitter(emitter):
请参阅 ,如下。
请注意,使用任何 startXxxSpan 方法启动的范围仅在调用相应 completeXxxSpan 后才会传输到 Instana。 此外,对于嵌套跨度,调用顺序必须正确。
使用 Promise API 嵌套范围时也必须小心。 以下是正确的:
instana.sdk.promise.startEntrySpan('custom-entry')
// any number of other promises/async operations
.then(() => {
...
})
.then(() => {
return instana.sdk.promise.startExitSpan('custom-exit')
// any number of other promises/async operations associated with the exit span
.then(() => {
...
})
.then(() => {
// Important: The exit span needs to be completed in the promise chain
// started with startExitSpan, not in the outer promise chain started
// with startEntrySpan.
instana.sdk.promise.completeExitSpan();
});
})
.then(() => {
instana.sdk.promise.completeEntrySpan();
logger.info('Yay! 🎉');
});
请参阅以下中间范围的有效示例:
await instana.sdk.async.startIntermediateSpan('intermediate-span-name')
// trigger an internal request
await request(`http://127.0.0.1:${port}`)
instana.sdk.async.completeIntermediateSpan();
// This example demonstrates how to start two overlaping intermediate spans.
const span1 = await instana.sdk.async.startIntermediateSpan('intermediate-span-name-1')
await request(`http://127.0.0.1:${port}`)
const span2 = await instana.sdk.async.startIntermediateSpan('intermediate-span-name-2')
await request(`http://127.0.0.1:${port}`)
// Pass "span1" as third argument to signalise which span to complete.
instana.sdk.async.completeIntermediateSpan(null, null, span1);
await request(`http://127.0.0.1:${port}`)
instana.sdk.async.completeIntermediateSpan(null, null, span2);
此时,您可能想知道为什么 SDK 的 API 是这样设计的,尤其是 startXxxSpan 方法接受回调/返回承诺的业务可能看起来很尴尬。 重点在于,我们需要在跟踪时保留异步上下文。 由于 Node.js 是单线程的,并且使用回调执行异步操作,因此 Node.js 收集器需要一种方法确定哪些操作属于回调或 promise 中的所跟踪操作的哪个范围。
处理事件发射器
如果与定制 SDK 范围关联的工作涉及到事件发射器,并且如果范围内运行的代码侦听已发出的事件,那么需要绑定事件发射器,否则您的跟踪代码将不会按预期运行。 以下是如何执行操作:
instana.sdk.callback.startEntrySpan('custom-span', () => {
const emitter = ... // some event emitter
instana.sdk.callback.bindEmitter(emitter);
...
emitter.on('some-event', () => {
instana.sdk.callback.completeEntrySpan();
logger.info('Done! 🎉');
});
});
在没有活动条目跨度的情况下,跟踪处于非活动状态
通常,仅当存在活动条目范围时,Instana 跟踪程序才会捕获出口或中间范围。 这是不捕获与处理您的业务相关事务无关的噪音的重要保障。 但是,在一些边缘案例中,这种保护措施可以获得效果。 包括由 Instana 的 自动检测不支持的机制触发的工作在内的示例:
- 这可能是消息传递库的入局消息,但我们(尚未)支持。
- 或者它可能是通过不受支持的协议发出的请求,例如原始 TCP 消息或 websocket 通信(这两个消息都不受自动检测的支持)。
- 另一个示例是运行调度作业的应用程序,这些作业不是从外部触发的,而是由进程本身触发的(例如,使用
setTimeout、setInterval或不受支持的 Node.js 调度库)。
没有任何输入范围的结果是,Instana Node.js 跟踪程序也不会捕获在此上下文中进行的调用的任何其他中间或出口范围。 即:
- 如果通过受支持库触发了出局 HTTP 请求,数据库调用或任何其他将创建出口范围的操作,但没有活动条目范围,那么将不会捕获该出口范围。
- 如果使用 Instana Node.js SDK 启动中间或出口范围,但不存在活动的条目范围,那么 SDK 将拒绝启动该范围。 将记录有关此问题的警告。
不过,现在已经有了解决这种情况的办法。 对于 Instana 收集器4.0及更高版本,即使没有入口跨度,也可以捕捉出口跨度。 要启用此功能,请参阅允许根退出跨度而不进入跨度中的说明。 对于 Instana 收集器 "4.0之前的版本,您需要在任务开始时手动输入 "开始进入跨度,并在任务结束时输入 "完成。 有效入口跨度的存在可确保捕捉到所有出口跨度和中间跨度。
您还可以创建功能部件请求,为您正在使用的触发器类型实现现成可用的支持。
手动恢复异步上下文
Instana 在内部依赖于 async_hooks 保留异步上下文。 这将自动且透明地工作,而无需手动干预。 但是,有一些库和代码模式可能会破坏 async_hooks 的连续性,因此异步上下文会丢失。 其中一些是在此问题列表中列出的。 使用此类模块可能会导致不完整的跟踪和缺少调用。
Instana 的 Node.js SDK 提供了用于纠正此问题的 API。 请注意,几乎从不需要使用此 API。 仅当您确切知道哪个代码行导致缺少调用以及原因时,才应该使用此选项。
instana.sdk.getAsyncContext():返回当前活动的异步上下文。 在调用中断异步连续性的代码之前,请先调用此代码。 (自: v1.100.0)instana.sdk.runInAsyncContext(context, callback):在提供的异步上下文中运行给定的callback。 应该已通过调用instana.sdk.getAsyncContext()获取context对象。 将应该在该上下文中运行的所有代码合并到callback中。 (自: v1.100.0)instana.sdk.runPromiseInAsyncContext(context, createPromiseFn):在提供的异步上下文中运行 promise。 应该已通过调用instana.sdk.getAsyncContext()获取context对象。 函数createPromiseFn应该会返回您要在该上下文中运行的承诺。 (自: v1.100.0)
以下是 runInAsyncContext 的一些示例代码以演示其用法:
// ...
// 1. Fetch the currently active asynchronous context directly _before_ the
// asynchronous operation that breaks async_hooks continuity.
const activeContext = instana.sdk.getAsyncContext();
someLibrary.functionThatBreaksAsyncContinuity((error, result) => {
// 2. Restore the asynchronous context directly _after_ the asynchronous
// operation that breaks async_hooks/async_wrap continuity by calling
// instana.sdk.runInAsyncContext with the context object acquired
// earlier.
instana.sdk.runInAsyncContext(activeContext, () => {
// 3. Wrap all subsequent code in the callback given to instana.sdk.runInAsyncContext.
if (error) {
// ...
}
// ...
});
});
以下是runPromiseInAsyncContext的一些示例代码:
// ...
// 1. Fetch the currently active asynchronous context directly _before_ the
// asynchronous operation that breaks async_hooks continuity.
const activeContext = instana.sdk.getAsyncContext();
someLibrary.functionThatBreaksAsyncContinuity((error, result) => {
// 2. Restore the asynchronous context directly _after_ the asynchronous
// operation that breaks async_hooks/async_wrap continuity by calling
// instana.sdk.runInAsyncContext with the context object acquired
// earlier.
return instana.sdk.runPromiseInAsyncContext(activeContext, () => {
/* a function that returns the promise you want to run */}
);
});
OpenTracing 集成
此包还实现 OpenTracing API。 要将 OpenTracing for Node.js 与 Instana 配合使用,您应该禁用自动跟踪并使用 Instana OpenTracing API 实现。 以下示例项目对此进行了演示:
// Always initialize the collector as the first module inside the application.
const instana = require('@instana/collector')({
tracing: {
automaticTracingEnabled: false
}
});
// instantiate the OpenTracing tracer:
const opentracing = require('opentracing');
// optionally use the opentracing provided singleton tracer wrapper
opentracing.initGlobalTracer(instana.opentracing.createTracer());
// retrieve the tracer instance from the opentracing tracer wrapper
const tracer = opentracing.globalTracer();
// start a new trace with an operation name
const span = tracer.startSpan('auth');
// mark operation as failed
span.setTag(opentracing.Tags.ERROR, true);
// finish the span and schedule it for transmission to instana
span.finish();
限制
- OpenTracing 未与 Instana 的自动跟踪集成。 尤其是,使用 OpenTracing API 创建的范围将不会与我们的自动跟踪检测所创建的范围属于相同的跟踪。 如果要将附加范围添加到自动创建的范围,那么您应该首选 SDK 而不是 OpenTracing。 实际上,我们建议在一个应用程序中使用自动跟踪(可选择由 SDK 范围扩充)或 OpenTracing,但不能同时使用这两种功能。
- Instana Node.js 收集器不支持 OpenTracing 二进制承运方。 此 OpenTracing 实现将静默忽略 OpenTracing 二进制承运方对象。
- 还应谨慎使用 OpenTracing 行李物品。 行李项是通过跨网络边界的承运方对象运输的元数据。 此外,此元数据由子级跨度(及其子级跨度...)继承. 这可能会产生一些开销。 我们建议完全避免使用 OpenTracing 行李 API。