Instana Node.js API

Initialisation du collecteur Instana Node.js

Comme expliqué sur la page d'installation, l'export principal de @instana/collector est une fonction qui initialise le collecteur Instana Node.js. En d'autres termes, lorsque vous chargez le module à l'aide de require ou import, il renvoie une fonction qui doit être appelée par votre code d'application, avant de demander ou d'importer d'autres modules. Les sections suivantes fournissent des explications plus détaillées à ce sujet pour les applications Node.js qui utilisent soit le système de modules d' CommonJS, soit le système de modules d'ECMAScript.

CommonJS

Vous n'avez besoin d'appeler la fonction d'initialisation qui est exportée par @instana/collector qu'une seule fois, mais vous devez le faire au début de votre application Node.js , avant toute autre instruction require et avant l'exécution de tout autre code:

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

La fonction d'initialisation renvoie une référence à elle-même, ce qui est utile si vous souhaitez accéder à d'autres exportations fournies par le collecteur Instana Node.js. Ainsi, les deux fragments suivants sont équivalents :

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

est identique à :

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

La fonction d'initialisation accepte un paramètre facultatif, un objet de configuration :

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

Consultez la page de configuration pour plus de détails sur l'objet de configuration.

Pour plus d'informations sur l'installation et la configuration initiale du collecteur Instana Node.js, consultez les sections « Installation du collecteur Node.js » et « Vérification de l'intégration du collecteur ».

Modules ECMAScript

Pour les applications d' Node.js s qui utilisent le système de modules ECMAScript, initialisez le collecteur Instana Node.js à l'aide des indicateurs de chargement Node.js.

Si vous souhaitez utiliser le SDK Instana Node.js dans votre application Node.js basée sur des modules ECMAScript, vous pouvez obtenir une référence à l'interface Instana API comme suit :

import instana from '@instana/collector';
 

Pour obtenir la portée actuelle d'une application express, exécutez la commande comme indiqué dans l'exemple suivant :

import instana from '@instana/collector';
...

const app = express();

app.get('/', (req) => {
  const span = instana.currentSpan();
  console.log(span);
  console.log(span.getTraceId());
})
 

Vous n'avez pas besoin de réinitialiser le collecteur Instana Node.js, car celui-ci est initialisé par l'indicateur de chargement Node.js--import fourni lors du démarrage.

Utilisation de plusieurs paquets de collecteurs d' Instana s

Dans un environnement « Kubernetes », il se peut que plusieurs collecteurs d' Instana s soient en cours d'exécution.

Kubernetes

Lorsque vous utilisez le webhook Instana AutoTrace, le collecteur Node.js est automatiquement installé dans le /opt/instana/instrumentation/nodejs/node_modules/@instana/collector répertoire.

Si vous souhaitez utiliser le SDK Instana Node.js dans votre application Node.js basée sur Kubernetes, il est recommandé de l'installer @instana/collector en tant que dépendance directe dans votre projet.

const instana = require('@instana/collector')();
 
Remarque : l'initialisation du collecteur Instana Node.js prend un certain temps. Pour commencer immédiatement à créer des segments à l'aide du SDK, ajoutez un court délai (par exemple, 500 ms) afin de vous assurer que le collecteur Instana Node.js est prêt et a établi une connexion avec l'agent hôte Instana.

Accéder au site Instana API

La plupart des applications n'ont besoin que d 'initialiser le collecteur « Instana » pour la surveillance. Cependant, dans certains cas, vous devrez peut-être utiliser des fonctionnalités ou des éléments supplémentaires de l'interface Instana API, au-delà de la simple initialisation du collecteur. Il peut s'agir de scénarios nécessitant des configurations de surveillance avancées, un suivi personnalisé ou des intégrations spécifiques.

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

// ...
 
Remarque : si vous avez besoin d'une référence à @instana/collector dans plusieurs fichiers, il n'est pas nécessaire de l'initialiser @instana/collector à nouveau. Vous pouvez utiliser const instana = require('@instana/collector'); à la place de const instana = require('@instana/collector')(); dans tous les fichiers, à l'exception de ceux du module principal et du point d'entrée de votre application.

