Zum Hauptinhalt springen
🛟 Zögern Sie nicht, uns zu kontaktieren, falls Sie beim Implementieren benutzerdefinierter Instrumentierungen auf Probleme stoßen. Wir helfen Ihnen gerne!
Die Einbeziehung benutzerdefinierter Instrumentierung in Ihre Anwendung kann nützlich sein, um die spezifischen Codezeilen zu identifizieren, die Performance-Probleme verursachen. AppSignal bietet Helper, mit denen Sie relevante Daten über den Zustand Ihrer Anwendung neben Performance-Metriken anzeigen können, um die Ursachen von Performance-Problemen Ihrer Anwendung zu identifizieren.

Einrichtung

Um die Hilfsfunktionen von AppSignal in Ihrer Instrumentierung zu verwenden, müssen Sie zuerst opentelemetry importieren und ein Tracer-Objekt definieren. Abhängig von Ihrer Instrumentierung müssen Sie möglicherweise auch zusätzliche Spans erstellen. Unsere Beispiel-Anwendungsfälle zeigen, wie Sie Tracer, Spans und Helper verwenden können, um Ihre benutzerdefinierte Instrumentierung zu erstellen.

OpenTelemetry importieren und Tracer definieren

Die AppSignal-Integration für Node.js verwendet OpenTelemetry-Tracer-Objekte. Diese Tracer enthalten verschiedene Funktionen zum Erstellen benutzerdefinierter Instrumentierungen. Der Tracer stellt Funktionen zum Erstellen und Erzeugen neuer Spans bereit. Diese Dokumentation beschreibt, wie Sie Tracer und Spans verwenden können, um Ihre benutzerdefinierte Integration zu implementieren. Sie müssen zuerst das trace-Objekt aus @opentelemetry/api importieren, bevor Sie mit Tracern und Spans arbeiten können. Durch den Aufruf der Funktion getTracer am trace-Objekt kann ein neues Tracer-Objekt erstellt werden. Sie müssen Ihrem Tracer-Objekt in dieser Funktion einen Namen geben, wie im untenstehenden Beispiel, in dem die Funktion getTracer verwendet wird, um einen Tracer mit dem Namen "my interesting app" zu definieren.
import { trace } from "@opentelemetry/api";

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

Spans

Ein Span ist der Name des Objekts, das wir verwenden, um Daten über die Performance Ihrer Anwendung, etwaige Fehler und den umgebenden Kontext zu erfassen. Ein Span ist Teil eines umfassenderen Trace, einer hierarchischen Darstellung des Datenflusses durch Ihre Anwendung. Spans verfolgen die Start- und Endzeit eines Ereignisses sowie weitere Informationen wie den Namen oder andere damit zusammenhängende Daten. Mehr über Spans erfahren Sie in der OpenTelemetry Tracing Dokumentation.

Einen aktiven Span erstellen

Neue Spans können über das trace-Objekt aufgerufen werden. In einem Express- oder Koa-Handler instrumentierter Code befindet sich bereits in einem Span. Sie können neue Spans erstellen, indem Sie die Funktion startActiveSpan() am Tracer-Objekt aufrufen. Neu erstellte Spans sind die Kinder des Spans, in dem sie erstellt wurden, sofern einer existiert. Sie können Child-Spans verwenden, um die Performance von Aufgaben zu messen, die innerhalb eines Parent-Spans ausgeführt werden. Sie sollten Ihrem Span einen Namen geben, der seinen Zweck verdeutlicht. Führen Sie alle Aufgaben, die Sie überwachen möchten, innerhalb der Funktion startActiveSpan() aus, wie im folgenden Beispiel.
tracer.startActiveSpan("printing coffee beans", async (span) => {
  const coffeeBeans = await fetchAllCoffeeBeans();
  console.log(coffeeBeans);

  span.end();
});
Nach Abschluss der Aufgabe müssen Sie den Span schließen: span.end()

Beispiel-Anwendungsfälle

Bei der Implementierung benutzerdefinierter Instrumentierung möchten Sie möglicherweise das Verhalten bestimmter Funktionen verstehen, z. B. wie lange ihre Ausführung dauert.

Helper verwenden

