Python 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:
- Setting the
revision_sha
configuration value:
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
- Setting a
SCOUT_REVISION_SHA
environment variable equal to the SHA of your latest release. - If you are using Heroku, enable Dyno Metadata. This adds a
HEROKU_SLUG_COMMIT
environment variable to your dynos, which Scout then associates with deploys.
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:
- How many users are impacted by slow requests?
- How many trial customers are impacted by slow requests?
- How much of an impact are slow requests having on our highest paying customers?
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:
"1.1.1.1"
"free"
100
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:
- 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 thewith scout_apm.api.WebTransaction('')
context manager. - 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 thewith 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:
WebTransaction
: For transactions that impact the user-facing experience. Time spent in these transactions will appear on your app overboard dashboard and appear in the “Web” area of the UI.BackgroundTransaction
: For transactions that don’t have an impact on the user-facing experience (example: cron jobs). These will be available in the “Background Jobs” area of the UI.
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")
name
- A semi-detailed version of what the section of code is. It should be static between different invocations of the method. Individual details like a user ID, or counts or other data points can be added as tags. Names likeretreive_from_api
orGET
are good names.kind
- A high level area of the application. This defaults toCustom
. Your whole application should have a very low number of unique strings here. In our built-in instruments, this is things likeTemplate
andSQL
. For custom instrumentation, it can be strings likeMongoDB
orHTTP
or similar. This should not change based on input or state of the application.tags
- Tagging adds context to the request, and can be viewed on the context tab of the trace, as well as on the trace explorer page. A dictionary of key/value pairs. Key should be a string, but value can be any json-able structure. High-cardinality fields like a user ID are permitted.
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.
ARM and graviton Support
We now have support for ARM and graviton.
- Our Python agent automatically detects ARM support
- To explicitly connect, the core_agent_triple configuration setting must be specified.