Vérification de l'état du suivi

Pour vérifier si la fonction de trace est activée, vous pouvez utiliser la méthode instana.isTracing() , qui vérifie l'état de la fonction de trace. Cette méthode peut être utilisée dans les scénarios où vous devez exécuter du code de manière conditionnelle en fonction du statut de la fonction de trace. Cependant, dans la plupart des cas, il n'est pas nécessaire de vérifier si le traçage est activé, car Instana gère automatiquement le traçage. Vous pouvez utiliser cette méthode pour le débogage ou dans des environnements complexes où vous devez vous assurer que la fonction de trace est explicitement contrôlée.

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

if (instana.isTracing()) {
    console.log('Tracing is enabled.');
} else {
    console.log('Tracing is disabled.');
}
 

Définition du consignateur après initialisation

Utilisez instana.setLogger(logger) pour fournir un enregistreur personnalisé à @instana/collector à la place de l'enregistreur pino par défaut.

Comme indiqué précédemment, vous devez appeler la fonction d'initialisation (renvoyée par require('@instana/collector')) immédiatement, avant de demander ou d'importer d'autres packages, sinon le traçage automatique d'Instana ne fonctionnera que partiellement. En particulier, vous devez initialiser Instana avant de demander votre package de journalisation (par exemple, bunyan, pino ou winston). Si vous demandez le package de journalisation avant d'initialiser Instana, vous ne verrez pas les messages de journal dans Instana.

D'autre part, vous souhaiterez peut-être transmettre votre propre consignateur au collecteur Node.js de sorte que ses messages de journal aient le même format et soient écrits dans les mêmes fichiers journaux / destinations que le reste des messages de journal de votre application. Si vous transmettez votre consignateur à la fonction d'initialisation du collecteur, il vous faudra demander ce package de journalisation avant d'initialiser Instana. Pour résoudre cette dépendance cyclique, @instana/collector permet à la fonction setLogger d'initialiser Instana sans recourir à un consignateur personnalisé dans un premier temps et de définir le consignateur par la suite.

Pour donner un exemple concret, le code suivant n'est pas pris en charge :

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

Au lieu de cela, initialisez Instana en premier, sans consignateur, avant toute autre demande. Ensuite, définissez le consignateur que devra utiliser Instana, une fois le consignateur demandé et initialisé :

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

