> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom instrumentation for Node.js

<Note>
  🛟 Don't hesitate to [contact us](mailto:support@appsignal.com) if you run into
  any issues while implementing custom instrumentations. We're here to help!
</Note>

Including custom instrumentation in your application can be useful for identifying the specific lines of code causing performance problems. AppSignal provides [helpers](#helpers), which let you display relevant data about the state of your application alongside performance metrics to assist you in identifying the root causes of any performance problems your application may be experiencing.

## Setup

To use AppSignal's helper functions in your instrumentation, you must first [import `opentelemetry` and define a tracer object](#import-opentelemetry-and-define-tracer). Depending on your instrumentation, you may also need to [create additional spans](#spans).

Our [Example Use Cases](#example-use-cases) demonstrate how you can use tracers, spans, and helpers to create your custom instrumentation.

### Import OpenTelemetry and Define Tracer

The AppSignal integration for Node.js uses OpenTelemetry tracer objects. These tracers contain various functions for creating custom instrumentations.

The Tracer exposes functions for creating and creating new spans. This documentation will outline how you can use tracers and spans to implement your custom integration.

You must first import the trace object from `@opentelemetry/api` before working with tracers and spans. By invoking the `getTracer` function on the trace object a new tracer object can be created. You must give your tracer object a name in this function, as seen in the example below, where the `getTracer` function is used to define a tracer with the name `"my interesting app"`.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { trace } from "@opentelemetry/api";

  const tracer = trace.getTracer("my-interesting-app");
  ```
</CodeGroup>

### Spans

A span is the name of the object that we use to capture data about the performance of your application, any errors and any surrounding context. A span forms part of a broader trace, a hierarchical representation of the flow of data through your application. Spans keep track of the start and end time of an event, along with other information, such as the name or other data related to it.

You can read more about spans in the [OpenTelemetry Tracing Documentation](https://opentelemetry.io/docs/instrumentation/js/api/tracing/).

### Creating an Active Span

New spans can be invoked via the [trace object](#import-opentelemetry-and-define-tracer). Code instrumented inside an Express of Koa handler will already be inside a span. You can create new spans by invoking the`startActiveSpan()` function on the tracer object. Newly created spans will be the child of the span they were created in, if one exists. You can use child spans to measure the performance of tasks executed from within a parent span.

You should give your span a name that makes its purpose clear. Execute all of the tasks you wish to monitor within the `startActiveSpan()` function, like in the below example.

<CodeGroup>
  ```javascript Node.js theme={null}
  tracer.startActiveSpan("printing coffee beans", async (span) => {
    const coffeeBeans = await fetchAllCoffeeBeans();
    console.log(coffeeBeans);

    span.end();
  });
  ```
</CodeGroup>

After the task is complete, you must close the span: `span.end()`

### Example Use Cases

When implementing custom instrumentation, you may be curious to understand the behavior of particular functions, for example, how long it takes for them to execute.

#### Using Helpers

In the below example, we are curious about the performance of our "order-coffee" GET endpoint for different roasts of coffee. To investigate this, we use the `setAttribute` function to create a [tag](#tag) called flavor, the value we retrieve from request parameters.

**Note:** Because this is inside an Express request handler, a root span has already been created.

<CodeGroup>
  ```javascript Node.js theme={null}
  app.get('/order-coffee', (req, res) => {
      const roast = req.params.roast
      setTag("roast", roast)

      const coffeeBeans = pickCoffeeBeans(roast)
      prepareCoffeeBag(coffeeBeans)
    })
  }
  ```
</CodeGroup>

To get deeper insights into how our code is performing, we can use child spans to inspect functions called within our Express handler.

The code below creates a child span of the `"picking coffee beans"` span defined in the `pickCoffeeBeans` function.

Once the attribute has been assigned and all functions executed, we `.end()` the span to ensure AppSignal receives the start and finish time and attributes we've assigned:

<CodeGroup>
  ```javascript Node.js theme={null}
  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();
    });
  }
  ```
</CodeGroup>

In AppSignal, we can see performance data for this function. We can use the roast and batch tags to filter the data and gain greater insights into what parameters potentially influence how our application's code behaves.

<img src="https://mintcdn.com/appsignal-715f5a51/4TRZP0Sq9Zq7PAPW/assets/images/screenshots/node/node-instrumentation-sample.png?fit=max&auto=format&n=4TRZP0Sq9Zq7PAPW&q=85&s=4afd1219ca91a383ca1519c779eca87b" alt="Screen shot of tags" width="933" height="329" data-path="assets/images/screenshots/node/node-instrumentation-sample.png" />

#### Active Spans

While tags are helpful to analyse performance differences on the same function, it does not give us insight into the performance of any functions called from within our function.

To give us greater insights into what's happening inside of the `pickCoffeeBeans()`, we create a new `activeSpan` and name it "picking coffee beans". All logic we wish to track is executed from within an async function.

We `await` the promise returned by `retrieveDrinkTypes()`, so that we can track it's performance as a child span of the `"picking coffee beans"` span we created in `pickCoffeeBeans()`.

<CodeGroup>
  ```javascript Node.js theme={null}
  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);
    });
  }
  ```
</CodeGroup>

With this instrumentation, AppSignal will provide insights into the performance of the `pickCoffeeBeans()`, which will include the performance data of the `retrieveDrinkTypes()` function, providing greater insight into what factors are impacting the overall performance of a function.

## Helpers

<Warning>
  Data sent to AppSignal must not contain any personal data, such as names,
  email addresses, etc. It is your responsibility to ensure that your
  application's data is sanatized before being forwarded to AppSignal. When
  identifying a person is necessary your application must use alternative forms
  of identification such as a user ID, hash, or pseudonym.
</Warning>

To use helper functions, you must first import them from `@appsignal/nodejs`. In the example below the namespace the code is being executed in is being reported to AppSignal. All available helper functions are outlined in the [below documentation](/#helper-functions).

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setNamespace } from "@appsignal/nodejs";

  setNamespace("web");
  ```
