Advanced Features

Deploy Tracking

Scout can track deploys, making it easier to correlate specific deploys to changes in performance.

To ensure scout tracks your deploy, please provide the SCOUT_REVISION_SHA environment variable. You may also set the revisionSHA on a ScountConfiguration object instance:

const config = scout.buildScoutConfiguration({
    monitor: true,
    key: "<app key>",
    name: "<app name>",
    revisionSHA: "<sha>",
});

Request Queuing

Our Node integration can measure the time it takes a request to reach your application from farther upstream (a load balancer or web server). This appears in Scout as “Request Queueing” and provides an indication of your application’s capacity. Large request queueing time is an indication that your app needs more capacity.

Please view request queueing section to learn how to get these insights.

Custom Context

Context lets you see the key attributes of requests. For example, you can add custom context to answer critical questions like:

It’s simple to add custom context to your app:

// Express only: Add context inside a handler function
app.get("/", (req, req) => {
  scout.api.Context.add("Key", "Value"); // returns a Promise
})

Context Key Restrictions

The Context key must be a String with only printable characters. Custom context keys may contain alphanumeric characters, dashes, and underscores. Spaces are not allowed.

Attempts to add invalid context will be ignored.

Context Value Types

Context values can be any json-serializable type. Examples:

PII

To best help protect your data, we suggest using ids instead of explicit names and emails

Custom Instrumentation

You can extend Scout to trace transactions outside our officially supported libraries (e.g. Cron jobs and other web frameworks) and time the execution of sections of code that falls outside our provided instrumentation.

Asynchronous functionality can be marked as a transaction with code similar to the following:

scout.api.WebTransaction.run("transaction-name", (finishTransaction) => {
   yourAsyncFunction()
   .then(() => finishTransaction())
   .catch(err => {
    // error handling code goes here
    finishTransaction();
   });
});

For Asynchronous functionality in a callback-passing style:

scout.api.WebTransaction.run("transaction-name", (finishTransaction) => {
   yourCallbackStyleAsyncFunction((err) => {
    if (err) {
      // error handling code goes here
      return;
    }

    finishTransaction();
   });
});

Synchronous functionality can be marked as transactions with code similar to the following:

scout.api.WebTransaction.runSync("sync-transaction-name", () => {
  yourSyncFunction();
});

Transactions & Timing

Scout’s instrumentation is divided into 2 areas:

  1. Transactions: these wrap around an entire flow of work, like a web request or Cron job. The Scout Web UI groups data under transactions.
  2. Timing: these measure small pieces of code that occur inside of a transaction, like an HTTP request to an outside service, or a database call. This is displayed within a transaction trace in the UI.

Instrumenting Transactions

A transaction groups a sequence of work under in the Scout UI. These are used to generate transaction traces. For example, you may create a transaction that wraps around the entire execution of a NodeJS script that is ran as a Cron Job.

The Express integration does this all for you. You only will need to manually instrument transactions in special cases. Contact us at support@scoutapm.com for help.

Limits

We limit the number of unique transactions that can be instrumented. Tracking too many uniquely named transactions can impact the performance of the UI. Do not dynamically generate transaction names in your instrumentation as this can quickly exceed our rate limits. Use context to add high-dimensionality information instead.

Web or Background transactions?

Scout distinguishes between two types of transactions:

scout.api.WebTransaction.run("GET /users", () => { ... your code ... });
scout.api.BackgroundTransaction.run("your-bg-transaction", () => { ... your code ... });

Timing functions and blocks of code

In existing transactions, both automatically created with Express instruments, and also manually created, you can time sections of code that are interesting to your application.

Traces that allocate significant amount of time to Controller or Job layers are good candidates to add custom instrumentation. This indicates a significant amount of time is falling outside our default instrumentation.

Asynchronous functionality may be instrumented with code similar to the following:

// NOTE: The transaction is *implicit* inside of express route handlers, if you are using the express middleware
scout.api.WebTransaction.run("transaction-name", (finishTransaction) => {
  // Start the first instrumentation
  const first = scout.api.instrument("instrument-name", (finishInstrument) => {
    // instrument code
    return yourAsyncFunction()
        .then(() => finishInstrument());
  });

  // Start the second instrumentation
  scout.api.instrument("instrument-name", (finishInstrument) => {
    // instrument code
    return yourAsyncFunction()
        .then(() => finishInstrument());
  });

  // Finish the transaction once all instrumentations are recorded
  Promise.all([first, second])
    .then(() => finishTransaction());
});

For Asynchronous functionality in a callback-passing style:

// NOTE: The transaction is *implicit* inside of express route handlers, if you are using the express middleware
scout.api.WebTransaction.run("transaction-name", (finishTransaction) => {
  // Start the first instrumentation
  const first = scout.api.instrument("first-instrumentation", (finishFirst) => {
    // instrument code
    yourCallbackStyleAsyncFunction((err) => {
      if (err) {
        // error handling code here
        return;
      }

      finishFirst();

      // Start a second instrumentation
      const second = scout.api.instrument("second-instrumentation", (finishSecond) => {
        // instrument code
        yourCallbackStyleAsyncFunction((err) => {
          if (err) {
            // error handling code here
            return;
          }

          finishSecond();
          finishTransaction();
        });
      });

    });
  });
});

Synchronous functionality can be instrumented with code similar to the following:

// NOTE: The transaction is *implicit* inside of express route handlers, if you are using the express middleware
scout.api.WebTransaction.runSync("sync-transaction-name", (finishTransaction) => {
  scout.api.instrumentSync("first-instrumentation", () => {
    yourSyncFunction();
  });

  scout.api.instrumentSync("second-instrumentation", () => {
    yourSyncFunction();
  });
});

Limits

We limit the number of metrics that can be instrumented. Tracking too many unique metrics can impact the performance of our UI. Do not dynamically generate metric types in your instrumentation as this can quickly exceed our rate limits.

For high-cardinality details, use context.

Getting Started

With existing code like:

// A handler that handles GET /
const handler = (req, res) => {
    // Functionality here
};

The express middleware automatically wraps your request and handler with a transaction/instrumentation as if you’d written the following:

scout.api.WebTransaction.run("Controller/GET /<your route>", finishTransaction => { // transaction name format is `<kind>/<name>`
  scout.api.instrument("Controller/GET /<your route>", finishSpan => { // instrumentation name format is `<kind>/<name>`
    // The original handler code
  });
});