Les premières lignes du journal généré par ` Instana ` (pendant la procédure d'initialisation) seront enregistrées par le journaliseur pino par défaut d' Instana, mais tout ce qui suit cet instana.setLogger(logger) appel sera enregistré par le journaliseur que vous avez configuré. De plus, la sortie de journal de votre application s'affiche correctement dans l'onglet "Messages du journal" dans les tableaux de bord d'Instana (notez que seuls les appels de journal dont la gravité est au moins "WARN" sont affichés).

Si vous transmettez un consignateur Bunyan ou Pino à setLogger, le collecteur Node.js va créer des enfants du consignateur indiqué avec le même niveau de journalisation et les mêmes flux cible. D'autres modules de journalisation sont également pris en charge tant qu'ils fournissent des fonctions pour les niveaux de journalisation debug, info, warn et error. Dans ce cas, le collecteur Node.js ne crée pas de consignateurs enfants, mais utilise simplement le consignateur indiqué en l'état.

Notez que vous devez définir le niveau de consignation souhaité sur le journal d'événements que vous fournissez à @instana/collector. Si vous obtenez des journaux de débogage inattendus de @instana/collector, veillez à envoyer un journal d'événements pour lequel le niveauinfo ou warn est défini.

Accès à l'étendue active en cours

La fonctionnalité de traçage automatisé de Instana gère tout pour vous pour les bibliothèques prises en charge; vous n'avez pas besoin d'intervenir.

Néanmoins, le code d'application est limité à l'accès en lecture seule à l'état interne du collecteur. A cet effet, un descripteur d'étendue active en cours peut être obtenu avec instana.currentSpan(). Cette méthode renvoie un descripteur factice lorsqu'aucune étendue n'est actuellement active. Un descripteur d'étendue renvoyé ainsi offre les méthodes suivantes :

span.getTraceId() : renvoie l'ID de trace de l'étendue. (Depuis la version 1.62.0)

span.getSpanId() : renvoie l'ID de l'étendue. (Depuis la version 1.62.0)

span.getParentSpanId() : renvoie l'ID de l'étendue parent de l'étendue. (Depuis la version 1.62.0)

span.getName() : renvoie le nom de l'étendue. (Depuis la version 1.62.0)

span.isEntrySpan() : détermine s'il s'agit d'une étendue d'entrée (étendue serveur). (Depuis la version 1.62.0)

span.isExitSpan() : détermine s'il s'agit d'une étendue de sortie (étendue client). (Depuis la version 1.62.0)

span.isIntermediateSpan() : détermine s'il s'agit d'une étendue intermédiaire (étendue locale). (Depuis la version 1.62.0)

span.getTimestamp() : renvoie l'horodatage du démarrage de l'étendue (depuis la version v1.62.0).

span.getDuration(): renvoie la durée de l'intervalle. Cette méthode retournera 0 si l'étendue n'est pas encore terminée. Notez que cela est presque toujours le cas car instana.currentSpan() renvoie la durée de l'étendue active en cours, qui, par définition, n'est pas terminée. Cela ne renverra une durée supérieure à 0 que si span.disableAutoEnd() et span.end() ont été utilisés (voir ci-dessous). (Depuis la version 1.62.0)

span.annotate(path, value): ajoute une annotation (également appelée balise ou balise personnalisée) à l'étendue. path peut être fourni sous la forme d'une chaîne séparée par des points ou sous la forme d'un tableau de chaînes. Ainsi, les deux appels suivants sont équivalents :

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

Notez que les balises personnalisées doivent toujours être préfixées par sdk.custom.tags. Vous pouvez également utiliser annotate pour remplacer les balises standard, comme le modèle de chemin d'accès HTTP (exemple : span.annotate('http.path_tpl', '/user/{id}/details')), mais cela n'est pas recommandé, sauf s'il existe de très bonnes raisons d'interférer avec le traçage automatique de Instana. (Depuis la version 1.97.1)

span.markAsErroneous(message, [customErrorMessagePath]): marque l'étendue comme erronée en définissant le nombre d'erreurs d'étendue sur 1. L'argument de message explique pourquoi l'intervalle est considéré comme erroné. Si cette méthode est appelée sans arguments, un message d'erreur par défaut est défini automatiquement. Fournir un chemin personnalisé pour le message d'erreur (customErrorMessagePath ) est facultatif carNode.js Tracer gère automatiquement le chemin.

Exemples :

  • Marquage d'une étendue comme erronée:
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');
 
  • Marquer une plage d' HTTP s comme erronée avec le chemin personnalisé :
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]): marque l'étendue comme non erronée. Cette méthode peut être utile pour rendre une étendue non erronée si l'étendue a été précédemment marquée comme une erreur, soit par l'instrumentation de traçage automatique, soit par l'appel de la méthode span.markAsErroneous . Si vous avez utilisé le paramètre customErrorMessagePath lors de l'appel de la méthode span.markAsErroneous , indiquez le même chemin d'accès à la méthode span.markAsNonErroneous . Si ce n'est pas le cas, vous pouvez omettre le paramètre customErrorMessagePath . (Depuis: v2.25.2)

span.getErrorCount(): Renvoie le nombre d'erreurs qui se sont produites lors du traitement de la demande associée à l'intervalle actuellement actif. Cette méthode renvoie 1 si l'étendue a été marquée comme une erreur et renvoie 0 dans le cas contraire. Un nombre d'erreurs supérieur à 1 peut se produire uniquement lorsque l'étendue en cours est une étendue par lots. (Depuis la version 1.62.0)

span.disableAutoEnd(): voir section suivante. (Depuis la version 1.55.1)

span.end(errorCount): voir section suivante. (Depuis la version 1.55.1)

Etendues terminées manuellement (entrées du courtier de messages)