</CodeGroup>

All available helper functions for custom instrumentation attributes are outlined in the [list below](#helper-functions).

### Helper Functions

<Tip>
  The code snippets for the helpers below assume your code is already being
  instrumented (for example, inside an Express or Koa request handler). If your
  code is not already instrumented, you must [create a span](#spans) and use the
  helpers inside of it.
</Tip>

* [Namespace](#namespace)
* [Tag](#tag)
* [Request Parameters](#request-parameters)
* [Set Session Data](#set-session-data)
* [Request Headers](#request-headers)
* [Root Name](#root-name)
* [Custom Data](#custom-data)
* **[Child Span Helpers:](#child-span-helpers)**
  * [Category](#category)
  * [Name](#name)
  * [Body](#body)

### Namespace

Sets the string value of the root span namespace.

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { setNamespace } from "@appsignal/nodejs";
  setNamespace("app");
  ```
</CodeGroup>

### Tag

Sets a tag, for example from a request parameter, which can be used as a filter from within the AppSignal application. In the below example we create a tag called color with the value blue. You can configure tags with names relevant to the context of your application.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setTag } from "@appsignal/nodejs";
  setTag("color", "blue");
  ```
</CodeGroup>

### Request Parameters

An object that is serializable to JSON. Incoming request parameters request body and query parameters.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setParams } from "@appsignal/nodejs";

  const exampleParams = { action: "delete" };
  setParams(exampleParams);
  ```
</CodeGroup>

### Set Session Data

An object that is serializable to JSON.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setSessionData } from "@appsignal/nodejs";

  const exampleSessionData = { locale: "en-GB" };
  setSessionData(exampleSessionData);
  ```
</CodeGroup>

### Request Headers

A string containing the header value.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setHeader } from "@appsignal/nodejs";
  setHeader("Content-type", "application/json");
  ```
</CodeGroup>

### Root Name

Allows you to set the name of the trace. Samples are grouped into actions by their trace name.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setRootName } from "@appsignal/nodejs";

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

#### Example Use Case

Your application has an endpoint called `GET /coffee`.

<CodeGroup>
  ```javascript Node.js theme={null}
  app.get("/coffee", (req, res) => {
    // ...
  });
  ```
</CodeGroup>

All requests to this endpoint will generate samples called `GET /coffee`, but your endpoint handles multiple actions: `coffee?action=buy`, and `coffee?action=sell`.

<img src="https://mintcdn.com/appsignal-715f5a51/4TRZP0Sq9Zq7PAPW/assets/images/screenshots/node/span-without-root-name.png?fit=max&auto=format&n=4TRZP0Sq9Zq7PAPW&q=85&s=c17dd17713da423bac8d380df6274a1e" alt="Span Without Root Name" width="1030" height="640" data-path="assets/images/screenshots/node/span-without-root-name.png" />

While they all use the `GET /coffee` endpoint, they are conceptually very different and so it would make sense for them to be grouped separately in AppSignal rather than in the same `GET /coffee` sample. To do this, you can use the `setRootName()` helper:

<CodeGroup>
  ```javascript Node.js theme={null}
  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
    }
  });
  ```
</CodeGroup>

Using `setRootName` will change the name of the root span, grouping the samples for the requests `coffee?action=buy` and `coffee?action=sell` into separate actions:

<img src="https://mintcdn.com/appsignal-715f5a51/4TRZP0Sq9Zq7PAPW/assets/images/screenshots/node/root-name-span.png?fit=max&auto=format&n=4TRZP0Sq9Zq7PAPW&q=85&s=2d6521845df3d5bcd8938900b4b643ac" alt="Root Name Span" width="1052" height="780" data-path="assets/images/screenshots/node/root-name-span.png" />

### Custom Data

An object that is serializable to JSON.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setCustomData } from "@appsignal/nodejs";

  const exampleCustomData = { stroopwaffle: "true", coffee: "false" };
  setCustomData(exampleCustomData);
  ```
</CodeGroup>

### Child Span Helpers

The following helpers only apply to child spans. To create a child span, you must create a new [Active Span](#creating-an-active-span).

New spans are automatically children of their parent span.

#### Category

A string containing the child span category. The name should use `.` to express the category's hierarchical inheritance. For example: `cafe.coffee.cupsize`

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setCategory } from "@appsignal/nodejs";
  setCategory("category.name");
  ```
</CodeGroup>

#### Name

A string containing the child span event timeline title.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setName } from "@appsignal/nodejs";
  setName("Users query");
  ```
</CodeGroup>

#### Body

<Warning>
  🔐 Do not send <strong>Personal Identifiable Information (PII)</strong> to AppSignal. Filter PII (e.g., names, emails) and use an ID, hash, or pseudonymized identifier instead. <br /> <br /> For <strong>HIPAA-covered entities</strong>, more info on signing a Business Associate Agreement (BAA) is available in our <a href="/support/business-add-ons">Business Add-Ons documentation</a>.
</Warning>

The span's body can include additional information about the event, like the HTTP request, the connected host, etc. Be sure to sanitize the information before adding it to the span so no Personal Identifiable Information is sent to AppSignal. This information will be visible for the span when hovering over the event timeline.

To store SQL queries in the span's body, please use the [`setSqlBody` helper](#sql-body) instead.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setBody } from "@appsignal/nodejs";
  setBody("Span body");
  ```
</CodeGroup>

#### SQL body

<Tip>
  Available since Node.js package 3.0.25.
</Tip>

Set a SQL query as the body of the span as it appears in the performance event timeline in the incident sample detail view. This is similar to the [`setBody` helper](#body), but is specialized for SQL queries. Any SQL query set as the body with this attribute will be sanitized to avoid sending PII (Personal Identifiable Information) data to our servers.

See the [`setBody` helper](#body) for more details on how the body attribute works.

When both the `setBody` and `setSqlBody` helpers are called on the same span, the `setSqlBody` helper's value is leading and the `setBody` helper's value will be ignored.

<CodeGroup>
  ```javascript Node.js theme={null}
  import { setSqlBody } from "@appsignal/nodejs";
  setSqlBody("SELECT * FROM users");
  ```
</CodeGroup>
