Instana Node.js API

起始設定 Instana Node.js 收集器

安裝頁面中所述, @instana/collector 的主要匯出是起始設定 Instana Node.js 收集器的函數。 也就是說,當您使用 requireimport來載入模組時,它會傳回在需要或匯入任何其他模組之前,您的應用程式碼需要呼叫的函數。 下列各節針對使用 CommonJS 模組系統或 ESCMAScript 模組系統的 Node.js 應用程式,更詳細地說明這一點。

CommonJS

您只需要呼叫 @instana/collector 所匯出的起始設定函數一次,但您必須在 Node.js 應用程式的開頭,在任何其他 require 陳述式之前,以及在執行任何其他程式碼之前,這樣做:

require('@instana/collector')();

起始設定函數會傳回本身的參照,如果您想要存取 Instana Node.js 收集器所提供的其他匯出項目,這會相關。 也就是說,下列兩個 Snippet 是相等的:

const instana = require('@instana/collector')();

與下列相同:

const instana = require('@instana/collector');
instana();

起始設定函數接受一個選用參數,即配置物件:

const instana = require('@instana/collector')({
  // ... configuration object, see configuration documentation
});

如需配置物件的詳細資料,請參閱 配置頁面

如需安裝及起始設定 Instana Node.js 收集器的詳細資料,請參閱 安裝一般陷阱 小節。

ESCMAScript 模組

對於使用 ECMAScript 模組系統的 Node.js 應用程式,起始設定 Instana Node.js 收集器的建議方法是 實驗性載入器旗標。 如果您想要在以 Node.js 應用程式為基礎的 ECMAScript 模組中使用 Instana Node.js SDK ,則可以取得 instana API 的參照,如下所示:

import instana from '@instana/collector';

您不需要再次起始設定 Instana Node.js 收集器,因為它由啟動時提供的 --experimental-loader 旗標來處理。

使用多個 Instana 收集器套件

Kubernetes

當您使用 Instana AutoTrace WebHook時, 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 API

幾乎所有應用程式都只需要如上所述起始設定 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.sdk.callback.startEntrySpan('custom-span', () => { ... });

// or:
instana.opentracing.createTracer();

// ...

附註: 如果您需要在多個檔案中參照 @instana/collector ,則不需要再次起始設定 @instana/collector 。 除了應用程式主要模組和應用程式進入點中的檔案之外,您可以在所有檔案中使用 const instana = require('@instana/collector'); ,而不使用 const instana = require('@instana/collector')();

在起始設定之後設定日誌程式

使用 instana.setLogger(logger) 將自訂日誌程式提供給 @instana/collector ,而非預設 bunyan 日誌程式。

如前所述,在需要/匯入任何其他套件之前,您需要立即呼叫起始設定函數 (由 require('@instana/collector')傳回) ,否則 Instana 的自動追蹤只會部分運作。 特別是,這需要您先起始設定 Instana ,然後再要求您的記載套件 (例如, bunyanpinowinston)。 如果您在起始設定 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 日誌程式時,設定 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 的預設 bunyan 日誌程式來記載,但 instana.setLogger(logger) 呼叫之後的所有內容都將使用您已設定的日誌程式來記載。 此外,您應用程式的日誌輸出會正確地顯示在 Instana 儀表板的「日誌訊息」標籤中 (請注意,我們只會顯示嚴重性至少為 "WARN" 的日誌呼叫)。

如果您將 Bunyan 或 Pino 日誌程式傳遞至 setLogger,則 Node.js 收集器會建立具有相同記載層次及目標串流之給定日誌程式的子項。 也支援其他記載模組,只要它們提供 debuginfowarnerror記載層次的功能即可。 在此情況下, Node.js 收集器不會建立子項日誌程式,只會依現狀使用給定的日誌程式。

請注意,您負責在提供給 @instana/collector的日誌程式上設定想要的記載層次。 如果您從 @instana/collector看到非預期的除錯日誌,請確定您傳入層次已設為 infowarn的日誌程式。

存取目前作用中的 Span

Instana 的自動追蹤會為您處理 受支援程式庫的所有項目,不需要干擾。

不過,應用程式碼會被授與對收集器內部狀態的有限唯讀存取權。 基於此目的,可以使用 instana.currentSpan()來獲得目前作用中跨距的控點。 當目前沒有跨距處於作用中狀態時,此方法將傳回虛擬控點。 此方法傳回的跨距控點提供下列方法:

span.getTraceId(): 傳回跨距的追蹤 ID。 (從 v1.62.0開始)

span.getSpanId(): 傳回跨距的跨距 ID。 (從 v1.62.0開始)

