Module: Appsignal::Helpers::Instrumentation

Includes:
Utils::DeprecationMessage
Included in:
Appsignal
Defined in:
lib/appsignal/helpers/instrumentation.rb

Overview

rubocop:disable Metrics/ModuleLength

Instance Method Summary collapse

Methods included from Utils::DeprecationMessage

#deprecation_message, message

Instance Method Details

#add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc) ⇒ void

This method returns an undefined value.

Add breadcrumbs to the transaction.

Breadcrumbs can be used to trace what path a user has taken before encounterin an error.

Only the last 20 added breadcrumbs will be saved.

Examples:

Appsignal.add_breadcrumb(
  "Navigation",
  "http://blablabla.com",
  "",
  { :response => 200 },
  Time.now.utc
)
Appsignal.add_breadcrumb(
  "Network",
  "[GET] http://blablabla.com",
  "",
  { :response => 500 }
)
Appsignal.add_breadcrumb(
  "UI",
  "closed modal(change_password)",
  "User closed modal without actions"
)

Parameters:

  • category (String)

    category of breadcrumb e.g. "UI", "Network", "Navigation", "Console".

  • action (String)

    name of breadcrumb e.g "The user clicked a button", "HTTP 500 from http://blablabla.com"

  • message (Hash) (defaults to: "")

    a customizable set of options

  • metadata (Hash) (defaults to: {})

    a customizable set of options

  • time (Hash) (defaults to: Time.now.utc)

    a customizable set of options

Options Hash (message):

  • optional (String)

    message in string format

Options Hash (metadata):

  • key/value (Hash<String,String>)

    metadata in format

Options Hash (time):

  • time (Time)

    of breadcrumb, should respond to .to_i defaults to Time.now.utc

See Also:

Since:

  • 2.12.0



489
490
491
492
493
494
495
# File 'lib/appsignal/helpers/instrumentation.rb', line 489

def add_breadcrumb(category, action, message = "",  = {}, time = Time.now.utc)
  return unless active?
  return unless Appsignal::Transaction.current?

  transaction = Appsignal::Transaction.current
  transaction.add_breadcrumb(category, action, message, , time)
end

#instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT) { ... } ⇒ Object

Instrument helper for AppSignal.

For more help, read our custom instrumentation guide, listed under "See also".

Examples:

Simple instrumentation

Appsignal.instrument("fetch.issue_fetcher") do
  # To be instrumented code
end

Instrumentation with title and body

Appsignal.instrument(
  "fetch.issue_fetcher",
  "Fetching issue",
  "GitHub API"
) do
  # To be instrumented code
end

Parameters:

  • name (String)

    Name of the instrumented event. Read our event naming guide listed under "See also".

  • title (String, nil) (defaults to: nil)

    Human readable name of the event.

  • body (String, nil) (defaults to: nil)

    Value of importance for the event, such as the server against an API call is made.

  • body_format (Integer) (defaults to: Appsignal::EventFormatter::DEFAULT)

    Enum for the type of event that is instrumented. Accepted values are EventFormatter::DEFAULT and EventFormatter::SQL_BODY_FORMAT, but we recommend you use #instrument_sql instead of EventFormatter::SQL_BODY_FORMAT.

Yields:

  • yields the given block of code instrumented in an AppSignal event.

Returns:

  • (Object)

    Returns the block's return value.

See Also:

Since:

  • 1.3.0



536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/appsignal/helpers/instrumentation.rb', line 536

def instrument(
  name,
  title = nil,
  body = nil,
  body_format = Appsignal::EventFormatter::DEFAULT
)
  Appsignal::Transaction.current.start_event
  yield if block_given?
ensure
  Appsignal::Transaction
    .current
    .finish_event(name, title, body, body_format)
end

#instrument_sql(name, title = nil, body = nil) { ... } ⇒ Object

Instrumentation helper for SQL queries.

This helper filters out values from SQL queries so you don't have to.

Examples:

SQL query instrumentation

