Advanced Features

Deploy Tracking

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

Scout identifies deploys via the following approaches:

from scout_apm.api import Config
Config.set(revision_sha=os.popen("git rev-parse HEAD").read().strip())  # if the app directory is a git repo

Request Queuing

Our Python 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 forest from the trees. For example, you can add custom context to answer critical questions like:

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

import scout_apm.api
# scout_apm.api.Context.add(key, value)
scout_apm.api.Context.add("user_id", request.user.id)

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 better protect your data, we suggest using ids instead of explicit names and emails

Renaming Transactions

If you want to rename the current transaction, call rename_transaction() with the new name:

import scout_apm.api

scout_apm.api.rename_transaction("Controller/" + "your_new_name_here")

You can use this whether the transaction was started from a built-in integration or custom instrumentation.

GraphQL

If you have a GraphQL endpoint which serves any number of queries, you likely want to have each of those types of queries show up in the Scout UI as different endpoints. You can accomplish this by renaming the transaction during the request like so:

import scout_apm.api

scout_apm.api.rename_transaction("Controller/" + derive_graphql_name())

Do not generate highly cardinality transaction names, like ScoutApm::Transaction.rename("GraphQL/foobar_#{current_user.id}"), as we limit the number of transactions that can be tracked. High-cardinality transaction names can quickly surpass this limit.

Custom Instrumentation

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

Transactions & Timing

Scout’s instrumentation is divided into 2 areas:

  1. Transactions: these wrap around a flow of work, like a web request or Cron job. The UI groups data under transactions. Use the @scout_apm.api.WebTransaction() decorator or wrap blocks of code via the with scout_apm.api.WebTransaction('') context manager.
  2. Timing: these measure individual pieces of work, like an HTTP request to an outside service and display timing information within a transaction trace. Use the @scout_apm.api.instrument() decorator or the with scout_apm.api.instrument() as instrument context manager.

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 Python script that is ran as a Cron Job.

Limits

We limit the number of unique transactions that can be instrumented. Tracking too many unique transactions can impact the performance of our UI. Do not dynamically generate transaction names in your instrumentation (i.e. with scout_apm.api.WebTransaction('update_user_"+user.id')) as this can quickly exceed our rate limits. Use context to add high-dimensionality information instead.

Getting Started

Import the API module and configure Scout:

import scout_apm.api

# A dict containing the configuration for APM.
# See our Python help docs for all configuration options.
config = {
    "name": "My App Name",
    "key": "APM_Key",
    "monitor": True,
}

# The `install()` method must be called early on within your app code in order
# to install the APM agent code and instrumentation.
scout_apm.api.install(config=config)

Web or Background transactions?

Scout distinguishes between two types of transactions:

Explicit

scout_apm.api.WebTransaction.start("Foo")  # or BackgroundTransaction.start()
# do some app work
scout_apm.api.WebTransaction.stop()

As a context manager

with scout_apm.api.WebTransaction("Foo"):  # or BackgroundTransaction()
    # do some app work

As a decorator

@scout_apm.api.WebTransaction("Foo")  # or BackgroundTransaction()
def my_foo_action(path):
    # do some app work

Cron Job Example

#!/usr/bin/env python

import requests
import scout_apm.api

# A dict containing the configuration for APM.
# See our Python help docs for all configuration options.
config = {
    "name": "My App Name",
    "key": "YOUR_SCOUT_KEY",
    "monitor": True,
}

# The `install()` method must be called early on within your app code in order
# to install the APM agent code and instrumentation.
scout_apm.api.install(config=config)

# Will appear under Background jobs in the Scout UI
with scout_apm.api.BackgroundTransaction("Foo"):
    response = requests.get("https://httpbin.org/status/418")
    print(response.text)

Timing functions and blocks of code

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

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 (ie with scout_apm.api.instrument("Computation_for_user_" + user.id)) as this can quickly exceed our rate limits.

For high-cardinality details, use tags: with scout_apm.api.instrument("Computation", tags={"user": user.id}).

Getting Started

Import the API module:

import scout_apm.api

# or to not use the whole prefix on each call:
from scout_apm.api import instrument
scout_apm.api.instrument(name, tags={}, kind="Custom")

As a context manager

Wrap a section of code in a unique “span” of work.

The yielded object can be used to add additional tags individually.

def foo():
    with scout_apm.api.instrument("Computation 1") as instrument:
        instrument.tag("record_count", 100)
        # Work

    with scout_apm.api.instrument("Computation 2", tags={"filtered_record_count": 50}) as instrument:
        # Work

As a decorator

Wraps a whole function, timing the execution of specified function within a transaction trace. This uses the same API as the ContextManager style.

@scout_apm.api.instrument("Computation")
def bar():
    # Work

Instrumenting Async Code

The Python Agent supports decorating async functions for Python 3.6 and up. If you need to instrument an asynchronous function, or a function that returns an awaitable, you can use the async_ decorator to decorate your function:

@scout_apm.api.WebTransaction.async_("Foo")
async def foo():
    # app work
@scout_apm.api.BackgroundTransaction.async_("Bar")
async def bar():
    # app work
@scout_apm.api.instrument.async_("Spam")
async def spam():
    # app work
# Use async_ even though this function doesn't use async def
# because it returns an awaitable.
@scout_apm.api.instrument.async_("Returns Awaitable")
def returns_awaitable():
    return spam()

Note: If you use the async decorator on a synchronous function that does not return an awaitable,a RuntimeWarning will be logged indicating that an awaitable was not awaited.