appsignal 2.5.0.alpha.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/.rspec +4 -0
- data/.rubocop.yml +66 -0
- data/.rubocop_todo.yml +124 -0
- data/.travis.yml +72 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +639 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +264 -0
- data/Rakefile +214 -0
- data/appsignal.gemspec +42 -0
- data/benchmark.rake +77 -0
- data/bin/appsignal +13 -0
- data/ext/Rakefile +27 -0
- data/ext/agent.yml +64 -0
- data/ext/appsignal_extension.c +692 -0
- data/ext/base.rb +79 -0
- data/ext/extconf.rb +35 -0
- data/gemfiles/capistrano2.gemfile +7 -0
- data/gemfiles/capistrano3.gemfile +7 -0
- data/gemfiles/grape.gemfile +7 -0
- data/gemfiles/no_dependencies.gemfile +5 -0
- data/gemfiles/padrino.gemfile +7 -0
- data/gemfiles/que.gemfile +5 -0
- data/gemfiles/rails-3.2.gemfile +6 -0
- data/gemfiles/rails-4.0.gemfile +6 -0
- data/gemfiles/rails-4.1.gemfile +6 -0
- data/gemfiles/rails-4.2.gemfile +10 -0
- data/gemfiles/rails-5.0.gemfile +5 -0
- data/gemfiles/rails-5.1.gemfile +5 -0
- data/gemfiles/resque.gemfile +12 -0
- data/gemfiles/sequel-435.gemfile +11 -0
- data/gemfiles/sequel.gemfile +11 -0
- data/gemfiles/sinatra.gemfile +6 -0
- data/gemfiles/webmachine.gemfile +5 -0
- data/lib/appsignal.rb +804 -0
- data/lib/appsignal/auth_check.rb +65 -0
- data/lib/appsignal/capistrano.rb +10 -0
- data/lib/appsignal/cli.rb +108 -0
- data/lib/appsignal/cli/demo.rb +63 -0
- data/lib/appsignal/cli/diagnose.rb +500 -0
- data/lib/appsignal/cli/helpers.rb +72 -0
- data/lib/appsignal/cli/install.rb +277 -0
- data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
- data/lib/appsignal/config.rb +287 -0
- data/lib/appsignal/demo.rb +107 -0
- data/lib/appsignal/event_formatter.rb +74 -0
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
- data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
- data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
- data/lib/appsignal/extension.rb +63 -0
- data/lib/appsignal/extension/jruby.rb +460 -0
- data/lib/appsignal/garbage_collection_profiler.rb +48 -0
- data/lib/appsignal/hooks.rb +105 -0
- data/lib/appsignal/hooks/action_cable.rb +113 -0
- data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
- data/lib/appsignal/hooks/celluloid.rb +30 -0
- data/lib/appsignal/hooks/data_mapper.rb +18 -0
- data/lib/appsignal/hooks/delayed_job.rb +19 -0
- data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
- data/lib/appsignal/hooks/net_http.rb +29 -0
- data/lib/appsignal/hooks/passenger.rb +22 -0
- data/lib/appsignal/hooks/puma.rb +35 -0
- data/lib/appsignal/hooks/que.rb +21 -0
- data/lib/appsignal/hooks/rake.rb +39 -0
- data/lib/appsignal/hooks/redis.rb +30 -0
- data/lib/appsignal/hooks/sequel.rb +60 -0
- data/lib/appsignal/hooks/shoryuken.rb +43 -0
- data/lib/appsignal/hooks/sidekiq.rb +144 -0
- data/lib/appsignal/hooks/unicorn.rb +40 -0
- data/lib/appsignal/hooks/webmachine.rb +23 -0
- data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
- data/lib/appsignal/integrations/data_mapper.rb +33 -0
- data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
- data/lib/appsignal/integrations/grape.rb +53 -0
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
- data/lib/appsignal/integrations/object.rb +35 -0
- data/lib/appsignal/integrations/padrino.rb +84 -0
- data/lib/appsignal/integrations/que.rb +43 -0
- data/lib/appsignal/integrations/railtie.rb +41 -0
- data/lib/appsignal/integrations/rake.rb +2 -0
- data/lib/appsignal/integrations/resque.rb +20 -0
- data/lib/appsignal/integrations/resque_active_job.rb +30 -0
- data/lib/appsignal/integrations/sinatra.rb +17 -0
- data/lib/appsignal/integrations/webmachine.rb +38 -0
- data/lib/appsignal/js_exception_transaction.rb +54 -0
- data/lib/appsignal/marker.rb +63 -0
- data/lib/appsignal/minutely.rb +42 -0
- data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
- data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
- data/lib/appsignal/rack/streaming_listener.rb +73 -0
- data/lib/appsignal/system.rb +81 -0
- data/lib/appsignal/transaction.rb +498 -0
- data/lib/appsignal/transmitter.rb +107 -0
- data/lib/appsignal/utils.rb +127 -0
- data/lib/appsignal/utils/params_sanitizer.rb +59 -0
- data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
- data/lib/appsignal/version.rb +3 -0
- data/lib/sequel/extensions/appsignal_integration.rb +3 -0
- data/resources/appsignal.yml.erb +39 -0
- data/resources/cacert.pem +3866 -0
- data/spec/.rubocop.yml +7 -0
- data/spec/lib/appsignal/auth_check_spec.rb +80 -0
- data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
- data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
- data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
- data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
- data/spec/lib/appsignal/cli/install_spec.rb +632 -0
- data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
- data/spec/lib/appsignal/cli_spec.rb +56 -0
- data/spec/lib/appsignal/config_spec.rb +637 -0
- data/spec/lib/appsignal/demo_spec.rb +87 -0
- data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
- data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
- data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
- data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
- data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
- data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
- data/spec/lib/appsignal/extension_spec.rb +137 -0
- data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
- data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
- data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
- data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
- data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
- data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
- data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
- data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
- data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
- data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
- data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
- data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
- data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
- data/spec/lib/appsignal/hooks_spec.rb +195 -0
- data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
- data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
- data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
- data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
- data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
- data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
- data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
- data/spec/lib/appsignal/marker_spec.rb +51 -0
- data/spec/lib/appsignal/minutely_spec.rb +50 -0
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
- data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
- data/spec/lib/appsignal/system_spec.rb +131 -0
- data/spec/lib/appsignal/transaction_spec.rb +1146 -0
- data/spec/lib/appsignal/transmitter_spec.rb +152 -0
- data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
- data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
- data/spec/lib/appsignal/utils_spec.rb +150 -0
- data/spec/lib/appsignal_spec.rb +1049 -0
- data/spec/spec_helper.rb +116 -0
- data/spec/support/fixtures/containers/cgroups/docker +14 -0
- data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
- data/spec/support/fixtures/containers/cgroups/lxc +10 -0
- data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
- data/spec/support/fixtures/containers/cgroups/none +1 -0
- data/spec/support/fixtures/generated_config.yml +24 -0
- data/spec/support/fixtures/uploaded_file.txt +0 -0
- data/spec/support/helpers/api_request_helper.rb +19 -0
- data/spec/support/helpers/cli_helpers.rb +26 -0
- data/spec/support/helpers/config_helpers.rb +21 -0
- data/spec/support/helpers/dependency_helper.rb +73 -0
- data/spec/support/helpers/directory_helper.rb +27 -0
- data/spec/support/helpers/env_helpers.rb +33 -0
- data/spec/support/helpers/example_exception.rb +13 -0
- data/spec/support/helpers/example_standard_error.rb +13 -0
- data/spec/support/helpers/log_helpers.rb +22 -0
- data/spec/support/helpers/std_streams_helper.rb +66 -0
- data/spec/support/helpers/system_helpers.rb +8 -0
- data/spec/support/helpers/time_helpers.rb +11 -0
- data/spec/support/helpers/transaction_helpers.rb +37 -0
- data/spec/support/matchers/contains_log.rb +7 -0
- data/spec/support/mocks/fake_gc_profiler.rb +19 -0
- data/spec/support/mocks/mock_extension.rb +6 -0
- data/spec/support/project_fixture/config/application.rb +0 -0
- data/spec/support/project_fixture/config/appsignal.yml +32 -0
- data/spec/support/project_fixture/config/environments/development.rb +0 -0
- data/spec/support/project_fixture/config/environments/production.rb +0 -0
- data/spec/support/project_fixture/config/environments/test.rb +0 -0
- data/spec/support/project_fixture/log/.gitkeep +0 -0
- data/spec/support/rails/my_app.rb +6 -0
- data/spec/support/shared_examples/instrument.rb +43 -0
- data/spec/support/stubs/delayed_job.rb +0 -0
- metadata +483 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require "rack"
|
|
2
|
+
|
|
3
|
+
module Appsignal
|
|
4
|
+
module Rack
|
|
5
|
+
# Stub old middleware. Prevents Sinatra middleware being loaded twice.
|
|
6
|
+
# This can happen when users use the old method of including
|
|
7
|
+
# `use Appsignal::Rack::SinatraInstrumentation` in their modular Sinatra
|
|
8
|
+
# applications. This is no longer needed. Instead Appsignal now includes
|
|
9
|
+
# `use Appsignal::Rack::SinatraBaseInstrumentation` automatically.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class SinatraInstrumentation
|
|
13
|
+
def initialize(app, options = {})
|
|
14
|
+
@app = app
|
|
15
|
+
@options = options
|
|
16
|
+
Appsignal.logger.warn "Please remove Appsignal::Rack::SinatraInstrumentation "\
|
|
17
|
+
"from your Sinatra::Base class. This is no longer needed."
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(env)
|
|
21
|
+
@app.call(env)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def settings
|
|
25
|
+
@app.settings
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class SinatraBaseInstrumentation
|
|
30
|
+
attr_reader :raise_errors_on
|
|
31
|
+
|
|
32
|
+
def initialize(app, options = {})
|
|
33
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::SinatraInstrumentation"
|
|
34
|
+
@app = app
|
|
35
|
+
@options = options
|
|
36
|
+
@raise_errors_on = raise_errors?(@app)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def call(env)
|
|
40
|
+
if Appsignal.active?
|
|
41
|
+
call_with_appsignal_monitoring(env)
|
|
42
|
+
else
|
|
43
|
+
@app.call(env)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call_with_appsignal_monitoring(env)
|
|
48
|
+
if @options[:params_method]
|
|
49
|
+
env[:params_method] = @options[:params_method]
|
|
50
|
+
end
|
|
51
|
+
request = @options.fetch(:request_class, Sinatra::Request).new(env)
|
|
52
|
+
transaction = Appsignal::Transaction.create(
|
|
53
|
+
SecureRandom.uuid,
|
|
54
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
|
55
|
+
request,
|
|
56
|
+
:force => @options.include?(:force) && @options[:force]
|
|
57
|
+
)
|
|
58
|
+
begin
|
|
59
|
+
Appsignal.instrument("process_action.sinatra") do
|
|
60
|
+
@app.call(env)
|
|
61
|
+
end
|
|
62
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
|
63
|
+
transaction.set_error(error)
|
|
64
|
+
raise error
|
|
65
|
+
ensure
|
|
66
|
+
# If raise_error is off versions of Sinatra don't raise errors, but store
|
|
67
|
+
# them in the sinatra.error env var.
|
|
68
|
+
if !raise_errors_on && env["sinatra.error"] && !env["sinatra.skip_appsignal_error"]
|
|
69
|
+
transaction.set_error(env["sinatra.error"])
|
|
70
|
+
end
|
|
71
|
+
transaction.set_action_if_nil(action_name(env))
|
|
72
|
+
transaction.set_metadata("path", request.path)
|
|
73
|
+
transaction.set_metadata("method", request.request_method)
|
|
74
|
+
transaction.set_http_or_background_queue_start
|
|
75
|
+
Appsignal::Transaction.complete_current!
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def action_name(env)
|
|
80
|
+
return unless env["sinatra.route"]
|
|
81
|
+
|
|
82
|
+
if env["SCRIPT_NAME"]
|
|
83
|
+
method, route = env["sinatra.route"].split(" ")
|
|
84
|
+
"#{method} #{env["SCRIPT_NAME"]}#{route}"
|
|
85
|
+
else
|
|
86
|
+
env["sinatra.route"]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def raise_errors?(app)
|
|
93
|
+
app.respond_to?(:settings) &&
|
|
94
|
+
app.settings.respond_to?(:raise_errors) &&
|
|
95
|
+
app.settings.raise_errors
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Appsignal
|
|
2
|
+
module Rack
|
|
3
|
+
# Appsignal module that tracks exceptions in Streaming rack responses.
|
|
4
|
+
#
|
|
5
|
+
# @api private
|
|
6
|
+
class StreamingListener
|
|
7
|
+
def initialize(app, options = {})
|
|
8
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::StreamingListener"
|
|
9
|
+
@app = app
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
if Appsignal.active?
|
|
15
|
+
call_with_appsignal_monitoring(env)
|
|
16
|
+
else
|
|
17
|
+
@app.call(env)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call_with_appsignal_monitoring(env)
|
|
22
|
+
request = ::Rack::Request.new(env)
|
|
23
|
+
transaction = Appsignal::Transaction.create(
|
|
24
|
+
SecureRandom.uuid,
|
|
25
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
|
26
|
+
request
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Instrument a `process_action`, to set params/action name
|
|
30
|
+
status, headers, body =
|
|
31
|
+
Appsignal.instrument("process_action.rack") do
|
|
32
|
+
begin
|
|
33
|
+
@app.call(env)
|
|
34
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
35
|
+
transaction.set_error(e)
|
|
36
|
+
raise e
|
|
37
|
+
ensure
|
|
38
|
+
transaction.set_action_if_nil(env["appsignal.action"])
|
|
39
|
+
transaction.set_metadata("path", request.path)
|
|
40
|
+
transaction.set_metadata("method", request.request_method)
|
|
41
|
+
transaction.set_http_or_background_queue_start
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Wrap the result body with our StreamWrapper
|
|
46
|
+
[status, headers, StreamWrapper.new(body, transaction)]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class StreamWrapper
|
|
52
|
+
def initialize(stream, transaction)
|
|
53
|
+
@stream = stream
|
|
54
|
+
@transaction = transaction
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def each
|
|
58
|
+
@stream.each { |c| yield(c) }
|
|
59
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
60
|
+
@transaction.set_error(e)
|
|
61
|
+
raise e
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def close
|
|
65
|
+
@stream.close if @stream.respond_to?(:close)
|
|
66
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
67
|
+
@transaction.set_error(e)
|
|
68
|
+
raise e
|
|
69
|
+
ensure
|
|
70
|
+
Appsignal::Transaction.complete_current!
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Appsignal
|
|
2
|
+
# System environment detection module.
|
|
3
|
+
#
|
|
4
|
+
# Provides useful methods to find out more about the host system.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
module System
|
|
8
|
+
MUSL_TARGET = "linux-musl".freeze
|
|
9
|
+
GEM_EXT_PATH = File.expand_path("../../../ext", __FILE__).freeze
|
|
10
|
+
|
|
11
|
+
def self.heroku?
|
|
12
|
+
ENV.key? "DYNO".freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the architecture for which the agent was installed.
|
|
16
|
+
#
|
|
17
|
+
# This value is saved when the gem is installed in `ext/extconf.rb`.
|
|
18
|
+
# We use this value to build the diagnose report with the installed
|
|
19
|
+
# CPU type and platform, rather than the detected architecture in
|
|
20
|
+
# {.agent_platform} during the diagnose run.
|
|
21
|
+
#
|
|
22
|
+
# @api private
|
|
23
|
+
# @return [String]
|
|
24
|
+
def self.installed_agent_architecture
|
|
25
|
+
architecture_file = File.join(GEM_EXT_PATH, "appsignal.architecture")
|
|
26
|
+
return unless File.exist?(architecture_file)
|
|
27
|
+
File.read(architecture_file)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Detect agent and extension platform build
|
|
31
|
+
#
|
|
32
|
+
# Used by `ext/extconf.rb` to select which build it should download and
|
|
33
|
+
# install.
|
|
34
|
+
#
|
|
35
|
+
# Use `export APPSIGNAL_BUILD_FOR_MUSL=1` if the detection doesn't work
|
|
36
|
+
# and to force selection of the musl build.
|
|
37
|
+
#
|
|
38
|
+
# @api private
|
|
39
|
+
# @return [String]
|
|
40
|
+
def self.agent_platform
|
|
41
|
+
return MUSL_TARGET if ENV["APPSIGNAL_BUILD_FOR_MUSL"]
|
|
42
|
+
|
|
43
|
+
host_os = RbConfig::CONFIG["host_os"].downcase
|
|
44
|
+
local_os =
|
|
45
|
+
case host_os
|
|
46
|
+
when /linux/
|
|
47
|
+
"linux"
|
|
48
|
+
when /darwin/
|
|
49
|
+
"darwin"
|
|
50
|
+
when /freebsd/
|
|
51
|
+
"freebsd"
|
|
52
|
+
else
|
|
53
|
+
host_os
|
|
54
|
+
end
|
|
55
|
+
if local_os =~ /linux/
|
|
56
|
+
ldd_output = ldd_version_output
|
|
57
|
+
return MUSL_TARGET if ldd_output.include? "musl"
|
|
58
|
+
ldd_version = ldd_output.match(/\d+\.\d+/)
|
|
59
|
+
if ldd_version && versionify(ldd_version[0]) < versionify("2.15")
|
|
60
|
+
return MUSL_TARGET
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
local_os
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
def self.versionify(version)
|
|
69
|
+
Gem::Version.new(version)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @api private
|
|
73
|
+
def self.ldd_version_output
|
|
74
|
+
`ldd --version 2>&1`
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.jruby?
|
|
78
|
+
RUBY_PLATFORM == "java"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module Appsignal
|
|
4
|
+
class Transaction
|
|
5
|
+
HTTP_REQUEST = "http_request".freeze
|
|
6
|
+
BACKGROUND_JOB = "background_job".freeze
|
|
7
|
+
ACTION_CABLE = "action_cable".freeze
|
|
8
|
+
FRONTEND = "frontend".freeze
|
|
9
|
+
BLANK = "".freeze
|
|
10
|
+
|
|
11
|
+
# Based on what Rails uses + some variables we'd like to show
|
|
12
|
+
ENV_METHODS = %w[
|
|
13
|
+
CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
|
|
14
|
+
PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
|
15
|
+
REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL REQUEST_URI
|
|
16
|
+
PATH_INFO
|
|
17
|
+
|
|
18
|
+
HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
|
|
19
|
+
HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
|
|
20
|
+
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
|
|
21
|
+
HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM
|
|
22
|
+
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR
|
|
23
|
+
HTTP_CLIENT_IP HTTP_RANGE
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def create(id, namespace, request, options = {})
|
|
28
|
+
# Allow middleware to force a new transaction
|
|
29
|
+
if options.include?(:force) && options[:force]
|
|
30
|
+
Thread.current[:appsignal_transaction] = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Check if we already have a running transaction
|
|
34
|
+
if Thread.current[:appsignal_transaction] != nil
|
|
35
|
+
# Log the issue and return the current transaction
|
|
36
|
+
Appsignal.logger.debug "Trying to start new transaction with id " \
|
|
37
|
+
"'#{id}', but a transaction with id '#{current.transaction_id}' " \
|
|
38
|
+
"is already running. Using transaction '#{current.transaction_id}'."
|
|
39
|
+
|
|
40
|
+
# Return the current (running) transaction
|
|
41
|
+
current
|
|
42
|
+
else
|
|
43
|
+
# Otherwise, start a new transaction
|
|
44
|
+
Thread.current[:appsignal_transaction] = Appsignal::Transaction.new(id, namespace, request, options)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def current
|
|
49
|
+
Thread.current[:appsignal_transaction] || NilTransaction.new
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def complete_current!
|
|
53
|
+
current.complete
|
|
54
|
+
rescue => e
|
|
55
|
+
Appsignal.logger.error("Failed to complete transaction ##{current.transaction_id}. #{e.message}")
|
|
56
|
+
ensure
|
|
57
|
+
clear_current_transaction!
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Remove current transaction from current Thread.
|
|
61
|
+
# @api private
|
|
62
|
+
def clear_current_transaction!
|
|
63
|
+
Thread.current[:appsignal_transaction] = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def garbage_collection_profiler
|
|
67
|
+
@garbage_collection_profiler ||= Appsignal::GarbageCollectionProfiler.new
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded
|
|
72
|
+
|
|
73
|
+
# @!attribute params
|
|
74
|
+
# Attribute for parameters of the transaction.
|
|
75
|
+
#
|
|
76
|
+
# When no parameters are set with {#params=} the parameters it will look
|
|
77
|
+
# for parameters on the {#request} environment.
|
|
78
|
+
#
|
|
79
|
+
# The parameters set using {#params=} are leading over those extracted
|
|
80
|
+
# from a request's environment.
|
|
81
|
+
#
|
|
82
|
+
# @return [Hash]
|
|
83
|
+
attr_writer :params
|
|
84
|
+
|
|
85
|
+
def initialize(transaction_id, namespace, request, options = {})
|
|
86
|
+
@transaction_id = transaction_id
|
|
87
|
+
@action = nil
|
|
88
|
+
@namespace = namespace
|
|
89
|
+
@request = request
|
|
90
|
+
@paused = false
|
|
91
|
+
@discarded = false
|
|
92
|
+
@tags = {}
|
|
93
|
+
@store = Hash.new({})
|
|
94
|
+
@options = options
|
|
95
|
+
@options[:params_method] ||= :params
|
|
96
|
+
|
|
97
|
+
@ext = Appsignal::Extension.start_transaction(
|
|
98
|
+
@transaction_id,
|
|
99
|
+
@namespace,
|
|
100
|
+
self.class.garbage_collection_profiler.total_time
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def nil_transaction?
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def complete
|
|
109
|
+
if discarded?
|
|
110
|
+
Appsignal.logger.debug "Skipping transaction '#{transaction_id}' " \
|
|
111
|
+
"because it was manually discarded."
|
|
112
|
+
return
|
|
113
|
+
end
|
|
114
|
+
if @ext.finish(self.class.garbage_collection_profiler.total_time)
|
|
115
|
+
sample_data
|
|
116
|
+
end
|
|
117
|
+
@ext.complete
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def pause!
|
|
121
|
+
@paused = true
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def resume!
|
|
125
|
+
@paused = false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def paused?
|
|
129
|
+
@paused == true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def discard!
|
|
133
|
+
@discarded = true
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def restore!
|
|
137
|
+
@discarded = false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def discarded?
|
|
141
|
+
@discarded == true
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def store(key)
|
|
145
|
+
@store[key]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def params
|
|
149
|
+
return @params if defined?(@params)
|
|
150
|
+
request_params
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Set tags on the transaction.
|
|
154
|
+
#
|
|
155
|
+
# @param given_tags [Hash] Collection of tags.
|
|
156
|
+
# @option given_tags [String, Symbol, Integer] :any
|
|
157
|
+
# The name of the tag as a Symbol.
|
|
158
|
+
# @option given_tags [String, Symbol, Integer] "any"
|
|
159
|
+
# The name of the tag as a String.
|
|
160
|
+
# @return [void]
|
|
161
|
+
#
|
|
162
|
+
# @see Appsignal.tag_request
|
|
163
|
+
# @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
|
|
164
|
+
# Tagging guide
|
|
165
|
+
def set_tags(given_tags = {})
|
|
166
|
+
@tags.merge!(given_tags)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Set an action name for the transaction.
|
|
170
|
+
#
|
|
171
|
+
# An action name is used to identify the location of a certain sample;
|
|
172
|
+
# error and performance issues.
|
|
173
|
+
#
|
|
174
|
+
# @param action [String] the action name to set.
|
|
175
|
+
# @return [void]
|
|
176
|
+
# @see Appsignal.set_action
|
|
177
|
+
# @see #set_action_if_nil
|
|
178
|
+
# @since 2.2.0
|
|
179
|
+
def set_action(action)
|
|
180
|
+
return unless action
|
|
181
|
+
@action = action
|
|
182
|
+
@ext.set_action(action)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Set an action name only if there is no current action set.
|
|
186
|
+
#
|
|
187
|
+
# Commonly used by AppSignal integrations so that they don't override
|
|
188
|
+
# custom action names.
|
|
189
|
+
#
|
|
190
|
+
# @example
|
|
191
|
+
# Appsignal.set_action("foo")
|
|
192
|
+
# Appsignal.set_action_if_nil("bar")
|
|
193
|
+
# # Transaction action will be "foo"
|
|
194
|
+
#
|
|
195
|
+
# @param action [String]
|
|
196
|
+
# @return [void]
|
|
197
|
+
# @see #set_action
|
|
198
|
+
# @since 2.2.0
|
|
199
|
+
def set_action_if_nil(action)
|
|
200
|
+
return if @action
|
|
201
|
+
set_action(action)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Set the namespace for this transaction.
|
|
205
|
+
#
|
|
206
|
+
# Useful to split up parts of an application into certain namespaces. For
|
|
207
|
+
# example: http requests, background jobs and administration panel
|
|
208
|
+
# controllers.
|
|
209
|
+
#
|
|
210
|
+
# Note: The "http_request" namespace gets transformed on AppSignal.com to
|
|
211
|
+
# "Web" and "background_job" gets transformed to "Background".
|
|
212
|
+
#
|
|
213
|
+
# @example
|
|
214
|
+
# transaction.set_action("admin")
|
|
215
|
+
#
|
|
216
|
+
# @param namespace [String] namespace name to use for this transaction.
|
|
217
|
+
# @return [void]
|
|
218
|
+
# @since 2.2.0
|
|
219
|
+
def set_namespace(namespace)
|
|
220
|
+
return unless namespace
|
|
221
|
+
@namespace = namespace
|
|
222
|
+
@ext.set_namespace(namespace)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def set_http_or_background_action(from = request.params)
|
|
226
|
+
return unless from
|
|
227
|
+
group_and_action = [
|
|
228
|
+
from[:controller] || from[:class],
|
|
229
|
+
from[:action] || from[:method]
|
|
230
|
+
]
|
|
231
|
+
set_action(group_and_action.compact.join("#"))
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def set_queue_start(start)
|
|
235
|
+
return unless start
|
|
236
|
+
@ext.set_queue_start(start)
|
|
237
|
+
rescue RangeError
|
|
238
|
+
Appsignal.logger.warn("Queue start value #{start} is too big")
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def set_http_or_background_queue_start
|
|
242
|
+
if namespace == HTTP_REQUEST
|
|
243
|
+
set_queue_start(http_queue_start)
|
|
244
|
+
elsif namespace == BACKGROUND_JOB
|
|
245
|
+
set_queue_start(background_queue_start)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def set_metadata(key, value)
|
|
250
|
+
return unless key && value
|
|
251
|
+
@ext.set_metadata(key, value)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def set_sample_data(key, data)
|
|
255
|
+
return unless key && data && (data.is_a?(Array) || data.is_a?(Hash))
|
|
256
|
+
@ext.set_sample_data(
|
|
257
|
+
key.to_s,
|
|
258
|
+
Appsignal::Utils.data_generate(data)
|
|
259
|
+
)
|
|
260
|
+
rescue RuntimeError => e
|
|
261
|
+
Appsignal.logger.error("Error generating data (#{e.class}: #{e.message}) for '#{data.inspect}'")
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def sample_data
|
|
265
|
+
{
|
|
266
|
+
:params => sanitized_params,
|
|
267
|
+
:environment => sanitized_environment,
|
|
268
|
+
:session_data => sanitized_session_data,
|
|
269
|
+
:metadata => metadata,
|
|
270
|
+
:tags => sanitized_tags
|
|
271
|
+
}.each do |key, data|
|
|
272
|
+
set_sample_data(key, data)
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def set_error(error)
|
|
277
|
+
return unless error
|
|
278
|
+
return unless Appsignal.active?
|
|
279
|
+
|
|
280
|
+
backtrace = cleaned_backtrace(error.backtrace)
|
|
281
|
+
@ext.set_error(
|
|
282
|
+
error.class.name,
|
|
283
|
+
error.message.to_s,
|
|
284
|
+
backtrace ? Appsignal::Utils.data_generate(backtrace) : Appsignal::Extension.data_array_new
|
|
285
|
+
)
|
|
286
|
+
end
|
|
287
|
+
alias_method :add_exception, :set_error
|
|
288
|
+
|
|
289
|
+
def start_event
|
|
290
|
+
@ext.start_event(self.class.garbage_collection_profiler.total_time)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
|
|
294
|
+
@ext.finish_event(
|
|
295
|
+
name,
|
|
296
|
+
title || BLANK,
|
|
297
|
+
body || BLANK,
|
|
298
|
+
body_format || Appsignal::EventFormatter::DEFAULT,
|
|
299
|
+
self.class.garbage_collection_profiler.total_time
|
|
300
|
+
)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
|
|
304
|
+
@ext.record_event(
|
|
305
|
+
name,
|
|
306
|
+
title || BLANK,
|
|
307
|
+
body || BLANK,
|
|
308
|
+
body_format || Appsignal::EventFormatter::DEFAULT,
|
|
309
|
+
duration,
|
|
310
|
+
self.class.garbage_collection_profiler.total_time
|
|
311
|
+
)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
|
|
315
|
+
start_event
|
|
316
|
+
yield if block_given?
|
|
317
|
+
ensure
|
|
318
|
+
finish_event(name, title, body, body_format)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# @api private
|
|
322
|
+
def to_h
|
|
323
|
+
JSON.parse(@ext.to_json)
|
|
324
|
+
end
|
|
325
|
+
alias_method :to_hash, :to_h
|
|
326
|
+
|
|
327
|
+
class GenericRequest
|
|
328
|
+
attr_reader :env
|
|
329
|
+
|
|
330
|
+
def initialize(env)
|
|
331
|
+
@env = env
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def params
|
|
335
|
+
env[:params]
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
private
|
|
340
|
+
|
|
341
|
+
# Returns calculated background queue start time in milliseconds, based on
|
|
342
|
+
# environment values.
|
|
343
|
+
#
|
|
344
|
+
# @return [nil] if no {#environment} is present.
|
|
345
|
+
# @return [nil] if there is no `:queue_start` in the {#environment}.
|
|
346
|
+
# @return [Integer]
|
|
347
|
+
def background_queue_start
|
|
348
|
+
env = environment
|
|
349
|
+
return unless env
|
|
350
|
+
queue_start = env[:queue_start]
|
|
351
|
+
return unless queue_start
|
|
352
|
+
|
|
353
|
+
(queue_start.to_f * 1000.0).to_i
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Returns HTTP queue start time in milliseconds.
|
|
357
|
+
#
|
|
358
|
+
# @return [nil] if no queue start time is found.
|
|
359
|
+
# @return [nil] if begin time is too low to be plausible.
|
|
360
|
+
# @return [Integer] queue start in milliseconds.
|
|
361
|
+
def http_queue_start
|
|
362
|
+
env = environment
|
|
363
|
+
return unless env
|
|
364
|
+
env_var = env["HTTP_X_QUEUE_START".freeze] || env["HTTP_X_REQUEST_START".freeze]
|
|
365
|
+
return unless env_var
|
|
366
|
+
cleaned_value = env_var.tr("^0-9".freeze, "".freeze)
|
|
367
|
+
return if cleaned_value.empty?
|
|
368
|
+
|
|
369
|
+
value = cleaned_value.to_i
|
|
370
|
+
if value > 4_102_441_200_000
|
|
371
|
+
# Value is in microseconds. Transform to milliseconds.
|
|
372
|
+
value / 1_000
|
|
373
|
+
elsif value < 946_681_200_000
|
|
374
|
+
# Value is too low to be plausible
|
|
375
|
+
nil
|
|
376
|
+
else
|
|
377
|
+
# Value is in milliseconds
|
|
378
|
+
value
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def sanitized_params
|
|
383
|
+
return unless Appsignal.config[:send_params]
|
|
384
|
+
|
|
385
|
+
options = {}
|
|
386
|
+
if Appsignal.config[:filter_parameters]
|
|
387
|
+
options[:filter_parameters] = Appsignal.config[:filter_parameters]
|
|
388
|
+
end
|
|
389
|
+
Appsignal::Utils::ParamsSanitizer.sanitize params, options
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def request_params
|
|
393
|
+
return unless request.respond_to?(options[:params_method])
|
|
394
|
+
|
|
395
|
+
begin
|
|
396
|
+
request.send options[:params_method]
|
|
397
|
+
rescue => e
|
|
398
|
+
# Getting params from the request has been know to fail.
|
|
399
|
+
Appsignal.logger.debug "Exception while getting params: #{e}"
|
|
400
|
+
nil
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# Returns sanitized environment for a transaction.
|
|
405
|
+
#
|
|
406
|
+
# The environment of a transaction can contain a lot of information, not
|
|
407
|
+
# all of it useful for debugging.
|
|
408
|
+
#
|
|
409
|
+
# Only the values from the keys specified in {ENV_METHODS} are returned.
|
|
410
|
+
#
|
|
411
|
+
# @return [nil] if no environment is present.
|
|
412
|
+
# @return [Hash<String, Object>]
|
|
413
|
+
def sanitized_environment
|
|
414
|
+
env = environment
|
|
415
|
+
return if env.empty?
|
|
416
|
+
|
|
417
|
+
{}.tap do |out|
|
|
418
|
+
ENV_METHODS.each do |key|
|
|
419
|
+
out[key] = env[key] if env[key]
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Returns sanitized session data.
|
|
425
|
+
#
|
|
426
|
+
# The session data is sanitized by the {Appsignal::Utils::ParamsSanitizer}.
|
|
427
|
+
#
|
|
428
|
+
# @return [nil] if `:skip_session_data` config is set to `true`.
|
|
429
|
+
# @return [nil] if the {#request} object doesn't respond to `#session`.
|
|
430
|
+
# @return [nil] if the {#request} session data is `nil`.
|
|
431
|
+
# @return [Hash<String, Object>]
|
|
432
|
+
def sanitized_session_data
|
|
433
|
+
return if Appsignal.config[:skip_session_data] ||
|
|
434
|
+
!request.respond_to?(:session)
|
|
435
|
+
session = request.session
|
|
436
|
+
return unless session
|
|
437
|
+
|
|
438
|
+
Appsignal::Utils::ParamsSanitizer.sanitize(session.to_hash)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Returns metadata from the environment.
|
|
442
|
+
#
|
|
443
|
+
# @return [nil] if no `:metadata` key is present in the {#environment}.
|
|
444
|
+
# @return [Hash<String, Object>]
|
|
445
|
+
def metadata
|
|
446
|
+
environment[:metadata]
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Returns the environment for a transaction.
|
|
450
|
+
#
|
|
451
|
+
# Returns an empty Hash when the {#request} object doesn't listen to the
|
|
452
|
+
# `#env` method or the `#env` is nil.
|
|
453
|
+
#
|
|
454
|
+
# @return [Hash<String, Object>]
|
|
455
|
+
def environment
|
|
456
|
+
return {} unless request.respond_to?(:env)
|
|
457
|
+
return {} unless request.env
|
|
458
|
+
|
|
459
|
+
request.env
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Only keep tags if they meet the following criteria:
|
|
463
|
+
# * Key is a symbol or string with less then 100 chars
|
|
464
|
+
# * Value is a symbol or string with less then 100 chars
|
|
465
|
+
# * Value is an integer
|
|
466
|
+
def sanitized_tags
|
|
467
|
+
@tags.select do |k, v|
|
|
468
|
+
(k.is_a?(Symbol) || k.is_a?(String) && k.length <= 100) &&
|
|
469
|
+
(((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) || v.is_a?(Integer))
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def cleaned_backtrace(backtrace)
|
|
474
|
+
if defined?(::Rails) && backtrace
|
|
475
|
+
::Rails.backtrace_cleaner.clean(backtrace, nil)
|
|
476
|
+
else
|
|
477
|
+
backtrace
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Stub that is returned by {Transaction.current} if there is no current
|
|
482
|
+
# transaction, so that it's still safe to call methods on it if there is no
|
|
483
|
+
# current transaction.
|
|
484
|
+
class NilTransaction
|
|
485
|
+
def method_missing(m, *args, &block)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Instrument should still yield
|
|
489
|
+
def instrument(*_args)
|
|
490
|
+
yield
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def nil_transaction?
|
|
494
|
+
true
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|