body = "SELECT * FROM ..."
Appsignal.instrument_sql("perform.query", nil, body) do
  # To be instrumented code
end

SQL query instrumentation

body = "WHERE email = 'foo@..'"
Appsignal.instrument_sql("perform.query", nil, body) do
  # query value will replace 'foo..' with a question mark `?`.
end

Parameters:

  • name (String)

    Name of the instrumented event. Read our event naming guide listed under "See also".

  • title (String, nil) (defaults to: nil)

    Human readable name of the event.

  • body (String, nil) (defaults to: nil)

    SQL query that's being executed.

Yields:

  • yields the given block of code instrumented in an AppSignal event.

Returns:

  • (Object)

    Returns the block's return value.

See Also:

Since:

  • 2.0.0



580
581
582
583
584
585
586
587
588
# File 'lib/appsignal/helpers/instrumentation.rb', line 580

def instrument_sql(name, title = nil, body = nil, &block)
  instrument(
    name,
    title,
    body,
    Appsignal::EventFormatter::SQL_BODY_FORMAT,
    &block
  )
end

#listen_for_error(tags = nil, namespace = Appsignal::Transaction::HTTP_REQUEST) { ... } ⇒ Object Also known as: listen_for_exception

Listen for an error to occur and send it to AppSignal.

Uses #send_error to directly send the error in a separate transaction. Does not add the error to the current transaction.

Make sure that AppSignal is integrated in your application beforehand. AppSignal won't record errors unless Appsignal.active? is true.

Examples:

# my_app.rb
# setup AppSignal beforehand

Appsignal.listen_for_error do
  # my code
  raise "foo"
end

Parameters:

  • tags (Hash, nil) (defaults to: nil)
  • namespace (String) (defaults to: Appsignal::Transaction::HTTP_REQUEST)

    the namespace for this error.

Yields:

  • yields the given block.

Returns:

  • (Object)

    returns the return value of the given block.

See Also:



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/appsignal/helpers/instrumentation.rb', line 136

def listen_for_error(
  tags = nil,
  namespace = Appsignal::Transaction::HTTP_REQUEST
)
  yield
rescue Exception => error # rubocop:disable Lint/RescueException
  send_error(error) do |transaction|
    transaction.set_tags(tags) if tags
    transaction.set_namespace(namespace) if namespace
  end
  raise error
end

#monitor_single_transaction(name, env = {}, &block) ⇒ Object

Monitor a transaction, stop AppSignal and wait for this single transaction to be flushed.

Useful for cases such as Rake tasks and Resque-like systems where a process is forked and immediately exits after the transaction finishes.



103
104
105
106
107
# File 'lib/appsignal/helpers/instrumentation.rb', line 103

def monitor_single_transaction(name, env = {}, &block)
  monitor_transaction(name, env, &block)
ensure
  stop("monitor_single_transaction")
end

#monitor_transaction(name, env = {}) { ... } ⇒ Object

Creates an AppSignal transaction for the given block.

If AppSignal is not Appsignal.active? it will still execute the block, but not create a transaction for it.

A event is created for this transaction with the name given in the name argument. The event name must start with either perform_job or process_action to differentiate between the "web" and "background" namespace. Custom namespaces are not supported by this helper method.

This helper method also captures any exception that occurs in the given block.

Examples:

Appsignal.monitor_transaction("perform_job.nightly_update") do
  # your code
end

with an environment

Appsignal.monitor_transaction(
  "perform_job.nightly_update",
  :metadata => { "user_id" => 1 }
) do
  # your code
end

Parameters:

  • name (String)

    main event name.

  • env (Hash<Symbol, Object>) (defaults to: {})

Options Hash (env):

  • :params (Hash<Symbol/String, Object>)

    Params for the monitored request/job, see Transaction#params= for more information.

  • :controller (String)

    name of the controller in which the transaction was recorded.

  • :class (String)

    name of the Ruby class in which the transaction was recorded. If :controller is also given, :controller is used instead.

  • :action (String)

    name of the controller action in which the transaction was recorded.

  • :method (String)

    name of the Ruby method in which the transaction was recorded. If :action is also given, :action is used instead.

  • :queue_start (Integer)

    the moment the request/job was queued. Used to track how long requests/jobs were queued before being executed.

  • :metadata (Hash<Symbol/String, String/Fixnum>)

    Additional metadata for the transaction, see Transaction#set_metadata for more information.

