Passer au contenu principal
🛟 N’hésitez pas à nous contacter si vous rencontrez des problèmes lors de la mise en œuvre d’instrumentations personnalisées. Nous sommes là pour vous aider !
L’inclusion d’une instrumentation personnalisée dans votre application peut être utile pour identifier les lignes de code spécifiques qui causent des problèmes de performance. AppSignal fournit des helpers, qui vous permettent d’afficher des données pertinentes sur l’état de votre application aux côtés des métriques de performance, afin de vous aider à identifier les causes profondes de tout problème de performance que votre application pourrait rencontrer.

Configuration

Pour utiliser les fonctions helper d’AppSignal dans votre instrumentation, vous devez d’abord importer opentelemetry et définir un objet tracer. Selon votre instrumentation, vous devrez peut-être également créer des spans supplémentaires. Nos exemples de cas d’usage montrent comment vous pouvez utiliser les tracers, les spans et les helpers pour créer votre instrumentation personnalisée.

Importer OpenTelemetry et définir un Tracer

L’intégration AppSignal pour Node.js utilise les objets tracer d’OpenTelemetry. Ces tracers contiennent diverses fonctions pour créer des instrumentations personnalisées. Le Tracer expose des fonctions pour créer de nouveaux spans. Cette documentation décrit comment vous pouvez utiliser les tracers et les spans pour implémenter votre intégration personnalisée. Vous devez d’abord importer l’objet trace depuis @opentelemetry/api avant de travailler avec les tracers et les spans. En invoquant la fonction getTracer sur l’objet trace, un nouvel objet tracer peut être créé. Vous devez donner un nom à votre objet tracer dans cette fonction, comme dans l’exemple ci-dessous, où la fonction getTracer est utilisée pour définir un tracer avec le nom "my interesting app".
import { trace } from "@opentelemetry/api";

const tracer = trace.getTracer("my-interesting-app");

Spans

Un span est le nom de l’objet que nous utilisons pour capturer des données sur les performances de votre application, les éventuelles erreurs et le contexte qui les entoure. Un span fait partie d’une trace plus large, une représentation hiérarchique du flux de données à travers votre application. Les spans gardent la trace de l’heure de début et de fin d’un événement, ainsi que d’autres informations, telles que le nom ou d’autres données qui s’y rapportent. Vous pouvez en savoir plus sur les spans dans la documentation de traçage OpenTelemetry.

Créer un span actif

De nouveaux spans peuvent être invoqués via l’objet trace. Le code instrumenté à l’intérieur d’un handler Express ou Koa sera déjà à l’intérieur d’un span. Vous pouvez créer de nouveaux spans en invoquant la fonction startActiveSpan() sur l’objet tracer. Les spans nouvellement créés seront l’enfant du span dans lequel ils ont été créés, s’il en existe un. Vous pouvez utiliser des spans enfants pour mesurer les performances des tâches exécutées depuis un span parent. Vous devez donner à votre span un nom qui rend son objectif clair. Exécutez toutes les tâches que vous souhaitez surveiller dans la fonction startActiveSpan(), comme dans l’exemple ci-dessous.
tracer.startActiveSpan("printing coffee beans", async (span) => {
  const coffeeBeans = await fetchAllCoffeeBeans();
  console.log(coffeeBeans);

  span.end();
});
Une fois la tâche terminée, vous devez fermer le span : span.end()

Exemples de cas d’usage

Lors de la mise en œuvre d’une instrumentation personnalisée, vous pouvez être curieux de comprendre le comportement de fonctions particulières, par exemple combien de temps elles mettent à s’exécuter.

Utilisation des helpers

Dans l’exemple ci-dessous, nous nous intéressons aux performances de notre endpoint GET « order-coffee » pour différentes torréfactions de café. Pour étudier cela, nous utilisons la fonction setAttribute pour créer un tag appelé flavor, dont la valeur est récupérée depuis les paramètres de la requête. Note : étant donné qu’il s’agit d’un handler de requête Express, un span racine a déjà été créé.
app.get('/order-coffee', (req, res) => {
    const roast = req.params.roast
    setTag("roast", roast)

    const coffeeBeans = pickCoffeeBeans(roast)
    prepareCoffeeBag(coffeeBeans)
  })
}
Pour obtenir des informations plus approfondies sur les performances de notre code, nous pouvons utiliser des spans enfants pour inspecter les fonctions appelées dans notre handler Express. Le code ci-dessous crée un span enfant du span "picking coffee beans" défini dans la fonction pickCoffeeBeans. Une fois l’attribut assigné et toutes les fonctions exécutées, nous appelons .end() sur le span pour nous assurer qu’AppSignal reçoit l’heure de début et de fin ainsi que les attributs que nous avons assignés :
function pickCoffeeBeans(roast) {
  tracer.startActiveSpan("picking coffee beans", async (span) => {
    const roastBrands = {
      light: "Caffinated Cloud",
      medium: "Feeling The Buzz",
      dark: "The Jitters",
    };

    const roastBrand = roastBrands[roast];

    setTag("brand", roastBrand);
    await retrieveDrinkTypes(roastBrand);
    span.end();
  });
}
Dans AppSignal, nous pouvons voir les données de performance pour cette fonction. Nous pouvons utiliser les tags roast et batch pour filtrer les données et obtenir des informations plus approfondies sur les paramètres qui pourraient influencer le comportement du code de notre application. Capture d'écran des tags