Nous avons déjà mentionné que la fonctionnalité de traçage automatisé d' Instana se charge de tout pour vous en ce qui concerne les bibliothèques prises en charge et qu'il n'est pas nécessaire d'intervenir. Il existe une exception à cette règle : les opérations de traçage déclenchées par la réception d'un message provenant d'un courtier de messages ( Kafka, RabbitMQ, NATS, NATS streaming, Google Cloud PubSub ). Etant donné qu'il n'existe aucune notion de réponse lors de la consommation d'un message d'un courtier de messages, aucun événement ne peut signaler au collecteur Instana Node.js la fin de toutes les opérations déclenchées par un message particulier (contrairement à une demande HTTP entrante, qui a toujours une réponse associée, délimitant la fin de la transaction).

Lorsqu'un processus reçoit un nouveau message, il démarre une étendue d'entrée. Les fonctions de trace d'Instana ont besoin d'un événement indiquant que cette étendue est terminée. D'autres appels seront affectés à la même trace uniquement lorsqu'ils seront déclenchées entre le début et la fin de l'étendue d'entrée. Par conséquent, le code d'application doit indiquer à Instana que le traitement du message entrant est terminé. À cette fin, il est possible d'utiliser l'identifiant de la section actuellement active, qui peut être obtenu via instana.currentSpan() (voir « Accès à la section actuellement active »). Le descripteur offre deux méthodes appropriées pour ce cas d'utilisation :

span.disableAutoEnd() : désactive la fin automatique de l'étendue en indiquant qu'elle sera terminée manuellement en appelant span.end() ultérieurement.

span.end(errorCount) : termine une étendue sur laquelle la méthode span.disableAutoEnd() a été appelée précédemment. L'argument errorCount est facultatif. Transmet 1 si une erreur s'est produite lors du traitement du message. Si rien n'est transmis, la valeur par défaut de errorCount est 0.

Voici un exemple de résultat pour 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);
});
 

Notez que l'intervalle ne sera jamais envoyé au système dorsal si vous appelez span.disableAutoEnd() mais que vous oubliez d'appeler span.end() dessus.

Notez également que pour les courtiers de messages comme Kafka, RabbitMQ et NATS, si vous n'appelez pas span.disableAutoEnd() de façon synchrone dans votre fonction de gestion des messages, l'étendue sera arrêtée et transmise automatiquement immédiatement après le retour de votre fonction de gestion des messages. Cela va interrompre la fonction de trace, c'est-à-dire que les opérations (accès à la base de données, appels HTTP sortants, envoi d'autres messages) exécutées lors du traitement de ce message ne s'afficheront pas comme des appels dans Instana.

Il n'est pas nécessaire de le faire lors de l'envoi ou de la publication de messages à un courtier de messages.

Activer le traçage en l'absence d'une période d'entrée active

Pour les collecteurs d' Instana s de type « 4.0 » et les versions ultérieures, vous pouvez capturer des intervalles de sortie même en l'absence d'intervalle d'entrée. Pour activer cette fonctionnalité, définissez la variable INSTANA_ALLOW_ROOT_EXIT_SPAN d'environnement ou configurez-la via la configuration du traceur. Pour plus d'informations sur la configuration de cette fonctionnalité, voir Allow root exit span without entry span.

Pour les versions antérieures au collecteur Instana 4.0, le collecteur Instana Node.js ne capture les segments de sortie ou intermédiaires que lorsqu'un segment d'entrée est actif. Pour plus d'informations, voir La recherche est inactive lorsqu'il n'existe pas de plage d'entrée active.

Création manuelle d'étendues avec le kit SDK

Le collecteur instrumente automatiquement les API les plus utilisées pour un traçage prêt à l'emploi, mais vous pourriez avoir besoin d'informations supplémentaires dans certains cas. Par exemple :

  • Bibliothèques ou frameworks personnalisés : Vous pouvez utiliser le SDK pour capturer des intervalles dans des zones de votre application qui ne sont pas automatiquement instrumentées, ce qui permet d'obtenir des informations qui n'auraient pas été détectées autrement.

  • Portées intermédiaires : vous pouvez créer des portées pour délimiter clairement les sections importantes de votre code, ce qui permet d'approfondir la visibilité de la trace sur des opérations spécifiques.

  • Travail déclenché en interne : Pour les applications qui lancent leur propre travail (comme les tâches programmées déclenchées par " setInterval, " setTimeout ou des méthodes similaires), vous pouvez utiliser le SDK pour activer le suivi de cette activité, même sans requête entrante provenant d'une source externe.

