API Instana Node.js

Instana-Kollektor Node.js initialisieren

Wie auf der Installationsseiteerläutert, ist der Hauptexport von @instana/collector eine Funktion, die den Instana-Kollektor Node.js initialisiert. Das heißt, wenn Sie das Modul mithilfe von require oder importladen, gibt es eine Funktion zurück, die von Ihrem Anwendungscode aufgerufen werden muss, bevor andere Module benötigt oder importiert werden. In den folgenden Abschnitten wird dies detaillierter für Node.js -Anwendungen erläutert, die entweder das CommonJS -Modulsystem oder das ESCMAScript -Modulsystem verwenden.

CommonJS

Sie müssen die Initialisierungsfunktion, die von @instana/collector exportiert wird, nur einmal aufrufen, aber Sie müssen dies am Anfang Ihrer Node.js -Anwendung tun, bevor andere require -Anweisungen ausgeführt werden und bevor weiterer Code ausgeführt wird:

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

Die Initialisierungsfunktion gibt einen Verweis auf sich selbst zurück, was relevant ist, wenn Sie auf andere Exporte zugreifen möchten, die von dem Instana-Node.js-Collector bereitgestellt werden. Das heißt, die folgenden zwei Snippets sind äquivalent:

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

ist identisch mit:

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

Die Initialisierungsfunktion akzeptiert einen optionalen Parameter, ein Konfigurationsobjekt:

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

Details zum Konfigurationsobjekt finden Sie auf der Konfigurationsseite .

Weitere Details zur Installation und Initialisierung des Instana-Kollektors Node.js finden Sie in den Abschnitten Installation und allgemeine Probleme .

ESCMAScript-Module

Für Node.js -Anwendungen, die das ECMAScript-Modulsystem verwenden, initialisieren Sie den Instana-Kollektor Node.js über die Node.js -Befehlszeilenflags. Wenn Sie Instana Node.js SDK in Ihrer ECMAScript-modulbasierten Node.js -Anwendung verwenden möchten, können Sie wie folgt einen Verweis auf die instana -API abrufen:

import instana from '@instana/collector';

Sie müssen den Instana-Kollektor Node.js nicht erneut initialisieren, da der Kollektor durch die Node.js -Befehlszeilenflags --import oder --experimental-loader initialisiert wird, die während des Starts bereitgestellt werden.

Mehrere Instana-Kollektorpakete verwenden

Kubernetes

Wenn Sie Instana AutoTrace WebHookverwenden, wird der Kollektor Node.js automatisch im Verzeichnis /opt/instana/instrumentation/nodejs/node_modules/@instana/collector installiert.

Wenn Sie das Instana Node.js SDK in Ihrer Kubernetes-basierten Node.js -Anwendung verwenden möchten, wird empfohlen, @instana/collector als direkte Abhängigkeit in Ihrem Projekt zu installieren.

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

Die Initialisierung des Instana-Kollektors Node.js dauert einige Zeit. Wenn Sie sofort mit der Erstellung von Bereichen mithilfe des SDK beginnen möchten, fügen Sie eine kurze Verzögerung (z. B. 500 ms) hinzu, um sicherzustellen, dass der Instana-Kollektor Node.js bereit ist und eine Verbindung zum Instana-Hostagenten hergestellt hat.

Auf Instana-API zugreifen

Fast alle Anwendungen müssen Instana nur wie oben dargestellt initialisieren. Es gibt jedoch ein paar Fälle, in denen Sie möglicherweise auf andere Teile der Instana-API zugreifen müssen.

// 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();

// ...

Wenn Sie einen Verweis auf @instana/collector in mehreren Dateien benötigen, müssen Sie @instana/collector nicht erneut initialisieren. Sie können const instana = require('@instana/collector'); anstelle von const instana = require('@instana/collector')(); in allen Dateien außer den Dateien im Hauptmodul und Anwendungseingangspunkt Ihrer Anwendung verwenden.

Festlegen der Protokollfunktion nach der Initialisierung

Verwenden Sie instana.setLogger(logger), um eine angepasste Protokollfunktion für @instana/collector anstelle der bunyan-Standardprotokollfunktion bereitzustellen.

Wie bereits erwähnt, müssen Sie die Initialisierungsfunktion aufrufen (die von require('@instana/collector')) sofort zurückgegeben wird, bevor andere Pakete erforderlich sind. Andernfalls funktioniert das automatische Tracing von Instana nur teilweise. Vor allem müssen Sie Instana initialisieren, bevor Sie Ihr Protokollierungspaket benötigen (z. B. bunyan, pino oder winston). Wenn das Protokollierungspaket vor der Initialisierung von Instana erforderlich ist, werden Ihnen Protokollnachrichten in Instana nicht angezeigt.