Yields:

  • the block to monitor.

Returns:

  • (Object)

    the value of the given block is returned.

Raises:

  • (Exception)

    any exception that occurs within the given block is re-raised by this method.

Since:

  • 0.10.0



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/appsignal/helpers/instrumentation.rb', line 60

def monitor_transaction(name, env = {}, &block)
  # Always verify input, even when Appsignal is not active.
  # This makes it more likely invalid arguments get flagged in test/dev
  # environments.
  if name.start_with?("perform_job")
    namespace = Appsignal::Transaction::BACKGROUND_JOB
    request   = Appsignal::Transaction::GenericRequest.new(env)
  elsif name.start_with?("process_action")
    namespace = Appsignal::Transaction::HTTP_REQUEST
    request   = ::Rack::Request.new(env)
  else
    internal_logger.error "Unrecognized name '#{name}': names must " \
      "start with either 'perform_job' (for jobs and tasks) or " \
      "'process_action' (for HTTP requests)"
    return yield
  end

  return yield unless active?

  transaction = Appsignal::Transaction.create(
    SecureRandom.uuid,
    namespace,
    request
  )
  begin
    Appsignal.instrument(name, &block)
  rescue Exception => error # rubocop:disable Lint/RescueException
    transaction.set_error(error)
    raise error
  ensure
    transaction.set_http_or_background_action(request.env)
    transaction.set_http_or_background_queue_start
    Appsignal::Transaction.complete_current!
  end
end

#send_error(error, tags = nil, namespace = nil) {|transaction| ... } ⇒ void Also known as: send_exception

This method returns an undefined value.

Send an error to AppSignal regardless of the context.

Records and send the exception to AppSignal.

This instrumentation helper does not require a transaction to be active, it starts a new transaction by itself.

Use #set_error if your want to add an exception to the current transaction.

Note: Does not do anything if AppSignal is not active or when the "error" is not a class extended from Ruby's Exception class.

Examples:

Send an exception

begin
  raise "oh no!"
rescue => e
  Appsignal.send_error(e)
end

Send an exception with tags. Deprecated method.

begin
  raise "oh no!"
rescue => e
  Appsignal.send_error(e, :key => "value")
end

Add more metadata to transaction

Appsignal.send_error(e) do |transaction|
  transaction.params = { :search_query => params[:search_query] }
  transaction.set_action("my_action_name")
  transaction.set_tags(:key => "value")
  transaction.set_namespace("my_namespace")
end

Parameters:

  • error (Exception)

    The error to send to AppSignal.

  • tags (Hash{String, Symbol => String, Symbol, Integer}) (defaults to: nil)

    Additional tags to add to the error. See also #tag_request. This parameter is deprecated. Use the block argument instead.

  • namespace (String) (defaults to: nil)

    The namespace in which the error occurred. See also #set_namespace. This parameter is deprecated. Use the block argument instead.

Yields:

  • (transaction)

    yields block to allow modification of the transaction before it's send.

Yield Parameters:

  • transaction (Transaction)

    yields the AppSignal transaction used to send the error.

See Also:

Since:

  • 0.6.0



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/appsignal/helpers/instrumentation.rb', line 203

