appsignal 2.1.0.alpha.3 → 2.1.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +43 -0
- data/.rubocop_todo.yml +289 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +11 -1
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/appsignal.gemspec +1 -0
- data/bin/appsignal +2 -2
- data/ext/agent.yml +11 -11
- data/ext/extconf.rb +33 -33
- data/gemfiles/rails-4.2.gemfile +4 -0
- data/gemfiles/resque.gemfile +4 -0
- data/lib/appsignal.rb +58 -54
- data/lib/appsignal/auth_check.rb +15 -16
- data/lib/appsignal/capistrano.rb +4 -4
- data/lib/appsignal/cli.rb +28 -28
- data/lib/appsignal/cli/diagnose.rb +11 -11
- data/lib/appsignal/cli/helpers.rb +1 -1
- data/lib/appsignal/cli/install.rb +38 -38
- data/lib/appsignal/cli/notify_of_deploy.rb +1 -1
- data/lib/appsignal/config.rb +73 -71
- data/lib/appsignal/event_formatter.rb +8 -8
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +4 -4
- data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +9 -7
- data/lib/appsignal/event_formatter/faraday/request_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +6 -6
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +56 -43
- data/lib/appsignal/extension.rb +5 -5
- data/lib/appsignal/hooks.rb +28 -26
- data/lib/appsignal/hooks/active_support_notifications.rb +2 -2
- data/lib/appsignal/hooks/celluloid.rb +1 -1
- data/lib/appsignal/hooks/data_mapper.rb +2 -2
- data/lib/appsignal/hooks/delayed_job.rb +1 -1
- data/lib/appsignal/hooks/mongo_ruby_driver.rb +1 -1
- data/lib/appsignal/hooks/net_http.rb +4 -4
- data/lib/appsignal/hooks/passenger.rb +2 -2
- data/lib/appsignal/hooks/puma.rb +4 -4
- data/lib/appsignal/hooks/rake.rb +1 -1
- data/lib/appsignal/hooks/redis.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +2 -2
- data/lib/appsignal/hooks/shoryuken.rb +8 -8
- data/lib/appsignal/hooks/sidekiq.rb +16 -15
- data/lib/appsignal/hooks/unicorn.rb +1 -1
- data/lib/appsignal/hooks/webmachine.rb +1 -1
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +6 -6
- data/lib/appsignal/integrations/data_mapper.rb +2 -3
- data/lib/appsignal/integrations/delayed_job_plugin.rb +5 -5
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +6 -6
- data/lib/appsignal/integrations/padrino.rb +8 -8
- data/lib/appsignal/integrations/railtie.rb +5 -5
- data/lib/appsignal/integrations/resque.rb +4 -5
- data/lib/appsignal/integrations/resque_active_job.rb +3 -6
- data/lib/appsignal/integrations/sinatra.rb +2 -2
- data/lib/appsignal/integrations/webmachine.rb +2 -3
- data/lib/appsignal/js_exception_transaction.rb +10 -10
- data/lib/appsignal/marker.rb +3 -3
- data/lib/appsignal/rack/generic_instrumentation.rb +10 -9
- data/lib/appsignal/rack/js_exception_catcher.rb +7 -6
- data/lib/appsignal/rack/rails_instrumentation.rb +9 -8
- data/lib/appsignal/rack/sinatra_instrumentation.rb +19 -17
- data/lib/appsignal/rack/streaming_listener.rb +9 -8
- data/lib/appsignal/system.rb +19 -17
- data/lib/appsignal/transaction.rb +97 -40
- data/lib/appsignal/transmitter.rb +23 -30
- data/lib/appsignal/utils.rb +3 -3
- data/lib/appsignal/utils/params_sanitizer.rb +1 -1
- data/lib/appsignal/utils/query_params_sanitizer.rb +1 -1
- data/lib/appsignal/version.rb +2 -2
- data/spec/.rubocop.yml +4 -0
- data/spec/lib/appsignal/auth_check_spec.rb +7 -7
- data/spec/lib/appsignal/capistrano2_spec.rb +41 -41
- data/spec/lib/appsignal/capistrano3_spec.rb +43 -44
- data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +9 -11
- data/spec/lib/appsignal/cli_spec.rb +9 -9
- data/spec/lib/appsignal/config_spec.rb +88 -86
- data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +9 -9
- data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +4 -4
- data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +4 -4
- data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +12 -12
- data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +4 -4
- data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +23 -23
- data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +29 -30
- data/spec/lib/appsignal/event_formatter_spec.rb +28 -28
- data/spec/lib/appsignal/extension_spec.rb +15 -15
- data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +6 -5
- data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +12 -12
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +34 -34
- data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +3 -3
- data/spec/lib/appsignal/hooks/net_http_spec.rb +10 -10
- data/spec/lib/appsignal/hooks/rake_spec.rb +7 -7
- data/spec/lib/appsignal/hooks/redis_spec.rb +6 -6
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +21 -22
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +48 -45
- data/spec/lib/appsignal/hooks/webmachine_spec.rb +2 -2
- data/spec/lib/appsignal/hooks_spec.rb +16 -17
- data/spec/lib/appsignal/integrations/data_mapper_spec.rb +7 -8
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +19 -19
- data/spec/lib/appsignal/integrations/object_spec.rb +3 -3
- data/spec/lib/appsignal/integrations/padrino_spec.rb +44 -44
- data/spec/lib/appsignal/integrations/railtie_spec.rb +13 -13
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +7 -7
- data/spec/lib/appsignal/integrations/resque_spec.rb +8 -8
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +7 -7
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +15 -15
- data/spec/lib/appsignal/js_exception_transaction_spec.rb +23 -23
- data/spec/lib/appsignal/marker_spec.rb +8 -8
- data/spec/lib/appsignal/minutely_spec.rb +6 -6
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +7 -7
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +13 -13
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +11 -11
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +21 -21
- data/spec/lib/appsignal/rack/streaming_listener_spec.rb +36 -36
- data/spec/lib/appsignal/system/container_spec.rb +1 -1
- data/spec/lib/appsignal/transaction_spec.rb +213 -184
- data/spec/lib/appsignal/transmitter_spec.rb +20 -20
- data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +19 -19
- data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +41 -41
- data/spec/lib/appsignal/utils_spec.rb +41 -37
- data/spec/lib/appsignal_spec.rb +150 -142
- data/spec/spec_helper.rb +18 -18
- data/spec/support/delegate_matcher.rb +6 -6
- data/spec/support/helpers/api_request_helper.rb +2 -2
- data/spec/support/helpers/config_helpers.rb +3 -3
- data/spec/support/helpers/dependency_helper.rb +13 -13
- data/spec/support/helpers/directory_helper.rb +5 -5
- data/spec/support/helpers/env_helpers.rb +14 -14
- data/spec/support/helpers/system_helpers.rb +3 -3
- data/spec/support/helpers/transaction_helpers.rb +10 -10
- metadata +20 -2
@@ -1,18 +1,17 @@
|
|
1
1
|
module Appsignal::Integrations
|
2
2
|
module WebmachinePlugin
|
3
3
|
module FSM
|
4
|
-
|
5
4
|
def run_with_appsignal
|
6
5
|
transaction = Appsignal::Transaction.create(
|
7
6
|
SecureRandom.uuid,
|
8
7
|
Appsignal::Transaction::HTTP_REQUEST,
|
9
8
|
request,
|
10
|
-
|
9
|
+
:params_method => :query
|
11
10
|
)
|
12
11
|
|
13
12
|
transaction.set_action("#{resource.class.name}##{request.method}")
|
14
13
|
|
15
|
-
Appsignal.instrument(
|
14
|
+
Appsignal.instrument("process_action.webmachine") do
|
16
15
|
run_without_appsignal
|
17
16
|
end
|
18
17
|
|
@@ -14,29 +14,29 @@ module Appsignal
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def set_action
|
17
|
-
@ext.set_action(@data[
|
17
|
+
@ext.set_action(@data["action"]) if @data["action"]
|
18
18
|
end
|
19
19
|
|
20
20
|
def set_metadata
|
21
21
|
@ext.set_metadata(
|
22
|
-
|
23
|
-
) if @data[
|
22
|
+
"path", @data["path"]
|
23
|
+
) if @data["path"]
|
24
24
|
end
|
25
25
|
|
26
26
|
def set_error
|
27
27
|
@ext.set_error(
|
28
|
-
@data[
|
29
|
-
@data[
|
30
|
-
Appsignal::Utils.data_generate(@data[
|
28
|
+
@data["name"],
|
29
|
+
@data["message"] || "",
|
30
|
+
Appsignal::Utils.data_generate(@data["backtrace"] || [])
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
34
34
|
def set_sample_data
|
35
35
|
{
|
36
|
-
:params => @data[
|
37
|
-
:session_data => @data[
|
38
|
-
:environment => @data[
|
39
|
-
:tags => @data[
|
36
|
+
:params => @data["params"],
|
37
|
+
:session_data => @data["session_data"],
|
38
|
+
:environment => @data["environment"],
|
39
|
+
:tags => @data["tags"]
|
40
40
|
}.each do |key, data|
|
41
41
|
next unless data.is_a?(Array) || data.is_a?(Hash)
|
42
42
|
@ext.set_sample_data(
|
data/lib/appsignal/marker.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class Marker
|
3
3
|
attr_reader :marker_data, :config
|
4
|
-
ACTION =
|
4
|
+
ACTION = "markers"
|
5
5
|
|
6
6
|
def initialize(marker_data, config)
|
7
7
|
@marker_data = marker_data
|
@@ -14,8 +14,8 @@ module Appsignal
|
|
14
14
|
"revision: #{marker_data[:revision]}, user: #{marker_data[:user]}"
|
15
15
|
|
16
16
|
result = transmitter.transmit(marker_data)
|
17
|
-
if result ==
|
18
|
-
puts
|
17
|
+
if result == "200"
|
18
|
+
puts "AppSignal has been notified of this deploy!"
|
19
19
|
else
|
20
20
|
raise "#{result} at #{transmitter.uri}"
|
21
21
|
end
|
@@ -1,11 +1,12 @@
|
|
1
|
-
require
|
1
|
+
require "rack"
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Rack
|
5
5
|
class GenericInstrumentation
|
6
6
|
def initialize(app, options = {})
|
7
|
-
Appsignal.logger.debug
|
8
|
-
@app
|
7
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::GenericInstrumentation"
|
8
|
+
@app = app
|
9
|
+
@options = options
|
9
10
|
end
|
10
11
|
|
11
12
|
def call(env)
|
@@ -24,20 +25,20 @@ module Appsignal
|
|
24
25
|
request
|
25
26
|
)
|
26
27
|
begin
|
27
|
-
Appsignal.instrument(
|
28
|
+
Appsignal.instrument("process_action.generic") do
|
28
29
|
@app.call(env)
|
29
30
|
end
|
30
31
|
rescue => error
|
31
32
|
transaction.set_error(error)
|
32
33
|
raise error
|
33
34
|
ensure
|
34
|
-
if env[
|
35
|
-
transaction.set_action(env[
|
35
|
+
if env["appsignal.route"]
|
36
|
+
transaction.set_action(env["appsignal.route"])
|
36
37
|
else
|
37
|
-
transaction.set_action(
|
38
|
+
transaction.set_action("unknown")
|
38
39
|
end
|
39
|
-
transaction.set_metadata(
|
40
|
-
transaction.set_metadata(
|
40
|
+
transaction.set_metadata("path", request.path)
|
41
|
+
transaction.set_metadata("method", request.request_method)
|
41
42
|
transaction.set_http_or_background_queue_start
|
42
43
|
Appsignal::Transaction.complete_current!
|
43
44
|
end
|
@@ -2,15 +2,16 @@ module Appsignal
|
|
2
2
|
module Rack
|
3
3
|
class JSExceptionCatcher
|
4
4
|
def initialize(app, options = {})
|
5
|
-
Appsignal.logger.debug
|
6
|
-
@app
|
5
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::JSExceptionCatcher"
|
6
|
+
@app = app
|
7
|
+
@options = options
|
7
8
|
end
|
8
9
|
|
9
10
|
def call(env)
|
10
|
-
if env[
|
11
|
-
body = JSON.parse(env[
|
11
|
+
if env["PATH_INFO"] == Appsignal.config[:frontend_error_catching_path]
|
12
|
+
body = JSON.parse(env["rack.input"].read)
|
12
13
|
|
13
|
-
if body[
|
14
|
+
if body["name"].is_a?(String) && !body["name"].empty?
|
14
15
|
transaction = JSExceptionTransaction.new(body)
|
15
16
|
transaction.complete!
|
16
17
|
code = 200
|
@@ -19,7 +20,7 @@ module Appsignal
|
|
19
20
|
code = 422
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
[code, {}, []]
|
23
24
|
else
|
24
25
|
@app.call(env)
|
25
26
|
end
|
@@ -1,11 +1,12 @@
|
|
1
|
-
require
|
1
|
+
require "rack"
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Rack
|
5
5
|
class RailsInstrumentation
|
6
6
|
def initialize(app, options = {})
|
7
|
-
Appsignal.logger.debug
|
8
|
-
@app
|
7
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::RailsInstrumentation"
|
8
|
+
@app = app
|
9
|
+
@options = options
|
9
10
|
end
|
10
11
|
|
11
12
|
def call(env)
|
@@ -30,19 +31,19 @@ module Appsignal
|
|
30
31
|
transaction.set_error(error)
|
31
32
|
raise error
|
32
33
|
ensure
|
33
|
-
controller = env[
|
34
|
+
controller = env["action_controller.instance"]
|
34
35
|
if controller
|
35
|
-
transaction.set_action("#{controller.class
|
36
|
+
transaction.set_action("#{controller.class}##{controller.action_name}")
|
36
37
|
end
|
37
38
|
transaction.set_http_or_background_queue_start
|
38
|
-
transaction.set_metadata(
|
39
|
-
transaction.set_metadata(
|
39
|
+
transaction.set_metadata("path", request.path)
|
40
|
+
transaction.set_metadata("method", request.request_method)
|
40
41
|
Appsignal::Transaction.complete_current!
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
45
|
def request_id(env)
|
45
|
-
env[
|
46
|
+
env["action_dispatch.request_id"] || SecureRandom.uuid
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "rack"
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Rack
|
@@ -9,9 +9,10 @@ module Appsignal
|
|
9
9
|
# `use Appsignal::Rack::SinatraBaseInstrumentation` automatically.
|
10
10
|
class SinatraInstrumentation
|
11
11
|
def initialize(app, options = {})
|
12
|
-
@app
|
13
|
-
|
14
|
-
|
12
|
+
@app = app
|
13
|
+
@options = options
|
14
|
+
Appsignal.logger.warn "Please remove Appsignal::Rack::SinatraInstrumentation "\
|
15
|
+
"from your Sinatra::Base class. This is no longer needed."
|
15
16
|
end
|
16
17
|
|
17
18
|
def call(env)
|
@@ -27,8 +28,9 @@ module Appsignal
|
|
27
28
|
attr_reader :raise_errors_on
|
28
29
|
|
29
30
|
def initialize(app, options = {})
|
30
|
-
Appsignal.logger.debug
|
31
|
-
@app
|
31
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::SinatraInstrumentation"
|
32
|
+
@app = app
|
33
|
+
@options = options
|
32
34
|
@raise_errors_on = raise_errors?(@app)
|
33
35
|
end
|
34
36
|
|
@@ -49,10 +51,10 @@ module Appsignal
|
|
49
51
|
SecureRandom.uuid,
|
50
52
|
Appsignal::Transaction::HTTP_REQUEST,
|
51
53
|
request,
|
52
|
-
|
54
|
+
:force => @options.include?(:force) && @options[:force]
|
53
55
|
)
|
54
56
|
begin
|
55
|
-
Appsignal.instrument(
|
57
|
+
Appsignal.instrument("process_action.sinatra") do
|
56
58
|
@app.call(env)
|
57
59
|
end
|
58
60
|
rescue => error
|
@@ -61,25 +63,25 @@ module Appsignal
|
|
61
63
|
ensure
|
62
64
|
# If raise_error is off versions of Sinatra don't raise errors, but store
|
63
65
|
# them in the sinatra.error env var.
|
64
|
-
if !@raise_errors_on && env[
|
65
|
-
transaction.set_error(env[
|
66
|
+
if !@raise_errors_on && env["sinatra.error"] && !env["sinatra.skip_appsignal_error"]
|
67
|
+
transaction.set_error(env["sinatra.error"])
|
66
68
|
end
|
67
69
|
transaction.set_action(action_name(env))
|
68
|
-
transaction.set_metadata(
|
69
|
-
transaction.set_metadata(
|
70
|
+
transaction.set_metadata("path", request.path)
|
71
|
+
transaction.set_metadata("method", request.request_method)
|
70
72
|
transaction.set_http_or_background_queue_start
|
71
73
|
Appsignal::Transaction.complete_current!
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
75
77
|
def action_name(env)
|
76
|
-
return unless env[
|
78
|
+
return unless env["sinatra.route"]
|
77
79
|
|
78
|
-
if env[
|
79
|
-
method, route = env[
|
80
|
-
"#{method} #{env[
|
80
|
+
if env["SCRIPT_NAME"]
|
81
|
+
method, route = env["sinatra.route"].split(" ")
|
82
|
+
"#{method} #{env["SCRIPT_NAME"]}#{route}"
|
81
83
|
else
|
82
|
-
env[
|
84
|
+
env["sinatra.route"]
|
83
85
|
end
|
84
86
|
end
|
85
87
|
|
@@ -3,8 +3,9 @@ module Appsignal
|
|
3
3
|
module Rack
|
4
4
|
class StreamingListener
|
5
5
|
def initialize(app, options = {})
|
6
|
-
Appsignal.logger.debug
|
7
|
-
@app
|
6
|
+
Appsignal.logger.debug "Initializing Appsignal::Rack::StreamingListener"
|
7
|
+
@app = app
|
8
|
+
@options = options
|
8
9
|
end
|
9
10
|
|
10
11
|
def call(env)
|
@@ -25,16 +26,16 @@ module Appsignal
|
|
25
26
|
|
26
27
|
# Instrument a `process_action`, to set params/action name
|
27
28
|
status, headers, body =
|
28
|
-
Appsignal.instrument(
|
29
|
+
Appsignal.instrument("process_action.rack") do
|
29
30
|
begin
|
30
31
|
@app.call(env)
|
31
32
|
rescue Exception => e
|
32
33
|
transaction.set_error(e)
|
33
34
|
raise e
|
34
35
|
ensure
|
35
|
-
transaction.set_action(env[
|
36
|
-
transaction.set_metadata(
|
37
|
-
transaction.set_metadata(
|
36
|
+
transaction.set_action(env["appsignal.action"])
|
37
|
+
transaction.set_metadata("path", request.path)
|
38
|
+
transaction.set_metadata("method", request.request_method)
|
38
39
|
transaction.set_http_or_background_queue_start
|
39
40
|
end
|
40
41
|
end
|
@@ -47,8 +48,8 @@ module Appsignal
|
|
47
48
|
|
48
49
|
class StreamWrapper
|
49
50
|
def initialize(stream, transaction)
|
50
|
-
|
51
|
-
|
51
|
+
@stream = stream
|
52
|
+
@transaction = transaction
|
52
53
|
end
|
53
54
|
|
54
55
|
def each
|
data/lib/appsignal/system.rb
CHANGED
@@ -5,31 +5,33 @@ module Appsignal
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.heroku?
|
8
|
-
ENV.key?
|
8
|
+
ENV.key? "DYNO".freeze
|
9
9
|
end
|
10
10
|
|
11
11
|
module Container
|
12
|
-
CGROUP_FILE =
|
12
|
+
CGROUP_FILE = "/proc/self/cgroup".freeze
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
$
|
14
|
+
class << self
|
15
|
+
def id
|
16
|
+
case cgroups
|
17
|
+
when %r{docker[-|/]([0-9a-f]+)}
|
18
|
+
$1
|
19
|
+
when %r{lxc/([0-9a-f-]+)$} # LXC / Heroku
|
20
|
+
$1
|
21
|
+
end
|
20
22
|
end
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
+
private
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def cgroups
|
27
|
+
file = CGROUP_FILE
|
28
|
+
return unless File.exist? file
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
File.read(file)
|
31
|
+
rescue SystemCallError => e
|
32
|
+
Appsignal.logger.debug "Unable to read '#{file}' to determine cgroup"
|
33
|
+
Appsignal.logger.debug e
|
34
|
+
end
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -1,26 +1,29 @@
|
|
1
|
-
require
|
1
|
+
require "json"
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
class Transaction
|
5
|
-
HTTP_REQUEST =
|
6
|
-
BACKGROUND_JOB =
|
7
|
-
FRONTEND =
|
8
|
-
BLANK =
|
5
|
+
HTTP_REQUEST = "http_request".freeze
|
6
|
+
BACKGROUND_JOB = "background_job".freeze
|
7
|
+
FRONTEND = "frontend".freeze
|
8
|
+
BLANK = "".freeze
|
9
9
|
|
10
10
|
# Based on what Rails uses + some variables we'd like to show
|
11
|
-
ENV_METHODS = %w(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
ENV_METHODS = %w(
|
12
|
+
CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
|
13
|
+
PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
14
|
+
REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL REQUEST_URI
|
15
|
+
PATH_INFO
|
16
|
+
|
17
|
+
HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
|
18
|
+
HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
|
19
|
+
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
|
20
|
+
HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM
|
21
|
+
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR
|
22
|
+
HTTP_CLIENT_IP HTTP_RANGE HTTP_X_AUTH_TOKEN
|
23
|
+
)
|
21
24
|
|
22
25
|
class << self
|
23
|
-
def create(id, namespace, request, options={})
|
26
|
+
def create(id, namespace, request, options = {})
|
24
27
|
# Allow middleware to force a new transaction
|
25
28
|
if options.include?(:force) && options[:force]
|
26
29
|
Thread.current[:appsignal_transaction] = nil
|
@@ -58,7 +61,7 @@ module Appsignal
|
|
58
61
|
|
59
62
|
attr_reader :ext, :transaction_id, :namespace, :request, :paused, :tags, :options, :discarded
|
60
63
|
|
61
|
-
def initialize(transaction_id, namespace, request, options={})
|
64
|
+
def initialize(transaction_id, namespace, request, options = {})
|
62
65
|
@transaction_id = transaction_id
|
63
66
|
@namespace = namespace
|
64
67
|
@request = request
|
@@ -82,7 +85,7 @@ module Appsignal
|
|
82
85
|
|
83
86
|
def complete
|
84
87
|
if discarded?
|
85
|
-
Appsignal.logger.debug(
|
88
|
+
Appsignal.logger.debug("Skipping transaction because it was manually discarded.".freeze)
|
86
89
|
return
|
87
90
|
end
|
88
91
|
if @ext.finish(self.class.garbage_collection_profiler.total_time)
|
@@ -119,7 +122,7 @@ module Appsignal
|
|
119
122
|
@store[key]
|
120
123
|
end
|
121
124
|
|
122
|
-
def set_tags(given_tags={})
|
125
|
+
def set_tags(given_tags = {})
|
123
126
|
@tags.merge!(given_tags)
|
124
127
|
end
|
125
128
|
|
@@ -128,13 +131,13 @@ module Appsignal
|
|
128
131
|
@ext.set_action(action)
|
129
132
|
end
|
130
133
|
|
131
|
-
def set_http_or_background_action(from=request.params)
|
134
|
+
def set_http_or_background_action(from = request.params)
|
132
135
|
return unless from
|
133
136
|
group_and_action = [
|
134
137
|
from[:controller] || from[:class],
|
135
138
|
from[:action] || from[:method]
|
136
139
|
]
|
137
|
-
set_action(group_and_action.compact.join(
|
140
|
+
set_action(group_and_action.compact.join("#"))
|
138
141
|
end
|
139
142
|
|
140
143
|
def set_queue_start(start)
|
@@ -182,7 +185,6 @@ module Appsignal
|
|
182
185
|
def set_error(error)
|
183
186
|
return unless error
|
184
187
|
return unless Appsignal.active?
|
185
|
-
return if Appsignal.is_ignored_error?(error)
|
186
188
|
|
187
189
|
backtrace = cleaned_backtrace(error.backtrace)
|
188
190
|
@ext.set_error(
|
@@ -197,7 +199,7 @@ module Appsignal
|
|
197
199
|
@ext.start_event(self.class.garbage_collection_profiler.total_time)
|
198
200
|
end
|
199
201
|
|
200
|
-
def finish_event(name, title, body, body_format=Appsignal::EventFormatter::DEFAULT)
|
202
|
+
def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
|
201
203
|
@ext.finish_event(
|
202
204
|
name,
|
203
205
|
title || BLANK,
|
@@ -207,7 +209,7 @@ module Appsignal
|
|
207
209
|
)
|
208
210
|
end
|
209
211
|
|
210
|
-
def record_event(name, title, body, duration, body_format=Appsignal::EventFormatter::DEFAULT)
|
212
|
+
def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
|
211
213
|
@ext.record_event(
|
212
214
|
name,
|
213
215
|
title || BLANK,
|
@@ -217,7 +219,7 @@ module Appsignal
|
|
217
219
|
)
|
218
220
|
end
|
219
221
|
|
220
|
-
def instrument(name, title=nil, body=nil, body_format=Appsignal::EventFormatter::DEFAULT)
|
222
|
+
def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
|
221
223
|
start_event
|
222
224
|
r = yield
|
223
225
|
finish_event(name, title, body, body_format)
|
@@ -238,23 +240,40 @@ module Appsignal
|
|
238
240
|
|
239
241
|
protected
|
240
242
|
|
243
|
+
# Returns calculated background queue start time in milliseconds, based on
|
244
|
+
# environment values.
|
245
|
+
#
|
246
|
+
# @return [nil] if no {#environment} is present.
|
247
|
+
# @return [nil] if there is no `:queue_start` in the {#environment}.
|
248
|
+
# @return [Integer]
|
241
249
|
def background_queue_start
|
242
|
-
|
243
|
-
return unless
|
250
|
+
env = environment
|
251
|
+
return unless env
|
252
|
+
queue_start = env[:queue_start]
|
253
|
+
return unless queue_start
|
254
|
+
|
244
255
|
(queue_start.to_f * 1000.0).to_i
|
245
256
|
end
|
246
257
|
|
258
|
+
# Returns HTTP queue start time in milliseconds.
|
259
|
+
#
|
260
|
+
# @return [nil] if no queue start time is found.
|
261
|
+
# @return [nil] if begin time is too low to be plausible.
|
262
|
+
# @return [Integer] queue start in milliseconds.
|
247
263
|
def http_queue_start
|
248
|
-
|
249
|
-
return unless
|
250
|
-
|
264
|
+
env = environment
|
265
|
+
return unless env
|
266
|
+
env_var = env["HTTP_X_QUEUE_START".freeze] || env["HTTP_X_REQUEST_START".freeze]
|
267
|
+
return unless env_var
|
268
|
+
cleaned_value = env_var.tr("^0-9".freeze, "".freeze)
|
251
269
|
return if cleaned_value.empty?
|
270
|
+
|
252
271
|
value = cleaned_value.to_i
|
253
272
|
if value > 4_102_441_200_000
|
254
|
-
# Value is in microseconds
|
273
|
+
# Value is in microseconds. Transform to milliseconds.
|
255
274
|
value / 1_000
|
256
275
|
elsif value < 946_681_200_000
|
257
|
-
# Value is
|
276
|
+
# Value is too low to be plausible
|
258
277
|
nil
|
259
278
|
else
|
260
279
|
# Value is in milliseconds
|
@@ -283,24 +302,62 @@ module Appsignal
|
|
283
302
|
Appsignal::Utils::ParamsSanitizer.sanitize params, options
|
284
303
|
end
|
285
304
|
|
305
|
+
# Returns sanitized environment for a transaction.
|
306
|
+
#
|
307
|
+
# The environment of a transaction can contain a lot of information, not
|
308
|
+
# all of it useful for debugging.
|
309
|
+
#
|
310
|
+
# Only the values from the keys specified in `ENV_METHODS` are returned.
|
311
|
+
#
|
312
|
+
# @return [nil] if no environment is present.
|
313
|
+
# @return [Hash<String, Object>]
|
286
314
|
def sanitized_environment
|
287
|
-
|
315
|
+
env = environment
|
316
|
+
return if env.empty?
|
317
|
+
|
288
318
|
{}.tap do |out|
|
289
319
|
ENV_METHODS.each do |key|
|
290
|
-
out[key] =
|
320
|
+
out[key] = env[key] if env[key]
|
291
321
|
end
|
292
322
|
end
|
293
323
|
end
|
294
324
|
|
325
|
+
# Returns sanitized session data.
|
326
|
+
#
|
327
|
+
# The session data is sanitized by the {Appsignal::Utils::ParamsSanitizer}.
|
328
|
+
#
|
329
|
+
# @return [nil] if `:skip_session_data` config is set to `true`.
|
330
|
+
# @return [nil] if the {#request} object doesn't respond to `#session`.
|
331
|
+
# @return [nil] if the {#request} session data is `nil`.
|
332
|
+
# @return [Hash<String, Object>]
|
295
333
|
def sanitized_session_data
|
296
|
-
return if Appsignal.config[:skip_session_data] ||
|
297
|
-
|
334
|
+
return if Appsignal.config[:skip_session_data] ||
|
335
|
+
!request.respond_to?(:session)
|
336
|
+
session = request.session
|
337
|
+
return unless session
|
338
|
+
|
298
339
|
Appsignal::Utils::ParamsSanitizer.sanitize(session.to_hash)
|
299
340
|
end
|
300
341
|
|
342
|
+
# Returns metadata from the environment.
|
343
|
+
#
|
344
|
+
# @return [nil] if no `:metadata` key is present in the {#environment}.
|
345
|
+
# @return [Hash<String, Object>]
|
301
346
|
def metadata
|
302
|
-
|
303
|
-
|
347
|
+
environment[:metadata]
|
348
|
+
end
|
349
|
+
|
350
|
+
# Returns the environment for a transaction.
|
351
|
+
#
|
352
|
+
# Returns an empty Hash when the {#request} object doesn't listen to the
|
353
|
+
# `#env` method or the `#env` is nil.
|
354
|
+
#
|
355
|
+
# @return [Hash<String, Object>]
|
356
|
+
def environment
|
357
|
+
return {} unless request.respond_to?(:env)
|
358
|
+
return {} unless request.env
|
359
|
+
|
360
|
+
request.env
|
304
361
|
end
|
305
362
|
|
306
363
|
# Only keep tags if they meet the following criteria:
|
@@ -310,7 +367,7 @@ module Appsignal
|
|
310
367
|
def sanitized_tags
|
311
368
|
@tags.select do |k, v|
|
312
369
|
(k.is_a?(Symbol) || k.is_a?(String) && k.length <= 100) &&
|
313
|
-
(((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) ||
|
370
|
+
(((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) || v.is_a?(Integer))
|
314
371
|
end
|
315
372
|
end
|
316
373
|
|
@@ -329,7 +386,7 @@ module Appsignal
|
|
329
386
|
end
|
330
387
|
|
331
388
|
# Instrument should still yield
|
332
|
-
def instrument(*
|
389
|
+
def instrument(*_args)
|
333
390
|
yield
|
334
391
|
end
|
335
392
|
|