Andererseits möchten Sie Ihre eigene Protokollfunktion möglicherweise an den Node.js-Collector übergeben, damit die Protokollnachrichten dasselbe Format haben und in dieselben Protokolldateien/Ziele geschrieben werden wie die übrigen Protokollnachrichten der Anwendung. Wenn Sie Ihre Protokollfunktion an die Initialisierungsfunktion vom Collector übergeben würden, würden Sie dieses Protokollpaket vor der Initialisierung von Instana benötigen. Um diese zyklische Abhängigkeit zu beheben, bietet @instana/collector die Funktion setLogger an, um Instana zunächst ohne eine angepasste Protokollfunktion zu initialisieren und die Protokollfunktion dann später festzulegen.

Folgendes wird beispielsweise nicht unterstützt:

// 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);

Initialisieren Sie stattdessen Instana zuerst ohne eine Protokollfunktion, bevor Sie etwas anderes benötigen. Legen Sie dann die Protokollfunktion fest, die Instana später verwenden soll, wenn die Protokollfunktion angefordert und initialisiert wurde:

// 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);

Die ersten wenigen Zeilen der Protokollausgabe von Instana (während der Initialisierungsprozedur) werden mit der Standard-Bunyan-Protokollfunktion von Instana protokolliert, aber alles nach dem instana.setLogger(logger)-Aufruf wird mit der von Ihnen festgelegten Protokollfunktion protokolliert. Außerdem wird die Protokollausgabe Ihrer Anwendung auf der Registerkarte "Protokollnachrichten" in den Dashboards von Instana korrekt angezeigt (beachten Sie, dass wir nur Protokollaufrufe zeigen, für die der Schweregrad mindestens "WARN" ist).

Wenn Sie eine Bunyan- oder Pino-Protokollfunktion an setLogger übergeben, erstellt der Node.js-Collector untergeordnete Elemente der angegebenen Protokollfunktion mit der gleichen Protokollebene und den gleichen Zieldatenströmen. Andere Protokollierungsmodule werden ebenfalls unterstützt, vorausgesetzt, sie stellen Funktionen für die Protokollebenen debug, info, warn und error bereit. In diesem Fall erstellt der Node.js-Collector keine untergeordneten Protokollfunktionen, sondern verwendet einfach die angegebene Protokollfunktion.

Beachten Sie, dass Sie für das Festlegen der gewünschten Protokollebene für die Protokollfunktion verantwortlich sind, die Sie für @instana/collector bereitstellen. Wenn unerwartete Debugprotokolle von @instana/collector angezeigt werden, müssen Sie sicherstellen, dass Daten in einer Protokollfunktion übergeben werden, deren Ebene auf info oder warn gesetzt wurde.

Auf Die Momentan Aktive Spanne Zugreifen

Das automatisierte Tracing von Instana verarbeitet alles für Sie für unterstützte Bibliotheken, es besteht keine Notwendigkeit, sich zu stören.

Dennoch wird dem Anwendungscode begrenzter Lesezugriff auf den internen Collector-Zustand erteilt. Zu diesem Zweck kann mit instana.currentSpan() eine Kennung für die gerade aktive Spanne angefordert werden. Diese Methode gibt eine Dummy-Kennung zurück, wenn gerade keine Spanne aktiv ist. Eine von dieser Methode zurückgegebene Spannenkennung bietet die folgenden Methoden an:

span.getTraceId(): Gibt die Trace-ID der Spanne zurück. (Seit v1.62.0)

span.getSpanId(): Gibt die Spannen-ID der Spanne zurück. (Seit v1.62.0)

span.getParentSpanId(): Gibt die übergeordnete Spannen-ID der Spanne zurück. (Seit v1.62.0)

span.getName(): Gibt den Namen der Spanne zurück. (Seit v1.62.0)

span.isEntrySpan(): Bestimmt, ob es sich bei der Spanne um eine Eingangsspanne (Serverspanne) handelt. (Seit v1.62.0)

span.isExitSpan(): Bestimmt, ob es sich bei der Spanne um eine Ausgangsspanne (Clientspanne) handelt. (Seit v1.62.0)

span.isIntermediateSpan(): Bestimmt, ob es sich bei der Spanne um eine Zwischenspanne (lokale Spanne) handelt. (Seit v1.62.0)