Les étendues créées avec le kit SDK s'intègrent de façon transparente aux fonctions de trace automatique d'Instana.

Terminologie

Le kit SDK fournit des fonctions permettant de créer des étendues d'entrée, des étendues intermédiaires et des étendues de sortie. En bref :

  • Les étendues d'entrée représentent des appels dans l'application sous surveillance. Ces appels peuvent être des requêtes d' HTTP s que l'application reçoit d'autres services ou des messages que l'application récupère dans une file d'attente. (Bien sûr, les demandes HTTP sont déjà couvertes par le traçage automatique.) En général, les spans d'entrée représentent quelque chose qui déclenche un traitement dans votre application Node.js. Une trace doit toujours commencer par un point d'entrée (créé par l'instrumentation automatique ou par le SDK). Le traceur « Node.js » reste inactif et aucune section intermédiaire ou de sortie n'est enregistrée, à moins qu'une section d'entrée active ne prenne fin. Pour plus d'informations, voir la section Le traçage est inactif en l'absence d'une plage d'entrée active.
  • Les étendues de sortie représentent les appels effectués par l'application. Il peut s'agir de demandes HTTP que l'application effectue (et qui ont obtenu des réponses d'autres services) ou d'appels de base de données. (Là encore, les demandes HTTP sortantes et de nombreuses bases de données populaires sont déjà couvertes par l'instrumentation automatique.)
  • Les étendues intermédiaires se produisent à l'intérieur de l'application sous surveillance, c'est-à-dire qu'elles n'entrent pas dans le processus et ne le quitte pas à l'instar des étendues d'entrée et de sortie. Les étendues intermédiaires peuvent également être utilisées pour encapsuler des étendues créées automatiquement si vous voulez fournir des attributs supplémentaires (appelés balises) non fournis par l'instrumentation automatique.

Pour plus d'informations sur la terminologie et, en particulier, sur le lien entre les segments et les appels que vous verrez dans l'interface utilisateur d' Instana, consultez notre documentation sur le traçage.

Il existe également une section consacrée aux bonnes pratiques en matière de traçage qu'il est utile de consulter avant de commencer à mettre en œuvre le traçage personnalisé à l'aide du SDK.

Il existe enfin une application de démonstration que vous pouvez lancer localement et explorer pour vous aider à vous familiariser avec le SDK d' Instana Node.js : https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

API par callback, API par Promise et API asynchrone

Le SDK offre trois types d'API différents, un basé sur le rappel (instana.sdk.callback), un basé sur la promesse (instana.sdk.promise) et un pour le code qui utilise le style async/await (instana.sdk.async). Celui qui est utilisé est purement une question de goût. Fondamentalement, les trois API offrent différentes méthodes pour démarrer une plage et pour terminer des plages. Vous devez démarrer l'étendue directement avant d'effectuer le travail que vous souhaitez que l'étendue représente et terminer l'étendue une fois que le travail est terminé.

Lors de l'utilisation de l'API de rappel, vous devez transmettre un rappel chaque fois que vous démarrez une nouvelle étendue et que vous effectuez tout le travail associé à cette étendue à l'intérieur de ce rappel (ou dans le rappel d'une opération asynchrone déclenchée de façon transitive à partir de ce rappel). Par exemple :

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

Notez que le rappel (callback) fourni à startXxxSpan est appelé immédiatement et de façon synchrone, sans arguments. D'autres opérations asynchrones peuvent être déclenchées à l'intérieur de ce rappel.

Lors de l'utilisation de l'API Promise, toutes les méthodes qui démarrent une nouvelle étendue renvoient une promesse. Tout le travail associé à cette étendue doit s'effectuer dans cette chaîne promise (c'est-à-dire, dans la section then de l'objet promise renvoyé par startXxxSpan ou dans un gestionnaire then situé plus bas dans la chaîne promise). Par exemple :

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