Im folgenden Beispiel sind wir an der Performance unseres „order-coffee”-GET-Endpunkts für verschiedene Kaffeeröstungen interessiert. Um dies zu untersuchen, verwenden wir die Funktion setAttribute, um einen Tag namens flavor zu erstellen, dessen Wert wir aus den Request-Parametern abrufen. Hinweis: Da dies innerhalb eines Express-Request-Handlers geschieht, wurde bereits ein Root-Span erstellt.
app.get('/order-coffee', (req, res) => {
    const roast = req.params.roast
    setTag("roast", roast)

    const coffeeBeans = pickCoffeeBeans(roast)
    prepareCoffeeBag(coffeeBeans)
  })
}
Um tiefere Einblicke in die Performance unseres Codes zu erhalten, können wir Child-Spans verwenden, um Funktionen zu untersuchen, die innerhalb unseres Express-Handlers aufgerufen werden. Der folgende Code erstellt einen Child-Span des "picking coffee beans"-Spans, der in der Funktion pickCoffeeBeans definiert ist. Sobald das Attribut zugewiesen und alle Funktionen ausgeführt wurden, beenden wir den Span mit .end(), um sicherzustellen, dass AppSignal die Start- und Endzeit sowie die zugewiesenen Attribute erhält:
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();
  });
}
In AppSignal können wir Performance-Daten für diese Funktion sehen. Wir können die Tags roast und batch verwenden, um die Daten zu filtern und tiefere Einblicke zu erhalten, welche Parameter potenziell beeinflussen, wie sich der Code unserer Anwendung verhält. Screenshot der Tags

Aktive Spans

Während Tags hilfreich sind, um Performance-Unterschiede in derselben Funktion zu analysieren, geben sie uns keinen Einblick in die Performance von Funktionen, die innerhalb unserer Funktion aufgerufen werden. Um uns tiefere Einblicke in das zu geben, was innerhalb von pickCoffeeBeans() passiert, erstellen wir einen neuen activeSpan und nennen ihn „picking coffee beans”. Die gesamte Logik, die wir verfolgen möchten, wird innerhalb einer async-Funktion ausgeführt. Wir await das Promise, das von retrieveDrinkTypes() zurückgegeben wird, damit wir seine Performance als Child-Span des "picking coffee beans"-Spans verfolgen können, den wir in pickCoffeeBeans() erstellt haben.
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);
  });
}
Mit dieser Instrumentierung liefert AppSignal Einblicke in die Performance von pickCoffeeBeans(), einschließlich der Performance-Daten der Funktion retrieveDrinkTypes(), was tiefere Einblicke in die Faktoren bietet, die die Gesamt-Performance einer Funktion beeinflussen.

Helper

Daten, die an AppSignal gesendet werden, dürfen keine personenbezogenen Daten wie Namen, E-Mail-Adressen usw. enthalten. Es liegt in Ihrer Verantwortung sicherzustellen, dass die Daten Ihrer Anwendung bereinigt werden, bevor sie an AppSignal weitergeleitet werden. Wenn die Identifizierung einer Person erforderlich ist, muss Ihre Anwendung alternative Identifikationsformen wie eine Benutzer-ID, einen Hash oder ein Pseudonym verwenden.
Um Hilfsfunktionen zu verwenden, müssen Sie diese zuerst aus @appsignal/nodejs importieren. Im folgenden Beispiel wird der Namespace, in dem der Code ausgeführt wird, an AppSignal gemeldet. Alle verfügbaren Hilfsfunktionen sind in der folgenden Dokumentation aufgeführt.
import { setNamespace } from "@appsignal/nodejs";

setNamespace("web");
Alle verfügbaren Hilfsfunktionen für benutzerdefinierte Instrumentierungs-Attribute sind in der Liste unten aufgeführt.

Hilfsfunktionen

Die folgenden Code-Snippets für die Helper gehen davon aus, dass Ihr Code bereits instrumentiert wird (z. B. innerhalb eines Express- oder Koa-Request-Handlers). Wenn Ihr Code noch nicht instrumentiert ist, müssen Sie einen Span erstellen und die Helper darin verwenden.

Namespace

Setzt den String-Wert des Namespace des Root-Spans.
import { setNamespace } from "@appsignal/nodejs";
setNamespace("app");

Tag

Setzt einen Tag, z. B. aus einem Request-Parameter, der als Filter innerhalb der AppSignal-Anwendung verwendet werden kann. Im folgenden Beispiel erstellen wir einen Tag namens color mit dem Wert blue. Sie können Tags mit Namen konfigurieren, die für den Kontext Ihrer Anwendung relevant sind.
import { setTag } from "@appsignal/nodejs";
setTag("color", "blue");

Request-Parameter