span.getTimestamp(): Gibt die Zeitmarke vom Anfang der Spanne an (seit v1.62.0).

span.getDuration(): Gibt die Dauer der Spanne zurück. Diese Methode gibt den Wert 0 zurück, wenn die Spanne noch nicht abgeschlossen wurde. Beachten Sie, dass dies fast immer der Fall ist, da instana.currentSpan() die derzeit aktive Spanne zurückgibt, die per Definition noch nicht abgeschlossen wurde. Gibt nur eine Dauer größer als 0 zurück, wenn span.disableAutoEnd() und span.end() verwendet wurden (siehe folgende Informationen). (Seit v1.62.0)

span.annotate(path, value): Fügt der Spanne eine Annotation (auch Tag oder angepasster Tag genannt) hinzu. Der Pfad (path) kann als durch Punkte getrennte Zeichenfolge oder als Array von Zeichenfolgen angegeben werden. Das heißt, die folgenden zwei Snippets sind funktional gleichwertig:

  • span.annotate('sdk.custom.tags.myTag', 'My Value') und
  • span.annotate(['sdk', 'custom', 'tags', 'myTag'], 'My Value').
  • span.annotate(['sdk', 'custom', 'tags', 'service'], 'dummy service').

Beachten Sie, dass vor angepassten Tags immer sdk.custom.tags vorangestellt sein sollte. Sie können auch annotate verwenden, um Standard-Tags zu überschreiben, etwa die HTTP-Pfadvorlage (Beispiel: span.annotate('http.path_tpl', '/user/{id}/details')), aber dies wird nicht empfohlen, es sei denn, es gibt sehr gute Gründe dafür, in das automatische Tracing von Instana einzugreifen. (Seit v1.97.1)

span.markAsErroneous(message, [customErrorMessagePath]): Markiert die Spanne als Fehler, d. h., die Fehleranzahl für die Spanne wird auf 1 gesetzt. Sie sollten eine Nachricht angeben, die erklärt, warum dieser Bereich als erstes Argument fehlerhaft ist. Die Nachricht wird als Anmerkung hinzugefügt. Wenn markAsErroneous ohne Argumente aufgerufen wird, wird eine Standardnachricht festgelegt. Sie können einen angepassten Pfad zur Fehlernachrichtenannotation angeben, aber in fast allen Fällen ist es besser, dass das Instana-SDK Node.js dies alleine handhabt. Wenn ein angepasster Pfad angegeben wird, kann er als Zeichenfolge oder als Array angegeben werden (siehe span.annotate) und muss mit sdk.custom.tagsbeginnen. (Seit: v2.25.2)

span.markAsNonErroneous([customErrorMessagePath]): Markiert die Spannweite als nicht fehlerhaft. Dies kann nützlich sein, wenn der Bereich durch eine automatische Traceinstrumentierung oder durch span.markAsErroneous als Fehler markiert wurde und diese Entscheidung zurückgesetzt werden muss. Wenn span.markAsErroneous mit einem früheren customErrorMessagePath verwendet wurde, geben Sie denselben Pfad zu span.markAsNonErroneousan, andernfalls lassen Sie den Parameter weg. (Seit: v2.25.2)

span.getErrorCount(): Gibt die Anzahl der Fehler zurück, die während der Verarbeitung der Anforderung aufgetreten sind, die dem momentan aktiven Bereich zugeordnet ist. Diese Methode gibt 1 zurück, wenn die Spanne als Fehler markiert wurde, und gibt andernfalls 0 zurück. Ein Fehlerzähler, der größer als 1 ist, kann nur auftreten, wenn der aktuelle Bereich ein Stapelbereich ist. (Seit v1.62.0)

span.disableAutoEnd(): Siehe nächster Abschnitt. (Seit v1.55.1)

span.end(errorCount): Siehe nächster Abschnitt. (Seit v1.55.1)

-Bereiche manuell beenden (Nachrichtenbrokereinträge)

Wir haben bereits erwähnt, dass die automatisierte Traceerstellung von Instana alles für Sie für unterstützte Bibliotheken verarbeitet und dass es nicht erforderlich ist, sich zu stören. Es gibt eine kleine Ausnahme von dieser Regel: Tracing-Operationen, die durch das Verarbeiten einer Nachricht von einem Nachrichtenbroker (Kafka, RabbitMQ, NATS, NATS-Streaming, Google Cloud PubSub) ausgelöst wurden. Da bei der Verarbeitung einer Nachricht von einem Nachrichtenbroker keine Reaktion oder Antwort erwartet wird, gibt es kein Ereignis, das dem Node.js-Collector von Instana melden könnte, dass alle Operationen, die von einer bestimmten Nachricht ausgelöst wurden, abgeschlossen wurden (im Gegensatz zu einer eingehenden HTTP-Anforderung, für die es immer eine zugeordnete Antwort gibt, die das Ende der Transaktion abgrenzt).

