In order to find out what specific pieces of code are causing performance problems it’s useful to add custom instrumentation to your application. This allows us to create better breakdowns of which code runs slowest and what type of action was the most time spent on.When you view saved traces of slow requests in AppSignal you’ll be able to see all the instrumentation your application uses internally. Template rendering, ActiveRecord queries and caching are instrumented and will be shown in the trace.That’s already very useful, but wouldn’t it be great if we could see
measurements of specific pieces of code you suspect might influence your
performance? Well, you can!By adding custom instrumentation, you can create more detailed breakdowns of a request or background job. You can instrument your code with the AppSignal instrumentation helpers, or with a notification library that AppSignal reads. Rails applications use ActiveSupport Notifications, and dry-rb applications use Dry::Monitor notifications.
When you add custom instrumentation to your code you’ll be able to receive even
more insights into your application. For example, you have to work with an
external API that fetches articles for your homepage:
class ArticleFetcher def self.fetch(category) Appsignal.instrument('fetch.article_fetcher') do # Download and process the articles end endendArticleFetcher.fetch('Latest news')
Once you add custom instruments like this AppSignal will start picking them up
and will show you how much time both an event group (article_fetcher in this
case) and individual events took.In this case you’ll notice that this API call is a huge influence on the
performance of our homepage, which was hidden before. We might want to consider
caching the articles.
Note: The name of the event you’re instrumenting is important for our
processor. Read more about event naming.
You can use as many instruments in any combination you like. You can
nest instrument calls and AppSignal will handle the nesting and aggregates of
the measurements nicely. You just have to keep the final segment (after the last
dot) of the key consistent.
Appsignal.instrument('fetch.article_fetcher') do 10.times do Appsignal.instrument('fetch_single_article.article_fetcher') do # Fetch single article end endend
By default AppSignal will collect the duration of an event and send it to our
servers. Since custom instrumentation is not hooked up to any framework
internals you might need to pass along more data if you want event details to
show up in AppSignal. This can be a descriptive title, or more specific
information like the query from a database call. We already do this for
ActiveRecord, Sequel, Redis, MongoDB, Sinatra, Grape,
and more.There are two helpers to allow you to instrument your code with AppSignal.
Appsignal.instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT, &block)# andAppsignal.instrument_sql(name, title = nil, body = nil, &block)
A more descriptive title of an event, such as "Fetch current user" or "Fetch blog post comments". It will appear next to the event name in the event tree
on the performance trace page to provide a little more context on what’s
happening.
Appsignal.instrument('fetch.custom_database', 'Fetch current user') do # ...end
In collector mode, the event’s span is named after both, as category (title). The example above is recorded as fetch.custom_database (Fetch current user). An event with no title is named after its category alone, and a title that matches the category is not repeated.
More details such as a database query that was used by the event.
sql = 'SELECT * FROM posts ORDER BY created_at DESC LIMIT 1'Appsignal.instrument('fetch.custom_database', 'Fetch latest post', sql) do # ...end
Warning: Please make sure the body payloads are sanitized
(sensitive/dynamic data is removed). Non-sanitized body events will be
discarded if they reach a certain limit.
Good:
Appsignal.instrument('custom.instrument', 'Instrument stuff', 'command/dynamic/?') do # ...endAppsignal.instrument('custom.instrument', 'Instrument stuff', 'command/dynamic/?') do # ...end
Bad:
Appsignal.instrument('custom.instrument', 'Instrument stuff', 'command/dynamic/123') do # ...endAppsignal.instrument('custom.instrument', 'Instrument stuff', 'command/dynamic/234') do # ...end
When passing in an SQL query as the body, you can use body_format = Appsignal::EventFormatter::SQL_BODY_FORMAT to do so.
Body format supports formatters to scrub the given data in the body argument
to remove any sensitive data from the value. There are currently two supported
values for the body_format argument.
Appsignal::EventFormatter::DEFAULT value
The Appsignal::EventFormatter::DEFAULT is the default value of this
argument. By default AppSignal will leave the value intact and not scrub any
data from it.
Appsignal::EventFormatter::SQL_BODY_FORMAT value
The Appsignal::EventFormatter::SQL_BODY_FORMAT value will run your data
through the SQL sanitizer and scrub any values in SQL queries.We recommend you use the Appsignal.instrument_sql helper for this instead.
SELECT * FROM users WHERE email = 'hector@appsignal.com' AND password = 'iamabot'-- becomesSELECT * FROM users WHERE email = ? AND password = ?
In older versions of the AppSignal gem (1.2 and lower) the
Appsignal.instrument is not available. If you cannot upgrade, it’s still
possible to use ActiveSupport::Notifications instead. If you don’t want to
use the Appsignal.instrument helper, but instead want to use
ActiveSupport::Notifications, you can still do so in AppSignal for Ruby gem
1.3 and higher too.
The method for instrumenting your code using ActiveSupport::Notifications
is very similar to how AppSignal does it. Using the article fetcher example
again you can see the differences are quite small.Also see our documentation on AppSignal event formatters when using ActiveSupport::Notifications.
For more information about ActiveSupport::Notifications instrumentation, see the official Rails ActiveSupport::Notifications documentation.
require "active_support"class ArticleFetcher def self.fetch(category) ActiveSupport::Notifications.instrument("fetch.article_fetcher") do # Download and process the articles end endendArticleFetcher.fetch("Latest news")
It works for nested instrumentation calls as well.
require "active_support"ActiveSupport::Notifications.instrument("fetch.article_fetcher") do 10.times do ActiveSupport::Notifications.instrument("fetch_single_article.article_fetcher") do # Fetch single article end endend
ActiveSupport::Notifications is highly flexible, you can instrument your code
any way you like. More information about ActiveSupport::Notifications can be
found in the
Rails API docs.
Warning: We do not track private ActiveSupport::Notifications events
that start with an exclamation mark (!). These events mostly include private
events generated by Rails.
AppSignal records events instrumented through Dry::Monitor::Notifications, the notification system used by the dry-rb libraries and by ROM.
require "dry/monitor"notifications = Dry::Monitor::Notifications.new(:app)notifications.instrument(:fetch_articles) do # Download and process the articlesend
Dry::Monitor reports an event under an ID rather than a name, so AppSignal names the event after its ID followed by .dry. The example above is recorded as a fetch_articles.dry event. An event formatter can give an event a name of its own instead. Read more about how AppSignal uses event names in our event naming guide.
Was this page helpful?
⌘I
Assistant
Responses are generated using AI and may contain mistakes.