Notez que l'objet promise renvoyé par startXxxSpan n'est jamais rejeté, il sera résolu immédiatement sans valeur. D'autres opérations asynchrones peuvent être déclenchées à l'intérieur de son gestionnaire then.

Pour le code de style async/await, le kit SDK propose instana.sdk.async (depuis la 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);
}
 

Sous le capot, instana.sdk.async n'est qu'un alias de instana.sdk.promise, car le code async/await est géré via des objets Promise dans l'environnement d'exécution Node.js.

Les méthodes completeXxxSpan sont identiques pour ces trois types d''API.

Si les exemples précédents ne suffisent pas, n'hésitez pas à consulter notre application de démonstration complète et fonctionnelle qui présente le SDK d' Node.js : https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

Méthodes d'API

Les paramètres communs suivants sont acceptés par les méthodes du kit SDK :

  • name: Le nom de l'élément span. Ce paramètre est obligatoire lors du démarrage d'une nouvelle étendue. Il doit s'agir d'une chaîne courte et explicite.
  • tags : objet JS facultatif de métadonnées supplémentaires pour l'étendue. Vous pouvez fournir des paramètres tags lors du démarrage de l'étendue et/ou à la fin de l'étendue. Si vous fournissez des paramètres tags au démarrage et à la fin de l'étendue, les deux objets seront fusionnés. Les paramètres tags seront affichés dans l'interface utilisateur Instana. Vous devez vous assurer de ne pas ajouter d'objets trop volumineux aux étendues que vous créez. Des paires clé-valeurs courtes doivent être utilisées. Si les étendues deviennent trop importantes, un lot d'étendues peut être supprimé au lieu d'être envoyé à l'agent Instana.
  • error : lorsqu'une erreur s'est produite lors de l'exécution du travail associé à l'étendue en cours, cette erreur peut être associée à l'étendue lors de son exécution.
  • traceId : concerne uniquement les étendues d'entrée et s'utilise pour intégrer une étendue d'entrée à une trace existante qui a déjà été démarrée dans un autre processus. Si vous fournissez un paramètre traceId, vous devez également indiquer un paramètre parentSpanId.
  • parentSpanId : concerne uniquement des étendues d'entrée qui font partie d'une trace existante qui a déjà été démarrée dans un autre processus. Il est utilisé pour référencer l'étendue de sortie qui a déclenché cette étendue d'entrée. Si vous fournissez un paramètre parentSpanId, vous devez également indiquer un paramètre traceId.

Les méthodes suivantes sont proposées par les trois API :

  • instana.sdk.callback.startEntrySpan(name [, tags[, traceId, parentSpanId]], callback), instana.sdk.{promise|async}.startEntrySpan(name [, tags[, traceId, parentSpanId]]): Marque le début d'un bloc de contenu. Vous devez indiquer une name pour l'élément span. Vous pouvez éventuellement fournir un objet tags. Les paramètres traceId et parentSpanId sont également facultatifs, mais vous devez fournir les deux ID ou aucun ID.
  • instana.sdk.callback.completeEntrySpan([error, tags]), instana.sdk.{promise|async}.completeEntrySpan([error, tags]): Met fin à une séquence de saisie. Un paramètre error et d'autres objets tags peuvent être indiqués. Si vous souhaitez fournir des paramètres tags supplémentaires mais pas de paramètre error, indiquez null en tant que premier argument.
  • instana.sdk.callback.startIntermediateSpan(name[, tags], callback), instana.sdk.{promise|async}.startIntermediateSpan(name[, tags]): Marque le début d'un intervalle intermédiaire. Vous devez fournir un name pour l'élément span, et vous pouvez, si vous le souhaitez, fournir un tags objet. Les appels de fonction renvoient l'étendue démarrée.
  • instana.sdk.callback.completeIntermediateSpan([error, tags, span]), instana.sdk.{promise|async}.completeIntermediateSpan([error, tags, span]): Termine une travée intermédiaire. Une erreur, des balises supplémentaires et l'étendue à effectuer peuvent être fournies. Si vous souhaitez ajouter des balises supplémentaires sans générer d'erreur, passez null comme premier argument.
  • instana.sdk.callback.startExitSpan(name[, tags], callback) , instana.sdk.{promise|async}.startExitSpan(name[, tags]): Marque le début d'une section de sortie. Vous devez fournir un nom (name) pour l'étendue et vous pouvez éventuellement indiquer un objet tags.
  • instana.sdk.callback.completeExitSpan([error, tags]) , instana.sdk.{promise|async}.completeExitSpan([error, tags]): Termine une section de sortie. Un paramètre error et d'autres objets tags peuvent être indiqués. Si vous souhaitez fournir des paramètres tags supplémentaires mais pas de paramètre error, indiquez null en tant que premier argument.
  • instana.sdk.callback.bindEmitter(emitter), instana.sdk.{promise|async}.bindEmitter(emitter): Voir ci-dessous.