Wenn ein Prozess eine neue Nachricht empfängt, startet er eine Eingangsspanne. Die Tracing-Funktionen von Instana benötigen ein Ereignis, das angibt, dass diese Spanne abgeschlossen ist. Andere Aufrufe werden nur dann demselben Trace zugewiesen, wenn sie zwischen dem Starten der Eingangsspanne und dem Beenden derselben Spanne ausgelöst werden. Daher muss der Anwendungscode Instana mitteilen, dass die Verarbeitung der eingehenden Nachricht abgeschlossen ist. Zu diesem Zweck kann die Kennung für die derzeit aktive Spanne verwendet werden, die mit instana.currentSpan() (siehe oben) angefordert werden kann. Die Kennung bietet zwei Methoden an, die für diesen Anwendungsfall relevant sind:

span.disableAutoEnd(): Inaktiviert den automatischen Abschluss der Spanne und markiert diese Spanne als eine, die manuell beendet wird, indem später span.end() aufgerufen wird.

span.end(errorCount): Schließt eine Spanne ab, bei der zuvor span.disableAutoEnd() aufgerufen wurde. Das Argument errorCount ist optional. Übergeben Sie 1, wenn bei der Verarbeitung der Nachricht ein Fehler aufgetreten ist. Wenn nichts übergeben wird, nimmt errorCount standardmäßig den Wert 0 an.

Hier ein Beispiel dafür, wie dies für RabbitMQ aussieht:

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);
});

Beachten Sie, dass der Bereich nie an das Back-End gesendet wird, wenn Sie span.disableAutoEnd() aufrufen, aber vergessen, span.end() darauf aufzurufen.

Beachten Sie auch, dass für Nachrichtenbroker wie Kafka, RabbitMQ und NATS gilt: Wenn Sie span.disableAutoEnd() nicht synchron in Ihrer Nachrichtenbehandlungsfunktion aufrufen, wird die Spanne automatisch sofort beendet und gesendet, nachdem die Funktion für die Nachrichtenverarbeitung zurückgegeben wurde. Dadurch wird dieser Trace unterbrochen, d. h. während der Verarbeitung dieser Nachricht ausgeführte Operationen (Datenbankzugriff, abgehende HTTP-Aufrufe, Senden anderer Nachrichten) werden in Instana nicht als Aufrufe angezeigt.

Es ist nicht erforderlich, dies zu tun, wenn Nachrichten an einen Nachrichtenbroker gesendet/veröffentlicht werden.

Spans manuell mit dem SDK erstellen

Der Collector verwendet automatisch häufig verwendete APIs, um sofort einsatzfähige Tracing-Unterstützung bereitzustellen. Manchmal kann es sein, dass dies nicht ausreicht. Das SDK kann verwendet werden, um Einblicke in Bereiche Ihrer Anwendungen zu liefern, z. B. benutzerdefinierte Bibliotheken und Frameworks, die andernfalls unbemerkt bleiben würden. Zu diesem Zweck ermöglicht das SDK Ihnen das manuelle Erstellen von Spannen. Ein weiterer Anwendungsfall ist die Erstellung von Zwischenspannen, um interessante Codeabschnitte abzugrenzen. Last but not least kann das SDK verwendet werden, um die Traceerstellung in Anwendungen zu aktivieren, die keine Anforderungen von außen empfangen, sondern eigene Arbeit starten, wie beispielsweise geplante Jobs, die von setIntervalausgelöst werden, setTimeout oder ähnliche Mittel (siehe wie folgt , wenn dies Ihr Anwendungsfall ist).

Mit dem SDK erstellte Spannen werden nahtlos in die automatischen Tracing-Funktionen von Instana integriert.

Terminologie