span.getParentSpanId(): 傳回跨距的母項跨距 ID。 (從 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').

請注意,自訂標籤應該一律以 sdk.custom.tags作為字首。 您也可以使用 annotate 來置換標準標籤,例如 HTTP 路徑範本 (例如: span.annotate('http.path_tpl', '/user/{id}/details')) ,但不建議這樣做,除非有非常好的理由干擾 Instana 的自動追蹤。 (從 v1.97.1開始)

span.markAsErroneous(message, [customErrorMessagePath]): 將跨距標示為錯誤,即將跨距的錯誤計數設為 1。 您應該提供訊息來說明為何此跨距作為第一個引數是錯誤的。 訊息將新增為註釋。 如果在沒有引數的情況下呼叫 markAsErroneous ,則會設定預設訊息。 您可以提供錯誤訊息註釋的自訂路徑,但在幾乎所有情況下,最好讓 Instana Node.js SDK 自行處理。 如果提供自訂路徑,則可以將其提供為字串或陣列 (請參閱 span.annotate) ,且它應該以 sdk.custom.tags開頭。 (從 v2.25.2開始)

span.markAsNonErroneous([customErrorMessagePath]): 將跨距標示為不錯誤。 如果跨距已由自動追蹤設備測試或由 span.markAsErroneous 標示為錯誤,且需要回復此決策,則這會很有用。 如果 span.markAsErroneous 已與 customErrorMessagePath 一起使用,請提供 span.markAsNonErroneous的相同路徑,否則請省略此參數。 (從 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 串流、 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() ,則會在訊息處理函數傳回之後 立即 結束並自動傳輸跨距。 這會岔斷該追蹤,亦即,在處理該訊息時所執行的作業 (DB 存取、送出 HTTP 呼叫、傳送其他訊息) 將不會顯示為 Instana 中的呼叫。

傳送/發佈 訊息至訊息分配管理系統時,不需要執行任何動作。

使用 SDK 手動建立跨距

收集器會自動指示廣泛使用的 API ,以新增直接現成可用的追蹤支援。 有時你會發現這還不夠。 SDK 可用來提供應用程式區域的見解,例如自訂程式庫及架構,否則會被忽略。 基於此目的, SDK 可讓您手動建立跨距。 另一個使用案例是建立 中間 文字段,以區分程式碼中感興趣的區段。 最後, SDK 可用來在應用程式中啟用追蹤,這些應用程式不會接收來自外部的要求,但會自行開始工作,例如 setIntervalsetTimeout 或類似方法所觸發的排定工作 (如果這是您的使用案例,請參閱 如下所示 )。

使用 SDK 建立的跨距與 Instana 的自動追蹤功能無縫整合。

術語

SDK 提供功能來建立進入跨距、中間跨距及結束跨距。 簡而言之:

  • 輸入文字段代表 進入 受監視應用程式的呼叫。 這些可能是應用程式 從其他服務接收 的 HTTP 要求,或應用程式從佇列中挑選的訊息。 (當然,自動追蹤已涵蓋 HTTP 要求。) 一般而言,項目跨距代表在 Node.js 應用程式內觸發處理的項目。 追蹤一律需要從項目跨距開始 (由自動檢測或透過 SDK 建立)。 Node.js 追蹤程式會保持非作用中,除非有作用中的進入跨距,否則不會擷取任何中間或結束跨距。 如需相關詳細資料,請參閱 當沒有作用中項目跨距時,追蹤處於非作用中狀態 一節。
  • 結束跨距代表應用程式所進行的呼叫。 這些可能是應用程式 發出 (並由其他服務回應) 或資料庫呼叫的 HTTP 要求。 (同樣地,自動檢測已涵蓋送出的 HTTP 要求及許多常用資料庫。)
  • 中間跨距是 受監視應用程式內發生的事情,亦即,它們既未進入也未離開處理程序,如進入跨距及結束跨距所執行。 如果您想要提供自動化設備測試未提供的其他屬性 (稱為 標籤) ,也可以使用中間文字段來包裝自動建立的文字段。

如需專有名詞的詳細資料,特別是 跨距 如何與您將在 Instana 使用者介面中看到的 呼叫 相關,請參閱我們的 追蹤文件

在開始使用 SDK 實作自訂追蹤之前,還有一個關於 追蹤最佳作法 的章節值得閱讀。

最後,您可以在本端啟動示範應用程式,並檢查以協助您開始使用 Instana Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk

回呼型 API、Promise 型 API 及非同步 API

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。 與該跨距相關聯的所有工作都需要在該承諾鏈中進行 (亦即,在 startXxxSpan 所傳回之承諾的 then 中,或在該承諾鏈更往下的任何 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 所傳回的承諾從未遭到拒絕,它會在沒有值的情況下立即解決。 其他非同步作業可以在其 then 處理程式內觸發。

對於非同步/等待樣式程式碼, SDK 提供 instana.sdk.async (從 1.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);
}

在 hood 下, instana.sdk.async 只是 instana.sdk.promise的別名,因為 async/await 程式碼是透過 Node.js 運行環境中的 Promises 來處理。

completeXxxSpan 方法對於所有三個 API 內容都是相同的。

如果上述範例不夠,請確保跳至我們完全成熟的工作展示應用程式,展示 Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk

API 方法

SDK 方法接受下列 一般參數 :

  • name: 跨距的名稱。 當啟動新的文字段時,此參數是必要的。 它應該是一個簡短且易於說明的字串。
  • tags: 包含跨距的其他 meta 資料的選用 JS 物件。 您可以在開始跨距時及/或完成跨距時提供標籤。 如果您在開始及完成跨距時提供標籤,則會合併這兩個物件。 標籤將顯示在 Instana 使用者介面中。 您需要確保不要將任意大型物件新增至您建立的文字段。 應該使用短鍵值組。 如果跨距變得太大,可能會捨棄一批跨距,而不是傳送至 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 物件。 traceIdparentSpanId 也是選用項目,但您需要同時提供兩個 ID 或完全不提供 ID。
  • 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。 此外,對於巢狀跨距,呼叫必須以正確的順序進行。

為了說明這一點,請考量下列兩個範例。 以下是有效的:

const sdk = instana.sdk.callback;

sdk.startEntrySpan('my-custom-entry', () => {
  doSomethingAsynchronous(() => {
    sdk.startExitSpan('my-custom-exit', () => {
      doAnotherThingAsynchronous(() => {
        sdk.completeExitSpan();
        sdk.completeEntrySpan();
        logger.info('Yay! 🎉');
      });
    });
  });
});

但這不是有效的:

const sdk = instana.sdk.callback;

sdk.startEntrySpan('my-custom-entry', () => {
  doSomethingAsynchronous(() => {
    sdk.startExitSpan('my-custom-exit', () => {
      doAnotherThingAsynchronous(() => {
        // WRONG ORDER - you first need to complete the span you started last.
        // Think of the spans as stack.
        sdk.completeEntrySpan();
        sdk.completeExitSpan();
        logger.info('Yay! 🎉');
      });
    });
  });
});

使用 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! 🎉');
  });