Notez que les étendues démarrées avec une méthode startXxxSpan ne sont transmises à Instana qu'une fois la méthode completeXxxSpan correspondante appelée. De plus, pour les balises `span` imbriquées, les appels doivent être dans le bon ordre.

Il convient d'être particulièrement attentif lors de l'imbrication d'étendues dans l'API Promise. Les éléments suivants sont corrects :

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

Consultez les exemples valides suivants pour les plages intermédiaires:

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

A ce stade, vous vous demandez peut-être pourquoi l'API du kit SDK est conçue ainsi, en particulier l'utilisation de méthodes startXxxSpan acceptant un rappel (callback) ou le retour d'un objet promise peut sembler bizarre. Le fait est que nous devons garder le contexte asynchrone pendant le traçage. Node.js fonctionnant avec des unités d'exécution uniques et utilisant des rappels pour effectuer des opérations asynchrones, le collecteur Node.js a besoin d'un moyen de déterminer quelles sont les opérations qui appartiennent aux différentes étendues. L'encapsulation de l'action tracée dans un objet callback ou promise permet d'y parvenir.

Traitement des émetteurs d'événements

Si le travail associé à votre étendue SDK personnalisée implique un émetteur d'événements et si le code s'exécutant à l'intérieur de l'étendue écoute les événements émis, vous devez lier l'émetteur d'événements, sinon votre code de trace ne se comportera pas comme prévu. Voici comment y parvenir :

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

Le traçage est désactivé en l'absence d'une période d'entrée active

En règle générale, les traceurs Instana ne capturent que la sortie ou les spans intermédiaires lorsqu'il existe un span d'entrée actif. Il s'agit d'une protection importante pour ne pas capturer le bruit qui n'est pas lié au traitement de vos transactions métier. Toutefois, il existe des cas limites où cette protection peut être un obstacle. On peut citer, à titre d'exemple, les tâches déclenchées par un mécanisme qui n'est pas pris en charge par l'instrumentation automatique d' Instana :

  • Il peut s'agir d'un message entrant provenant d'une bibliothèque de messagerie que nous ne prenons pas (encore) en charge.
  • Il peut aussi s'agir d'une demande via un protocole qui n'est pas pris en charge, par exemple, des messages TCP bruts ou une communication websocket (tous deux non pris en charge par l'instrumentation automatique).
  • Des applications qui exécutent des tâches planifiées qui ne sont pas déclenchées de l'extérieur, mais par le processus lui-même (par exemple en utilisant setTimeout, setInterval ou une bibliothèque de planification Node.js qui n'est pas prise en charge) est un autre exemple.

La conséquence de ne pas avoir de span d'entrée est que le traceur Instana Node.js ne capturera pas non plus d'autres spans intermédiaires ou de sortie pour les appels qui sont faits dans ce contexte. Par exemple :

  • Si une requête « HTTP » sortante, un appel à la base de données ou toute autre opération susceptible de générer un « exit span » est déclenchée via une bibliothèque prise en charge, mais qu'il n'existe aucun « entry span » actif, cet « exit span » ne sera pas capturé.
  • Si le SDK Instana Node.js est utilisé pour démarrer un span intermédiaire ou de sortie et qu'il n'y a pas de span d'entrée actif, le SDK refuse de démarrer ce span. Un avertissement est consigné à ce sujet.