Das SDK bietet Funktionen für die Erstellung von Eingangsspannen, Zwischenspannen und Ausgangsspannen an. Kurz gesagt gilt:

  • Eingangsspannen stellen Aufrufe in die überwachte Anwendung dar. Dabei kann es sich um HTTP-Anforderungen handeln, die die Anwendung von anderen Services empfängt, oder um Nachrichten, die die Anwendung aus einer Warteschlange abruft. (Selbstverständlich sind HTTP-Anforderungen bereits durch das automatische Tracing abgedeckt.) Im Allgemeinen stellen Eingabespans etwas dar, das die Verarbeitung innerhalb Ihrer Node.js-Anwendung auslöst. Ein Trace muss immer mit einem Eingabespan beginnen (die entweder durch automatische Instrumentierung oder über das SDK erstellt wird). Der Node.js-Tracer bleibt inaktiv und es werden keine Zwischen- oder Ausgabespans erfasst, es sei denn, es gibt einen aktiven Eingabespan. Weitere Informationen hierzu finden Sie im Abschnitt Traceerstellung ist inaktiv, wenn kein aktiver Eintragsbereich vorhanden ist .
  • Ausgangsspannen stellen Aufrufe dar, die von der Anwendung ausgegeben werden. Dabei kann es sich um HTTP-Anforderungen handeln, die von der Anwendung getätigt (und von anderen Services beantwortet) werden, oder um Datenbankaufrufe. (Auch hier gilt: Abgehende HTTP-Anforderungen und viele beliebte Datenbanken sind bereits durch die automatische Instrumentierung abgedeckt.)
  • Bei Zwischenspannen handelt es sich um Dinge, die innerhalb der überwachten Anwendung geschehen, d. h. sie gehen weder in den Prozess ein noch verlassen sie ihn, wie dies Eingangsspannen und Ausgangsspannen tun. Zwischenspannen können auch verwendet werden, um automatisch erstellte Spannen einzuschließen, falls Sie zusätzliche Attribute (Tags genannt) bereitstellen möchten, die von der automatischen Instrumentierung nicht bereitgestellt werden.

Weitere Informationen zur Terminologie und insbesondere zur Beziehung zwischen Bereichen und den Aufrufen , die in der Instana-Benutzerschnittstelle angezeigt werden, finden Sie in der Dokumentation zur Traceerstellung.

Es gibt auch einen Abschnitt zur Traceerstellung für bewährte Verfahren , der gelesen werden sollte, bevor mit der Implementierung der angepassten Traceerstellung mit dem SDK begonnen wird.

Schließlich gibt es eine Demo-App, die Sie lokal starten und untersuchen können, um Ihnen den Einstieg in das Instana Node.js SDK zu erleichtern: https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

Callback-basierte API, Promise-basierte API und asynchrone API

Das SDK bietet drei verschiedene Typen von APIs: einen rückrufbasierten (instana.sdk.callback), einen zusprechbasierten (instana.sdk.promise) und einen für Code, der den async/await -Stil (instana.sdk.async) verwendet. Welche verwendet wird, ist rein Geschmackssache. Im Grunde bieten alle drei APIs verschiedene Methoden zum Starten eines Bereichs und zum Vervollständigen von Bereichen. Sie müssen den Bereich direkt starten, bevor Sie die Arbeit ausführen, die der Bereich darstellen soll, und den Bereich abschließen, sobald die Arbeit abgeschlossen ist.

Wenn Sie die Callback-API verwenden, müssen Sie immer dann einen Callback übergeben, wenn Sie eine neue Spanne starten, und alle Aufgaben, die mit dieser Spanne verknüpft sind, innerhalb dieses Callbacks ausführen (oder im Callback einer beliebigen asynchronen Operation, die transitiv von diesem Callback ausgelöst wird). Hier ein Beispiel:

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);
  });
});

Beachten Sie, dass der für startXxxSpan angegebene Callback sofort und synchron ohne Argumente aufgerufen wird. Innerhalb dieses Callbacks können andere asynchrone Operationen ausgelöst werden.

Wenn Sie die Promise-API verwenden, geben alle Methoden, die eine neue Spanne starten, ein Promise zurück. Alle Aufgaben, die dieser Spanne zugeordnet sind, müssen in dieser Promise-Kette erfolgen (d. h. entweder im then des von startXxxSpan zurückgegebenen Promise oder in einem beliebigen then-Handler weiter unten in der Promise-Kette). Hier ein Beispiel:

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);
});

Beachten Sie, dass das von startXxxSpan zurückgegebene Promise nie zurückgewiesen, sondern sofort ohne einen Wert aufgelöst wird. Andere asynchrone Operationen können innerhalb des then-Handler ausgelöst werden.

Für Code im Stil 'async/await' bietet das SDK instana.sdk.async (seit Version 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);
}

Im Grunde ist instana.sdk.async nur ein anderer Name für instana.sdk.promise, da der Code async/await über Promises in der Node.js-Laufzeit verarbeitet wird.