但這不會成功:

instana.sdk.promise.startEntrySpan('custom-entry')
.then(() => {
  return instana.sdk.promise.startExitSpan('custom-exit');
})
.then(() => {
  // WRONG The currently active span in this context is the *entry* span, not
  // the exit span, so it is not possible to complete the exit span here.
  instana.sdk.promise.completeExitSpan();
  instana.sdk.promise.completeEntrySpan();
});

請參閱下列中間跨距的有效範例:

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 收集器需要一種方法來判斷哪些作業屬於回呼中所追蹤動作的跨距折返,或承諾會使其成為可能。

處理事件發射程式

如果與自訂 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 通訊 (這兩者不受自動設備測試支援)。
  • 另一個範例是執行排定工作的應用程式,這些排定工作不是從外部觸發,而是由處理程序本身觸發 (例如,使用 setTimeoutsetInterval 或不受支援的 Node.js 排程程式庫)。

沒有任何進入跨距的結果是 Instana Node.js 追蹤器也不會針對在此環境定義中進行的呼叫擷取任何其他中間或結束跨距。 亦即:

  • 如果透過 支援的程式庫觸發送出的 HTTP 要求、資料庫呼叫或會建立結束跨距的任何其他項目,但沒有作用中的進入跨距,則不會擷取該結束跨距。
  • 如果使用 Instana Node.js SDK 來啟動中間或結束跨距,但沒有作用中的進入跨距,則 SDK 將拒絕以啟動該跨距。 將會記載此相關的警告。

這種狀況的解決方案是在工作片段開始時手動 開始進入跨距 ,並在完成時 完成它 。 這樣,將會擷取所有結束及中間跨距,因為有作用中的進入跨距。

您也可以 建立特性要求 ,以針對您正在使用的觸發程式類型實作現成可用的支援。

手動還原非同步環境定義

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): 在提供的非同步環境定義中執行承諾。 呼叫 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 行李物品也要小心。 行李物品是跨網路界限透過承運方物件傳輸的 meta 資料。 此外,此 meta 資料由子項跨距 (及其子項跨距 ...) 繼承。. 這可能會產生一些額外負擔。 建議完全避免使用 OpenTracing 行李 API。

另請參閱