Spans actifs

Bien que les tags soient utiles pour analyser les différences de performance sur une même fonction, ils ne nous donnent pas d’aperçu des performances des fonctions appelées depuis notre fonction. Pour obtenir plus d’informations sur ce qui se passe à l’intérieur de pickCoffeeBeans(), nous créons un nouveau activeSpan et le nommons « picking coffee beans ». Toute la logique que nous souhaitons suivre est exécutée à l’intérieur d’une fonction async. Nous attendons (await) la promesse retournée par retrieveDrinkTypes(), afin de pouvoir suivre ses performances en tant que span enfant du span "picking coffee beans" que nous avons créé dans pickCoffeeBeans().
function pickCoffeeBeans(roast) {
  tracer.startActiveSpan("picking coffee beans", async (span) => {
    const roastBrands = {
      light: "Caffinated Cloud",
      medium: "Feeling The Buzz",
      dark: "The Jitters",
    };

    const roastBrand = roastBrands[roast];
    await retrieveDrinkTypes(roastBrand);
    span.end();
  });
}

function retrieveDrinkTypes(roastBrand) {
  return new Promise((resolve) => {
    const span = tracer.startActiveSpan("Fetching coffee types");

    const brandDrinks = {
      "Caffinated Cloud": ["americano", "capuccino"],
      "Feeling The Buzz": ["capuccino", "latte"],
      "The Jitters": ["espresso"],
    };

    const drinkTypes = brandDrinks[roastBrand];

    // we want to give our Barista's some time prepare the machine
    setTimeout(() => {
      resolve(drinkTypes);
      span.end();
    }, 60000);
  });
}
Avec cette instrumentation, AppSignal fournira des informations sur les performances de pickCoffeeBeans(), ce qui inclura les données de performance de la fonction retrieveDrinkTypes(), offrant ainsi une meilleure compréhension des facteurs qui impactent les performances globales d’une fonction.

Helpers

Les données envoyées à AppSignal ne doivent contenir aucune donnée personnelle, telle que des noms, des adresses e-mail, etc. Il est de votre responsabilité de vous assurer que les données de votre application sont assainies avant d’être transmises à AppSignal. Lorsqu’il est nécessaire d’identifier une personne, votre application doit utiliser d’autres formes d’identification telles qu’un identifiant utilisateur, un hash ou un pseudonyme.
Pour utiliser les fonctions helper, vous devez d’abord les importer depuis @appsignal/nodejs. Dans l’exemple ci-dessous, le namespace dans lequel le code est exécuté est signalé à AppSignal. Toutes les fonctions helper disponibles sont décrites dans la documentation ci-dessous.
import { setNamespace } from "@appsignal/nodejs";

setNamespace("web");
Toutes les fonctions helper disponibles pour les attributs d’instrumentation personnalisée sont décrites dans la liste ci-dessous.

Fonctions helper

Les extraits de code pour les helpers ci-dessous supposent que votre code est déjà instrumenté (par exemple, à l’intérieur d’un handler de requête Express ou Koa). Si votre code n’est pas déjà instrumenté, vous devez créer un span et utiliser les helpers à l’intérieur de celui-ci.

Namespace

Définit la valeur de chaîne du namespace du span racine.
import { setNamespace } from "@appsignal/nodejs";
setNamespace("app");

Tag

Définit un tag, par exemple à partir d’un paramètre de requête, qui peut être utilisé comme filtre dans l’application AppSignal. Dans l’exemple ci-dessous, nous créons un tag appelé color avec la valeur blue. Vous pouvez configurer les tags avec des noms pertinents pour le contexte de votre application.
import { setTag } from "@appsignal/nodejs";
setTag("color", "blue");

Paramètres de requête