Die completeXxxSpan-Methoden sind für alle drei API-Varianten identisch.

Wenn die obigen Beispiele nicht ausreichen, stellen Sie sicher, dass Sie zu unserer voll funktionsfähigen Demo-App springen, die das Node.js SDK zeigt: https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

-API-Methoden

Die folgenden allgemeinen Parameter werden von den SDK-Methoden akzeptiert:

  • name: der Name der Spanne. Dieser Parameter ist beim Starten einer neuen Spanne obligatorisch. Es sollte sich dabei um eine kurze und selbsterklärende Zeichenfolge handeln.
  • tags: ein optionales JS-Objekt aus zusätzlichen Metadaten für die Spanne. Sie können Tags angeben, wenn Sie die Spanne starten oder wenn Sie sie abschließen oder zu beiden Zeitpunkten. Wenn Sie beim Starten und beim Abschließen der Spanne Tags angeben, werden beide Objekte zusammengeführt. Die Tags werden in der Benutzerschnittstelle von Instana angezeigt. Sie müssen darauf achten, keine beliebig großen Objekte zu den von Ihnen erstellten Spannen hinzuzufügen. Es sollten kurze Schlüssel-Wert-Paare verwendet werden. Wenn Spannen zu groß werden, wird möglicherweise eine Gruppe von Spannen gelöscht, anstatt an den Instana-Agenten gesendet zu werden.
  • error: Wenn beim Ausführen der Arbeit, die der aktuellen Spanne zugeordnet ist, ein Fehler aufgetreten ist, kann dieser Fehler beim Abschließen der Spanne an diese angehängt werden.
  • traceId: Dieser Parameter ist nur für Eingangsspannen relevant und wird verwendet, um den Eingangsspannenteil eines vorhandenen Trace zu bilden, der bereits in einem anderen Prozess gestartet wurde. Wenn Sie eine traceId angeben, müssen Sie auch eine parentSpanId angeben.
  • parentSpanId: Dieser Parameter ist nur für Eingangsspannen relevant, die Teil eines bestehenden Trace sind, der bereits in einem anderen Prozess gestartet wurde. Es wird verwendet, um auf die Ausgangsspanne zu verweisen, die diese Eingangsspanne ausgelöst hat. Wenn Sie eine parentSpanId angeben, müssen Sie auch eine traceId angeben.

Die folgenden Methoden werden von allen drei APIs angeboten:

  • instana.sdk.callback.startEntrySpan(name [, tags[, traceId, parentSpanId]], callback),
    instana.sdk.{promise|async}.startEntrySpan(name [, tags[, traceId, parentSpanId]]):
    Startet einen Eintragsbereich. Sie müssen einen Namen (name) für die Spanne angeben. Sie können optional ein tags-Objekt bereitstellen. Die traceId und die parentSpanId sind ebenfalls optional, aber Sie müssen entweder beide IDs oder überhaupt keine ID angeben.
  • instana.sdk.callback.completeEntrySpan([error, tags]),
    instana.sdk.{promise|async}.completeEntrySpan([error, tags]):
    Beendet einen Eintragsbereich. Es können ein Fehler und zusätzliche Tags angegeben werden. Wenn Sie zusätzliche Tags, aber keinen Fehler angeben möchten, übergeben Sie null als erstes Argument.
  • instana.sdk.callback.startIntermediateSpan(name[, tags], callback),
    instana.sdk.{promise|async}.startIntermediateSpan(name[, tags]):
    Startet einen Zwischenbereich. Sie müssen eine name für die Spanne angeben und Sie können optional ein tags -Objekt bereitstellen. Die Funktionsaufrufe geben den gestarteten Bereich zurück.
  • instana.sdk.callback.completeIntermediateSpan([error, tags, span]),
    instana.sdk.{promise|async}.completeIntermediateSpan([error, tags, span]):
    Beendet einen Zwischenbereich. Es können ein Fehler, zusätzliche Tags und der auszuführende Bereich angegeben werden. Wenn Sie zusätzliche Tags ohne Fehler angeben wollen, übergeben Sie null als erstes Argument.
  • instana.sdk.callback.startExitSpan(name[, tags], callback)
    , instana.sdk.{promise|async}.startExitSpan(name[, tags]):
    Startet einen Exitbereich. Sie müssen einen Namen (name) für die Spanne und optional ein tags-Objekt bereitstellen.
  • instana.sdk.callback.completeExitSpan([error, tags])
    , instana.sdk.{promise|async}.completeExitSpan([error, tags]):
    Beendet einen Exitbereich. Es können ein Fehler und zusätzliche Tags angegeben werden. Wenn Sie zusätzliche Tags, aber keinen Fehler angeben möchten, übergeben Sie null als erstes Argument.
  • instana.sdk.callback.bindEmitter(emitter),
    instana.sdk.{promise|async}.bindEmitter(emitter):
    Siehe wie folgt.