Ein Objekt, das nach JSON serialisierbar ist. Eingehende Request-Parameter, Request-Body und Query-Parameter.
import { setParams } from "@appsignal/nodejs";

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

Session-Daten setzen

Ein Objekt, das nach JSON serialisierbar ist.
import { setSessionData } from "@appsignal/nodejs";

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

Request-Header

Ein String, der den Header-Wert enthält.
import { setHeader } from "@appsignal/nodejs";
setHeader("Content-type", "application/json");

Root Name

Ermöglicht es Ihnen, den Namen des Trace festzulegen. Samples werden anhand ihres Trace-Namens in Actions gruppiert.
import { setRootName } from "@appsignal/nodejs";

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

Beispiel-Anwendungsfall

Ihre Anwendung hat einen Endpunkt namens GET /coffee.
app.get("/coffee", (req, res) => {
  // ...
});
Alle Anfragen an diesen Endpunkt generieren Samples namens GET /coffee, aber Ihr Endpunkt verarbeitet mehrere Actions: coffee?action=buy und coffee?action=sell. Span ohne Root Name Obwohl alle den Endpunkt GET /coffee verwenden, sind sie konzeptionell sehr unterschiedlich, daher wäre es sinnvoll, dass sie in AppSignal separat gruppiert werden, anstatt im selben GET /coffee-Sample. Dazu können Sie den Helper setRootName() verwenden:
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
  }
});
Die Verwendung von setRootName ändert den Namen des Root-Spans und gruppiert die Samples für die Anfragen coffee?action=buy und coffee?action=sell in separate Actions: Root Name Span

Custom Data

Ein Objekt, das nach JSON serialisierbar ist.
import { setCustomData } from "@appsignal/nodejs";

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

Child Span Helper

Die folgenden Helper gelten nur für Child-Spans. Um einen Child-Span zu erstellen, müssen Sie einen neuen Active Span erstellen. Neue Spans sind automatisch Kinder ihres Parent-Spans.

Kategorie

Ein String, der die Kategorie des Child-Spans enthält. Der Name sollte . verwenden, um die hierarchische Vererbung der Kategorie auszudrücken. Zum Beispiel: cafe.coffee.cupsize
import { setCategory } from "@appsignal/nodejs";
setCategory("category.name");

Name

Ein String, der den Titel des Event-Timelines des Child-Spans enthält.
import { setName } from "@appsignal/nodejs";
setName("Users query");

Body

🔐 Senden Sie keine personenbezogenen Daten (Personal Identifiable Information, PII) an AppSignal. Filtern Sie PII (z. B. Namen, E-Mail-Adressen) und verwenden Sie stattdessen eine ID, einen Hash oder einen pseudonymisierten Bezeichner.

Für HIPAA-pflichtige Stellen finden Sie weitere Informationen zum Abschluss eines Business Associate Agreement (BAA) in unserer Business Add-Ons-Dokumentation.
Der Body des Spans kann zusätzliche Informationen über das Ereignis enthalten, wie den HTTP-Request, den verbundenen Host usw. Stellen Sie sicher, dass Sie die Informationen bereinigen, bevor Sie sie dem Span hinzufügen, damit keine personenbezogenen Daten an AppSignal gesendet werden. Diese Informationen sind für den Span sichtbar, wenn Sie mit der Maus über die Event-Timeline fahren. Um SQL-Abfragen im Body des Spans zu speichern, verwenden Sie stattdessen den setSqlBody-Helper.
import { setBody } from "@appsignal/nodejs";
setBody("Span body");

SQL body

Verfügbar seit Node.js-Paket 3.0.25.
Legen Sie eine SQL-Abfrage als Body des Spans fest, wie er in der Performance-Event-Timeline in der Detailansicht des Incident-Samples erscheint. Dies ist ähnlich dem setBody-Helper, ist aber auf SQL-Abfragen spezialisiert. Jede SQL-Abfrage, die mit diesem Attribut als Body gesetzt wird, wird bereinigt, um zu vermeiden, dass PII (Personal Identifiable Information)-Daten an unsere Server gesendet werden. Weitere Informationen zur Funktionsweise des Body-Attributs finden Sie im setBody-Helper. Wenn sowohl die Helper setBody als auch setSqlBody für denselben Span aufgerufen werden, ist der Wert des setSqlBody-Helpers führend und der Wert des setBody-Helpers wird ignoriert.
import { setSqlBody } from "@appsignal/nodejs";
setSqlBody("SELECT * FROM users");