def send_error(
  error,
  tags = nil,
  namespace = nil
)
  if tags
    call_location = caller(1..1).first
    deprecation_message \
      "The tags argument for `Appsignal.send_error` is deprecated. " \
        "Please use the block method to set tags instead.\n\n" \
        "  Appsignal.send_error(error) do |transaction|\n" \
        "    transaction.set_tags(#{tags})\n" \
        "  end\n\n" \
        "Appsignal.send_error called on location: #{call_location}"
  end
  if namespace
    call_location = caller(1..1).first
    deprecation_message \
      "The namespace argument for `Appsignal.send_error` is deprecated. " \
        "Please use the block method to set the namespace instead.\n\n" \
        "  Appsignal.send_error(error) do |transaction|\n" \
        "    transaction.set_namespace(#{namespace.inspect})\n" \
        "  end\n\n" \
        "Appsignal.send_error called on location: #{call_location}"
  end
  return unless active?

  unless error.is_a?(Exception)
    internal_logger.error "Appsignal.send_error: Cannot send error. " \
      "The given value is not an exception: #{error.inspect}"
    return
  end
  transaction = Appsignal::Transaction.new(
    SecureRandom.uuid,
    namespace || Appsignal::Transaction::HTTP_REQUEST,
    Appsignal::Transaction::GenericRequest.new({})
  )
  transaction.set_tags(tags) if tags
  transaction.set_error(error)
  yield transaction if block_given?
  transaction.complete
end

#set_action(action) ⇒ void

This method returns an undefined value.

Set a custom action name for the current transaction.

When using an integration such as the Rails or Sinatra AppSignal will try to find the action name from the controller or endpoint for you.

If you want to customize the action name as it appears on AppSignal.com you can use this method. This overrides the action name AppSignal generates in an integration.

Examples:

in a Rails controller

class SomeController < ApplicationController
  before_action :set_appsignal_action

  def set_appsignal_action
    Appsignal.set_action("DynamicController#dynamic_method")
  end
end

Parameters:

  • action (String)

See Also:

Since:

  • 2.2.0



359
360
361
362
363
364
365
# File 'lib/appsignal/helpers/instrumentation.rb', line 359

def set_action(action)
  return if !active? ||
    !Appsignal::Transaction.current? ||
    action.nil?

  Appsignal::Transaction.current.set_action(action)
end

#set_error(exception, tags = nil, namespace = nil) {|transaction| ... } ⇒ void Also known as: set_exception, add_exception

This method returns an undefined value.

Set an error on the current transaction.

Note: Does not do anything if AppSignal is not active, no transaction is currently active or when the "error" is not a class extended from Ruby's Exception class.

Examples:

Manual instrumentation of set_error.

# Manually starting AppSignal here
# Manually starting a transaction here.
begin
  raise "oh no!"
rescue => e
  Appsignal.set_error(e)
end
# Manually completing the transaction here.
# Manually stopping AppSignal here

In a Rails application

class SomeController < ApplicationController
  # The AppSignal transaction is created by our integration for you.
  def create
    # Do something that breaks
  rescue => e
    Appsignal.set_error(e)
  end
end

Add more metadata to transaction

Appsignal.set_error(e) do |transaction|
  transaction.params = { :search_query => params[:search_query] }
  transaction.set_action("my_action_name")
  transaction.set_tags(:key => "value")
  transaction.set_namespace("my_namespace")
end

Parameters:

  • exception (Exception)

    The error to add to the current transaction.

  • tags (Hash{String, Symbol => String, Symbol, Integer}) (defaults to: nil)

    Additional tags to add to the error. See also #tag_request. This parameter is deprecated. Use the block argument instead.

  • namespace (String) (defaults to: nil)

    The namespace in which the error occurred. See also #set_namespace. This parameter is deprecated. Use the block argument instead.

Yields:

  • (transaction)

    yields block to allow modification of the transaction.

Yield Parameters:

  • transaction (Transaction)

    yields the AppSignal transaction used to store the error.

See Also:

Since:

  • 0.6.6



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/appsignal/helpers/instrumentation.rb', line 300