Beachten Sie, dass mit einer startXxxSpan-Methode gestartete Spannen nur dann an Instana übergeben werden, wenn die entsprechende completeXxxSpan aufgerufen wurde. Außerdem müssen die Aufrufe für verschachtelte Spannen in der richtigen Reihenfolge ausgegeben werden.

Dies wird in den zwei folgenden Beispielen veranschaulicht. Folgendes ist gültig:

const sdk = instana.sdk.callback;

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

Folgendes ist jedoch nicht gültig:

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

Auch beim Verschachteln von Spannen mit der Promise-API muss man sorgfältig vorgehen. Folgendes ist richtig:

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

Aber Folgendes funktioniert nicht:

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();
});

Sehen Sie sich die folgenden gültigen Beispiele für Zwischenbereiche an:

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);

An dieser Stelle könnte man sich fragen, warum die API des SDK auf diese Weise gestaltet wurde. Besonders die Verwendung von startXxxSpan-Methoden, die einen Callback akzeptieren oder ein Promise zurückgeben, erscheint möglicherweise etwas umständlich. Entscheidend ist: Der asynchrone Kontext muss während des Tracing beibehalten werden. Da Node.js aus einem Einzelthread besteht und Callbacks für asynchrone Operationen verwendet, benötigt der Node.js-Collector eine Möglichkeit, festzustellen, welche Operationen zu welcher Spanne gehören. Das Einschließen der verfolgten Aktion in einen Callback oder ein Promise macht dies möglich.

Handhabung von Ereignisemittern

Wenn die Arbeit, die Ihrem angepassten SDK-Bereich zugeordnet ist, einen Ereignisemitter umfasst und wenn der Code, der innerhalb des Bereichs ausgeführt wird, für ausgegebene Ereignisse empfangsbereit ist, müssen Sie den Ereignisemitter binden . Andernfalls verhält sich Ihr Tracing-Code nicht wie erwartet. So funktioniert es:

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

Die Traceerstellung Ist Inaktiv, Wenn Es Keine Aktive Eintragsspan Gibt.

Generell gilt, dass Instana-Tracer nur dann Ausgabe- oder Zwischenspans erfassen, wenn es einen aktiven Eingabespan gibt. Dies ist eine wichtige Absicherung, damit keine Geräusche erfasst werden, die nichts mit der Verarbeitung Ihrer geschäftsbezogenen Transaktionen zu tun haben. Es gibt jedoch Fälle, in denen diese Absicherung in die Quere kommen kann. Beispiele hierfür sind Arbeiten, die von einem Mechanismus ausgelöst werden, der von Instanas automatischer Instrumentierungnicht unterstützt wird:

  • Dies könnte eine eingehende Nachricht von einer Nachrichtenbibliothek sein, die wir (noch) nicht unterstützen.
  • Oder es könnte sich um eine Anforderung über ein Protokoll handeln, das nicht unterstützt wird, z. B. rohe TCP-Nachrichten oder WebSocket-Kommunikation (die beide von der automatischen Instrumentierung nicht unterstützt werden).
  • Ein weiteres Beispiel sind Anwendungen, die geplante Jobs ausführen, die nicht von außen, sondern vom Prozess selbst ausgelöst werden (z. B. mithilfe von setTimeout, setInterval oder einer Node.js-Planungsbibliothek, die nicht unterstützt wird).

Das Fehlen eines Eingabespans hat zur Folge, dass der Instana Node.js Tracer auch keine anderen Zwischen- oder Ausgabespans für Aufrufe erfasst, die in diesem Kontext getätigt werden. Das heißt:

  • Wenn eine abgehende HTTP-Anforderung, ein Datenbankaufruf oder etwas anderes, das eine Exitausspannweite erstellen würde, über eine unterstützte Bibliothekausgelöst wird, aber keine aktive Eingangsspannweite vorhanden ist, wird diese Exitausspannweite nicht erfasst.
  • Wenn das Instana Node.js SDK verwendet wird, um einen Zwischen- oder Ausgabespan zu starten, es aber keinen aktiven Eingabespan gibt, wird das SDK den Start dieses Spans ablehnen. Es wird eine Warnung darüber protokolliert.