Un objet sérialisable en JSON. Les paramètres de requête entrants, le corps de la requête et les paramètres de requête.
import { setParams } from "@appsignal/nodejs";

const exampleParams = { action: "delete" };
setParams(exampleParams);

Définir les données de session

Un objet sérialisable en JSON.
import { setSessionData } from "@appsignal/nodejs";

const exampleSessionData = { locale: "en-GB" };
setSessionData(exampleSessionData);

En-têtes de requête

Une chaîne contenant la valeur de l’en-tête.
import { setHeader } from "@appsignal/nodejs";
setHeader("Content-type", "application/json");

Nom racine

Vous permet de définir le nom de la trace. Les échantillons sont regroupés en actions en fonction de leur nom de trace.
import { setRootName } from "@appsignal/nodejs";

// somewhere in your code where there's an active span...
setRootName("The action name");

Exemple de cas d’usage

Votre application a un endpoint appelé GET /coffee.
app.get("/coffee", (req, res) => {
  // ...
});
Toutes les requêtes vers cet endpoint généreront des échantillons appelés GET /coffee, mais votre endpoint gère plusieurs actions : coffee?action=buy et coffee?action=sell. Span sans nom racine Bien qu’elles utilisent toutes l’endpoint GET /coffee, elles sont conceptuellement très différentes et il serait donc logique qu’elles soient regroupées séparément dans AppSignal plutôt que dans le même échantillon GET /coffee. Pour ce faire, vous pouvez utiliser le helper setRootName() :
import { setRootName } from "@appsignal/nodejs";

app.get("/coffee", (req, res) => {
  if (req.query.action === "buy") {
    setRootName("Buy coffee");
    // ... buy coffee
  } else if (req.query.action === "sell") {
    setRootName("Sell coffee");
    // ... sell coffee
  }
});
L’utilisation de setRootName modifiera le nom du span racine, regroupant les échantillons des requêtes coffee?action=buy et coffee?action=sell dans des actions distinctes : Span avec nom racine

Données personnalisées

Un objet sérialisable en JSON.
import { setCustomData } from "@appsignal/nodejs";

const exampleCustomData = { stroopwaffle: "true", coffee: "false" };
setCustomData(exampleCustomData);

Helpers de span enfant

Les helpers suivants ne s’appliquent qu’aux spans enfants. Pour créer un span enfant, vous devez créer un nouveau span actif. Les nouveaux spans sont automatiquement enfants de leur span parent.

Catégorie

Une chaîne contenant la catégorie du span enfant. Le nom doit utiliser . pour exprimer l’héritage hiérarchique de la catégorie. Par exemple : cafe.coffee.cupsize
import { setCategory } from "@appsignal/nodejs";
setCategory("category.name");

Nom

Une chaîne contenant le titre de la chronologie d’événements du span enfant.
import { setName } from "@appsignal/nodejs";
setName("Users query");

Corps

🔐 N’envoyez pas d’informations personnelles identifiables (PII) à AppSignal. Filtrez les PII (par exemple, noms, e-mails) et utilisez un identifiant, un hash ou un identifiant pseudonymisé à la place.

Pour les entités couvertes par la HIPAA, plus d’informations sur la signature d’un Business Associate Agreement (BAA) sont disponibles dans notre documentation Business Add-Ons.
Le corps du span peut inclure des informations supplémentaires sur l’événement, comme la requête HTTP, l’hôte connecté, etc. Veillez à assainir les informations avant de les ajouter au span afin qu’aucune information personnelle identifiable ne soit envoyée à AppSignal. Ces informations seront visibles pour le span lors du survol de la chronologie d’événements. Pour stocker des requêtes SQL dans le corps du span, veuillez utiliser le helper setSqlBody à la place.
import { setBody } from "@appsignal/nodejs";
setBody("Span body");

Corps SQL

Disponible depuis le package Node.js 3.0.25.
Définir une requête SQL comme corps du span tel qu’elle apparaît dans la chronologie des événements de performance dans la vue détaillée d’un échantillon d’incident. Ceci est similaire au helper setBody, mais spécialisé pour les requêtes SQL. Toute requête SQL définie comme corps avec cet attribut sera assainie pour éviter d’envoyer des données PII (Personal Identifiable Information) à nos serveurs. Consultez le helper setBody pour plus de détails sur le fonctionnement de l’attribut body. Lorsque les helpers setBody et setSqlBody sont appelés sur le même span, la valeur du helper setSqlBody prévaut et la valeur du helper setBody sera ignorée.
import { setSqlBody } from "@appsignal/nodejs";
setSqlBody("SELECT * FROM users");