lib/appsignal/transaction.rb in appsignal-2.1.0.alpha.3 vs lib/appsignal/transaction.rb in appsignal-2.1.0.beta.1
- old
+ new
@@ -1,28 +1,31 @@
-require 'json'
+require "json"
module Appsignal
class Transaction
- HTTP_REQUEST = 'http_request'.freeze
- BACKGROUND_JOB = 'background_job'.freeze
- FRONTEND = 'frontend'.freeze
- BLANK = ''.freeze
+ HTTP_REQUEST = "http_request".freeze
+ BACKGROUND_JOB = "background_job".freeze
+ FRONTEND = "frontend".freeze
+ BLANK = "".freeze
# Based on what Rails uses + some variables we'd like to show
- ENV_METHODS = %w(CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
- PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
- REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL REQUEST_URI PATH_INFO
+ ENV_METHODS = %w(
+ CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
+ PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
+ REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL REQUEST_URI
+ PATH_INFO
- HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
- HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
- HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
- HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM HTTP_NEGOTIATE
- HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR HTTP_CLIENT_IP HTTP_RANGE
- HTTP_X_AUTH_TOKEN)
+ HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
+ HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
+ HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR
+ HTTP_CLIENT_IP HTTP_RANGE HTTP_X_AUTH_TOKEN
+ )
class << self
- def create(id, namespace, request, options={})
+ def create(id, namespace, request, options = {})
# Allow middleware to force a new transaction
if options.include?(:force) && options[:force]
Thread.current[:appsignal_transaction] = nil
end
@@ -56,11 +59,11 @@
end
end
attr_reader :ext, :transaction_id, :namespace, :request, :paused, :tags, :options, :discarded
- def initialize(transaction_id, namespace, request, options={})
+ def initialize(transaction_id, namespace, request, options = {})
@transaction_id = transaction_id
@namespace = namespace
@request = request
@paused = false
@discarded = false
@@ -80,11 +83,11 @@
false
end
def complete
if discarded?
- Appsignal.logger.debug('Skipping transaction because it was manually discarded.'.freeze)
+ Appsignal.logger.debug("Skipping transaction because it was manually discarded.".freeze)
return
end
if @ext.finish(self.class.garbage_collection_profiler.total_time)
sample_data
end
@@ -117,26 +120,26 @@
def store(key)
@store[key]
end
- def set_tags(given_tags={})
+ def set_tags(given_tags = {})
@tags.merge!(given_tags)
end
def set_action(action)
return unless action
@ext.set_action(action)
end
- def set_http_or_background_action(from=request.params)
+ def set_http_or_background_action(from = request.params)
return unless from
group_and_action = [
from[:controller] || from[:class],
from[:action] || from[:method]
]
- set_action(group_and_action.compact.join('#'))
+ set_action(group_and_action.compact.join("#"))
end
def set_queue_start(start)
return unless start
@ext.set_queue_start(start)
@@ -180,11 +183,10 @@
end
def set_error(error)
return unless error
return unless Appsignal.active?
- return if Appsignal.is_ignored_error?(error)
backtrace = cleaned_backtrace(error.backtrace)
@ext.set_error(
error.class.name,
error.message.to_s,
@@ -195,31 +197,31 @@
def start_event
@ext.start_event(self.class.garbage_collection_profiler.total_time)
end
- def finish_event(name, title, body, body_format=Appsignal::EventFormatter::DEFAULT)
+ def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
@ext.finish_event(
name,
title || BLANK,
body || BLANK,
body_format || Appsignal::EventFormatter::DEFAULT,
self.class.garbage_collection_profiler.total_time
)
end
- def record_event(name, title, body, duration, body_format=Appsignal::EventFormatter::DEFAULT)
+ def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
@ext.record_event(
name,
title || BLANK,
body || BLANK,
duration,
body_format || Appsignal::EventFormatter::DEFAULT
)
end
- def instrument(name, title=nil, body=nil, body_format=Appsignal::EventFormatter::DEFAULT)
+ def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
start_event
r = yield
finish_event(name, title, body, body_format)
r
end
@@ -236,27 +238,44 @@
end
end
protected
+ # Returns calculated background queue start time in milliseconds, based on
+ # environment values.
+ #
+ # @return [nil] if no {#environment} is present.
+ # @return [nil] if there is no `:queue_start` in the {#environment}.
+ # @return [Integer]
def background_queue_start
- return unless request.env
- return unless queue_start = request.env[:queue_start]
+ env = environment
+ return unless env
+ queue_start = env[:queue_start]
+ return unless queue_start
+
(queue_start.to_f * 1000.0).to_i
end
+ # Returns HTTP queue start time in milliseconds.
+ #
+ # @return [nil] if no queue start time is found.
+ # @return [nil] if begin time is too low to be plausible.
+ # @return [Integer] queue start in milliseconds.
def http_queue_start
- return unless request.env
- return unless env_var = request.env['HTTP_X_QUEUE_START'.freeze] || request.env['HTTP_X_REQUEST_START'.freeze]
- cleaned_value = env_var.tr('^0-9'.freeze, ''.freeze)
+ env = environment
+ return unless env
+ env_var = env["HTTP_X_QUEUE_START".freeze] || env["HTTP_X_REQUEST_START".freeze]
+ return unless env_var
+ cleaned_value = env_var.tr("^0-9".freeze, "".freeze)
return if cleaned_value.empty?
+
value = cleaned_value.to_i
if value > 4_102_441_200_000
- # Value is in microseconds
+ # Value is in microseconds. Transform to milliseconds.
value / 1_000
elsif value < 946_681_200_000
- # Value is to low to be plausible
+ # Value is too low to be plausible
nil
else
# Value is in milliseconds
value
end
@@ -281,38 +300,76 @@
options[:filter_parameters] = Appsignal.config[:filter_parameters]
end
Appsignal::Utils::ParamsSanitizer.sanitize params, options
end
+ # Returns sanitized environment for a transaction.
+ #
+ # The environment of a transaction can contain a lot of information, not
+ # all of it useful for debugging.
+ #
+ # Only the values from the keys specified in `ENV_METHODS` are returned.
+ #
+ # @return [nil] if no environment is present.
+ # @return [Hash<String, Object>]
def sanitized_environment
- return unless request.env
+ env = environment
+ return if env.empty?
+
{}.tap do |out|
ENV_METHODS.each do |key|
- out[key] = request.env[key] if request.env[key]
+ out[key] = env[key] if env[key]
end
end
end
+ # Returns sanitized session data.
+ #
+ # The session data is sanitized by the {Appsignal::Utils::ParamsSanitizer}.
+ #
+ # @return [nil] if `:skip_session_data` config is set to `true`.
+ # @return [nil] if the {#request} object doesn't respond to `#session`.
+ # @return [nil] if the {#request} session data is `nil`.
+ # @return [Hash<String, Object>]
def sanitized_session_data
- return if Appsignal.config[:skip_session_data] || !request.respond_to?(:session)
- return unless session = request.session
+ return if Appsignal.config[:skip_session_data] ||
+ !request.respond_to?(:session)
+ session = request.session
+ return unless session
+
Appsignal::Utils::ParamsSanitizer.sanitize(session.to_hash)
end
+ # Returns metadata from the environment.
+ #
+ # @return [nil] if no `:metadata` key is present in the {#environment}.
+ # @return [Hash<String, Object>]
def metadata
- return unless request.env
- request.env[:metadata]
+ environment[:metadata]
end
+ # Returns the environment for a transaction.
+ #
+ # Returns an empty Hash when the {#request} object doesn't listen to the
+ # `#env` method or the `#env` is nil.
+ #
+ # @return [Hash<String, Object>]
+ def environment
+ return {} unless request.respond_to?(:env)
+ return {} unless request.env
+
+ request.env
+ end
+
# Only keep tags if they meet the following criteria:
# * Key is a symbol or string with less then 100 chars
# * Value is a symbol or string with less then 100 chars
# * Value is an integer
def sanitized_tags
@tags.select do |k, v|
(k.is_a?(Symbol) || k.is_a?(String) && k.length <= 100) &&
- (((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) || (v.is_a?(Integer)))
+ (((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) || v.is_a?(Integer))
end
end
def cleaned_backtrace(backtrace)
if defined?(::Rails) && backtrace
@@ -327,10 +384,10 @@
class NilTransaction
def method_missing(m, *args, &block)
end
# Instrument should still yield
- def instrument(*args)
+ def instrument(*_args)
yield
end
def nil_transaction?
true