def set_error(exception, tags = nil, namespace = nil)
  if tags
    call_location = caller(1..1).first
    deprecation_message \
      "The tags argument for `Appsignal.set_error` is deprecated. " \
        "Please use the block method to set tags instead.\n\n" \
        "  Appsignal.set_error(error) do |transaction|\n" \
        "    transaction.set_tags(#{tags})\n" \
        "  end\n\n" \
        "Appsignal.set_error called on location: #{call_location}"
  end
  if namespace
    call_location = caller(1..1).first
    deprecation_message \
      "The namespace argument for `Appsignal.set_error` is deprecated. " \
        "Please use the block method to set the namespace instead.\n\n" \
        "  Appsignal.set_error(error) do |transaction|\n" \
        "    transaction.set_namespace(#{namespace.inspect})\n" \
        "  end\n\n" \
        "Appsignal.set_error called on location: #{call_location}"
  end
  unless exception.is_a?(Exception)
    internal_logger.error "Appsignal.set_error: Cannot set error. " \
      "The given value is not an exception: #{exception.inspect}"
    return
  end
  return if !active? || !Appsignal::Transaction.current?

  transaction = Appsignal::Transaction.current
  transaction.set_error(exception)
  transaction.set_tags(tags) if tags
  transaction.set_namespace(namespace) if namespace
  yield transaction if block_given?
end

#set_namespace(namespace) ⇒ void

This method returns an undefined value.

Set a custom namespace for the current transaction.

When using an integration such as Rails or Sidekiq AppSignal will try to find a appropriate namespace for the transaction.

A Rails controller will be automatically put in the "http_request" namespace, while a Sidekiq background job is put in the "background_job" namespace.

Note: The "http_request" namespace gets transformed on AppSignal.com to "Web" and "background_job" gets transformed to "Background".

If you want to customize the namespace in which transactions appear you can use this method. This overrides the namespace AppSignal uses by default.

A common request we've seen is to split the administration panel from the main application.

Examples:

create a custom admin namespace

class AdminController < ApplicationController
  before_action :set_appsignal_namespace

  def set_appsignal_namespace
    Appsignal.set_namespace("admin")
  end
end

Parameters:

  • namespace (String)

See Also:

Since:

  • 2.2.0



399
400
401
402
403
404
405
# File 'lib/appsignal/helpers/instrumentation.rb', line 399

def set_namespace(namespace)
  return if !active? ||
    !Appsignal::Transaction.current? ||
    namespace.nil?

  Appsignal::Transaction.current.set_namespace(namespace)
end

#tag_request(tags = {}) ⇒ void Also known as: tag_job

This method returns an undefined value.

Set tags on the current transaction.

Tags are extra bits of information that are added to transaction and appear on sample details pages on AppSignal.com.

Examples:

Appsignal.tag_request(:locale => "en")
Appsignal.tag_request("locale" => "en")
Appsignal.tag_request("user_id" => 1)

Nested hashes are not supported

# Bad
Appsignal.tag_request(:user => { :locale => "en" })

in a Rails controller

class SomeController < ApplicationController
  before_action :set_appsignal_tags

  def set_appsignal_tags
    Appsignal.tag_request(:locale => I18n.locale)
  end
end

Parameters:

  • tags (Hash) (defaults to: {})

    Collection of tags.

Options Hash (tags):

  • :any (String, Symbol, Integer)

    The name of the tag as a Symbol.

  • "any" (String, Symbol, Integer)

    The name of the tag as a String.

See Also:



440
441
442
443
444
445
446
# File 'lib/appsignal/helpers/instrumentation.rb', line 440

def tag_request(tags = {})
  return unless active?
  return unless Appsignal::Transaction.current?

  transaction = Appsignal::Transaction.current
  transaction.set_tags(tags)
end

#without_instrumentation { ... } ⇒ Object

Convenience method for skipping instrumentations around a block of code.

Examples:

Appsignal.without_instrumentation do
  # Complex code here
end

Yields:

  • block of code that shouldn't be instrumented.

Returns:

  • (Object)

    Returns the return value of the block.

Since:

  • 0.8.7



600
601
602
603
604
605
# File 'lib/appsignal/helpers/instrumentation.rb', line 600

def without_instrumentation
  Appsignal::Transaction.current&.pause!
  yield
ensure
  Appsignal::Transaction.current&.resume!
end