Die Lösung für diese Art von Situation besteht darin, eine Eintragsspanne am Anfang der Arbeitseinheit manuell zu starten und sie abzuschließen , wenn sie fertig ist. Auf diese Weise werden alle Ausgabe- und Zwischenspans erfasst, da es einen aktiven Eingabespan gibt.

Sie können auch eine Featureanforderung erstellen , um sofort einsatzfähige Unterstützung für den verwendeten Auslösertyp zu implementieren.

Asynchronen Kontext Manuell Wiederherstellen

Instana verlässt sich intern auf async_hooks , um den asynchronen Kontext beizubehalten. Dies funktioniert automatisch und transparent, ohne dass manuelle Eingriffe erforderlich sind. Es gibt jedoch Bibliotheken und Codemuster, die die Kontinuität von async_hooks unterbrechen könnten, sodass der asynchrone Kontext verloren geht. Einige dieser Probleme werden in dieser Listeaufgelistet. Das Verwenden eines solchen Moduls kann zu unvollständigen Traces und fehlenden Aufrufen führen.

Das Node.js-SDK stellt eine API bereit, um dies zu korrigieren. Bitte beachten Sie, dass es fast nie erforderlich ist, diese API zu verwenden. Sie sollten sie nur verwenden, wenn Sie genau wissen, welche Codezeile die fehlenden Aufrufe verursacht und warum.

  • instana.sdk.getAsyncContext(): Gibt den derzeit aktiven asynchronen Kontext zurück. Rufen Sie dies auf, bevor Sie den Code aufrufen, der die asynchrone Kontinuität stört. (Seit v1.100.0)
  • instana.sdk.runInAsyncContext(context, callback): Führt den angegebenen callback im angegebenen asynchronen Kontext aus. Das context-Objekt sollte bereits durch Aufrufen von instana.sdk.getAsyncContext() angefordert worden sein. Schließen Sie den gesamten Code, der in diesem Kontext ausgeführt werden soll, in callbackein. (Seit v1.100.0)
  • instana.sdk.runPromiseInAsyncContext(context, createPromiseFn): Führt ein Promise im bereitgestellten asynchronen Kontext aus. Das context-Objekt sollte bereits durch Aufrufen von instana.sdk.getAsyncContext() angefordert worden sein. Es wird erwartet, dass die Funktion createPromiseFn das Promise zurückgibt, das Sie in diesem Kontext ausführen möchten. (Seit v1.100.0)

Dies ist Beispiel-Code für runInAsyncContext, um dessen Verwendung zu veranschaulichen:

  // ...

  // 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) {
        // ...
      }

      // ...
    });
  });

Dies ist Beispiel-Code für 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 -Integration

Dieses Paket implementiert auch die OpenTracing-API. Zur Verwendung von OpenTracing für Node.js mit Instana sollten Sie die automatische Traceerstellung inaktivieren und die Instana-API-Implementierung OpenTracing verwenden. Das folgende Beispielprojekt veranschaulicht dies:

// 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();

Einschränkungen

  • OpenTracing ist nicht in das automatische Tracing von Instana integriert. Insbesondere werden Spannen, die mithilfe der OpenTracing-API erstellt wurden, nicht Teil desselben Trace sein wie die von unserer automatischen Tracing-Instrumentierung erstellten Spannen. Wenn Sie zusätzliche Bereiche zu den automatisch erstellten Bereichen hinzufügen möchten, sollten Sie das SDK gegenüber OpenTracingbevorzugen. Tatsächlich empfehlen wir, entweder das automatische Tracing (optional erweitert durch SDK-Spannen) oder OpenTracing zu verwenden, aber nicht beides in einer Anwendung.
  • Der Instana-Node.js-Collector bietet keine Unterstützung für OpenTracing-Binärträger. Diese OpenTracing-Implementierung ignoriert OpenTracing-Binärträgerobjekte im Hintergrund.
  • Außerdem sollte auch mit OpenTracing-Baggage-Elementen sorgsam umgegangen werden. Baggage-Elemente sind Metadaten, die mithilfe von Trägerobjekten über Netzgrenzen hinweg transportiert werden. Darüber hinaus werden diese Metadaten von untergeordneten Bereichen (und deren untergeordneten Bereichen ...) übernommen.. Dies kann einen gewissen Aufwand mit sich bringen. Wir empfehlen, die OpenTracing-Baggage-API ganz zu vermeiden.

Siehe auch