Cependant, une solution est désormais disponible pour ce type de situation. Pour Instana collector 4.0 et versions ultérieures, vous pouvez capturer les spans de sortie même en l'absence de span d'entrée. Pour activer cette fonctionnalité, voir les instructions de la section Autoriser la sortie de la racine de la travée sans la travée d'entrée. Pour les versions antérieures à Instana collector 4.0, vous devez lancer manuellement une séquence d'enregistrement au début d'une tâche et la clôturer une fois la tâche terminée. La présence d'une travée d'entrée active garantit que toutes les travées de sortie et les travées intermédiaires sont capturées.

Vous pouvez également créer une demande de fonction pour implémenter la prise en charge prête à l'emploi pour le type de déclencheur que vous utilisez.

Restauration manuelle du contexte asynchrone

Instana s'appuie en interne sur async_hooks pour conserver le contexte asynchrone. Cela fonctionne de façon automatique et transparente, sans nécessiter d'intervention manuelle. Cependant, il existe des bibliothèques et des modèles de code qui peuvent interrompre la continuité des hooks async_hooks et entraîner la perte du contexte asynchrone. Certains d'entre eux figurent dans cette liste de problèmes. L'utilisation d'un module de ce type peut susciter des traces incomplètes et des appels manquants.

Le kit SDK Node.js d'Instana fournit une API pour y remédier. Notez que l'utilisation de cette API n'est presque jamais requise. Vous ne devez y recourir que si vous connaissez exactement la ligne de code à l'origine des appels manquants ainsi que la cause.

  • instana.sdk.getAsyncContext() : renvoie le contexte asynchrone actif. Appelez cette méthode avant le code qui rompt la continuité asynchrone. (Depuis la version 1.100.0)
  • instana.sdk.runInAsyncContext(context, callback) : exécute l'objet callback dans le contexte asynchrone indiqué. L'objet context aurait dû être obtenu en appelant instana.sdk.getAsyncContext(). Ecapsulez le code censé s'exécuter dans ce contexte dans l'objet callback. (Depuis la version 1.100.0)
  • instana.sdk.runPromiseInAsyncContext(context, createPromiseFn) : exécute un objet promise dans le contexte asynchrone indiqué. L'objet context aurait dû être obtenu en appelant instana.sdk.getAsyncContext(). La fonction createPromiseFn doit renvoyer l'objet promise que vous souhaitez exécuter dans ce contexte. (Depuis la version 1.100.0)

Voici un exemple de code pour runInAsyncContext pour illustrer son utilisation :

  // ...

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

      // ...
    });
  });
 

Voici un exemple de code pour 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 */}
    );
  });
 

Intégration OpenTracing

Ce package implémente également l'API OpenTracing. Pour utiliser OpenTracing pour Node.js avec Instana, vous devez désactiver le traçage automatique et utiliser l'implémentation de Instana OpenTracing API. L'exemple de projet suivant en est l'illustration :

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

Limitations

  • OpenTracing n'est pas intégré au traçage automatique d'Instana. En particulier, les étendues créées à l'aide de l'API OpenTracing ne feront pas partie de la même trace que celles créées par notre instrumentation de traçage automatique. Si vous souhaitez ajouter des balises « span » supplémentaires à celles créées automatiquement, il est préférable d'utiliser le SDK plutôt que OpenTracing. En fait, nous vous recommandons d'utiliser le traçage automatique (avec éventuellement des étendues SDK) ou OpenTracing, mais pas les deux dans une application.
  • Le collecteur Instana Node.js ne prend pas en charge les objets binary carrier d'OpenTracing. Cette implémentation OpenTracing ignore silencieusement les objets binary carrier d'OpenTracing.
  • Il faut également être prudent avec les objet baggage d'OpenTracing. Les éléments baggage sont des métadonnées transportées par des objets transporteurs au-delà des limites du réseau. De plus, ces métadonnées sont héritées par les étendues enfant (et leurs étendues enfant ...). Cela peut entraîner des surcharges. Nous vous recommandons d'éviter complètement l'API baggage d'OpenTracing.