scout_apm 3.0.0.pre23 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +3 -4
  4. data/.travis.yml +17 -14
  5. data/CHANGELOG.markdown +150 -4
  6. data/Gemfile +2 -8
  7. data/README.markdown +30 -4
  8. data/Rakefile +1 -1
  9. data/ext/allocations/allocations.c +2 -0
  10. data/gems/README.md +28 -0
  11. data/gems/octoshark.gemfile +4 -0
  12. data/gems/rails3.gemfile +5 -0
  13. data/gems/rails4.gemfile +4 -0
  14. data/gems/rails5.gemfile +4 -0
  15. data/gems/rails6.gemfile +4 -0
  16. data/lib/scout_apm.rb +39 -9
  17. data/lib/scout_apm/agent.rb +29 -10
  18. data/lib/scout_apm/agent/exit_handler.rb +0 -1
  19. data/lib/scout_apm/agent_context.rb +26 -3
  20. data/lib/scout_apm/app_server_load.rb +7 -2
  21. data/lib/scout_apm/attribute_arranger.rb +0 -2
  22. data/lib/scout_apm/auto_instrument.rb +5 -0
  23. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
  24. data/lib/scout_apm/auto_instrument/layer.rb +23 -0
  25. data/lib/scout_apm/auto_instrument/parser.rb +27 -0
  26. data/lib/scout_apm/auto_instrument/rails.rb +175 -0
  27. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
  28. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +55 -0
  29. data/lib/scout_apm/background_job_integrations/que.rb +134 -0
  30. data/lib/scout_apm/background_job_integrations/resque.rb +6 -2
  31. data/lib/scout_apm/background_job_integrations/shoryuken.rb +124 -0
  32. data/lib/scout_apm/background_job_integrations/sidekiq.rb +5 -19
  33. data/lib/scout_apm/background_job_integrations/sneakers.rb +87 -0
  34. data/lib/scout_apm/config.rb +48 -7
  35. data/lib/scout_apm/detailed_trace.rb +217 -0
  36. data/lib/scout_apm/environment.rb +3 -0
  37. data/lib/scout_apm/error.rb +27 -0
  38. data/lib/scout_apm/error_service.rb +32 -0
  39. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  40. data/lib/scout_apm/error_service/error_record.rb +211 -0
  41. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  42. data/lib/scout_apm/error_service/middleware.rb +32 -0
  43. data/lib/scout_apm/error_service/notifier.rb +33 -0
  44. data/lib/scout_apm/error_service/payload.rb +47 -0
  45. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  46. data/lib/scout_apm/error_service/railtie.rb +11 -0
  47. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  48. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  49. data/lib/scout_apm/fake_store.rb +3 -0
  50. data/lib/scout_apm/framework_integrations/rails_2.rb +2 -1
  51. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +17 -6
  52. data/lib/scout_apm/git_revision.rb +6 -3
  53. data/lib/scout_apm/instant/middleware.rb +2 -1
  54. data/lib/scout_apm/instrument_manager.rb +8 -7
  55. data/lib/scout_apm/instruments/action_controller_rails_2.rb +3 -1
  56. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +56 -55
  57. data/lib/scout_apm/instruments/action_view.rb +114 -26
  58. data/lib/scout_apm/instruments/active_record.rb +66 -19
  59. data/lib/scout_apm/instruments/http.rb +48 -0
  60. data/lib/scout_apm/instruments/memcached.rb +43 -0
  61. data/lib/scout_apm/instruments/mongoid.rb +9 -4
  62. data/lib/scout_apm/instruments/net_http.rb +8 -1
  63. data/lib/scout_apm/job_record.rb +4 -2
  64. data/lib/scout_apm/layaway_file.rb +4 -0
  65. data/lib/scout_apm/layer.rb +5 -56
  66. data/lib/scout_apm/layer_children_set.rb +15 -6
  67. data/lib/scout_apm/layer_converters/converter_base.rb +15 -30
  68. data/lib/scout_apm/layer_converters/database_converter.rb +2 -15
  69. data/lib/scout_apm/layer_converters/slow_job_converter.rb +12 -2
  70. data/lib/scout_apm/layer_converters/slow_request_converter.rb +14 -4
  71. data/lib/scout_apm/layer_converters/trace_converter.rb +184 -0
  72. data/lib/scout_apm/limited_layer.rb +0 -7
  73. data/lib/scout_apm/metric_stats.rb +0 -8
  74. data/lib/scout_apm/middleware.rb +1 -1
  75. data/lib/scout_apm/periodic_work.rb +19 -0
  76. data/lib/scout_apm/remote/message.rb +4 -0
  77. data/lib/scout_apm/reporter.rb +8 -3
  78. data/lib/scout_apm/reporting.rb +2 -1
  79. data/lib/scout_apm/request_histograms.rb +8 -0
  80. data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
  81. data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
  82. data/lib/scout_apm/serializers/payload_serializer.rb +2 -2
  83. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +30 -15
  84. data/lib/scout_apm/slow_job_record.rb +5 -1
  85. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  86. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  87. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  88. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  89. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  90. data/lib/scout_apm/slow_request_policy.rb +18 -60
  91. data/lib/scout_apm/slow_transaction.rb +3 -1
  92. data/lib/scout_apm/store.rb +14 -10
  93. data/lib/scout_apm/tracked_request.rb +41 -30
  94. data/lib/scout_apm/transaction_time_consumed.rb +51 -0
  95. data/lib/scout_apm/utils/active_record_metric_name.rb +16 -3
  96. data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
  97. data/lib/scout_apm/utils/marshal_logging.rb +90 -0
  98. data/lib/scout_apm/utils/sql_sanitizer.rb +10 -1
  99. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +7 -0
  100. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +6 -0
  101. data/lib/scout_apm/utils/unique_id.rb +27 -0
  102. data/lib/scout_apm/version.rb +1 -1
  103. data/scout_apm.gemspec +13 -7
  104. data/test/test_helper.rb +2 -2
  105. data/test/unit/agent_context_test.rb +29 -0
  106. data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
  107. data/test/unit/auto_instrument/assignments.rb +31 -0
  108. data/test/unit/auto_instrument/controller-ast.txt +57 -0
  109. data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
  110. data/test/unit/auto_instrument/controller.rb +49 -0
  111. data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
  112. data/test/unit/auto_instrument/rescue_from.rb +13 -0
  113. data/test/unit/auto_instrument_test.rb +54 -0
  114. data/test/unit/error_service/error_buffer_test.rb +25 -0
  115. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  116. data/test/unit/extensions/periodic_callbacks_test.rb +2 -2
  117. data/test/unit/instruments/active_record_test.rb +40 -0
  118. data/test/unit/layer_children_set_test.rb +9 -0
  119. data/test/unit/request_histograms_test.rb +17 -0
  120. data/test/unit/serializers/payload_serializer_test.rb +39 -5
  121. data/test/unit/slow_request_policy_test.rb +42 -9
  122. data/test/unit/sql_sanitizer_test.rb +47 -0
  123. data/test/unit/transaction_time_consumed_test.rb +46 -0
  124. data/test/unit/utils/active_record_metric_name_test.rb +10 -2
  125. metadata +101 -19
  126. data/ext/stacks/extconf.rb +0 -37
  127. data/ext/stacks/scout_atomics.h +0 -86
  128. data/ext/stacks/stacks.c +0 -814
  129. data/lib/scout_apm/slow_job_policy.rb +0 -94
  130. data/lib/scout_apm/trace_compactor.rb +0 -312
  131. data/lib/scout_apm/utils/fake_stacks.rb +0 -88
  132. data/test/unit/instruments/active_record_instruments_test.rb +0 -5
  133. data/test/unit/slow_job_policy_test.rb +0 -6
  134. data/tester.rb +0 -53
@@ -0,0 +1,31 @@
1
+
2
+ class Assignments
3
+ def test_op_asgn
4
+ foo.bar += User.size
5
+ foo.bar -= User.size
6
+ end
7
+
8
+ def nested_assignment
9
+ @email ||= if (email = session["email"]).present?
10
+ User.where(email: email).first
11
+ else
12
+ nil
13
+ end
14
+ end
15
+
16
+ def paginate_collection(coll)
17
+ page = (params[:page].present? ? params[:page].to_i : 1)
18
+ per_page = (params[:per_page].present? ? params[:per_page].to_i : 20)
19
+ pagination, self.collection = pagy(
20
+ coll,
21
+ items: per_page,
22
+ page: page
23
+ )
24
+ headers[PAGINATION_TOTAL_HEADER] = pagination.count.to_s
25
+ headers[PAGINATION_TOTAL_PAGES_HEADER] = pagination.pages.to_s
26
+ headers[PAGINATION_PER_PAGE_HEADER] = per_page.to_s
27
+ headers[PAGINATION_PAGE_HEADER] = pagination.page.to_s
28
+ headers[PAGINATION_NEXT_PAGE_HEADER] = pagination.next.to_s
29
+ collection
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ s(:class,
2
+ s(:const, nil, :ClientsController),
3
+ s(:const, nil, :ApplicationController),
4
+ s(:begin,
5
+ s(:send, nil, :before_action,
6
+ s(:sym, :check_authorization)),
7
+ s(:def, :index,
8
+ s(:args),
9
+ s(:if,
10
+ s(:send,
11
+ s(:send,
12
+ s(:send, nil, :params), :[],
13
+ s(:sym, :status)), :==,
14
+ s(:str, "activated")),
15
+ s(:ivasgn, :@clients,
16
+ s(:send,
17
+ s(:const, nil, :Client), :activated)),
18
+ s(:ivasgn, :@clients,
19
+ s(:send,
20
+ s(:const, nil, :Client), :inactivated)))),
21
+ s(:def, :create,
22
+ s(:args),
23
+ s(:begin,
24
+ s(:ivasgn, :@client,
25
+ s(:send,
26
+ s(:const, nil, :Client), :new,
27
+ s(:send,
28
+ s(:send, nil, :params), :[],
29
+ s(:sym, :client)))),
30
+ s(:if,
31
+ s(:send,
32
+ s(:ivar, :@client), :save),
33
+ s(:send, nil, :redirect_to,
34
+ s(:ivar, :@client)),
35
+ s(:send, nil, :render,
36
+ s(:str, "new"))))),
37
+ s(:def, :edit,
38
+ s(:args),
39
+ s(:begin,
40
+ s(:ivasgn, :@client,
41
+ s(:send,
42
+ s(:const, nil, :Client), :new,
43
+ s(:send,
44
+ s(:send, nil, :params), :[],
45
+ s(:sym, :client)))),
46
+ s(:if,
47
+ s(:send,
48
+ s(:send, nil, :request), :post?),
49
+ s(:block,
50
+ s(:send,
51
+ s(:ivar, :@client), :transaction),
52
+ s(:args),
53
+ s(:send,
54
+ s(:ivar, :@client), :update_attributes,
55
+ s(:send,
56
+ s(:send, nil, :params), :[],
57
+ s(:sym, :client)))), nil)))))
@@ -0,0 +1,49 @@
1
+
2
+ class ClientsController < ApplicationController
3
+ before_action :check_authorization
4
+
5
+ def index
6
+ if ::ScoutApm::AutoInstrument("params[:status] == \"activated\"",["ROOT/test/unit/auto_instrument/controller.rb:6:in `index'"]){params[:status] == "activated"}
7
+ @clients = ::ScoutApm::AutoInstrument("Client.activated",["ROOT/test/unit/auto_instrument/controller.rb:7:in `index'"]){Client.activated}
8
+ else
9
+ @clients = ::ScoutApm::AutoInstrument("Client.inactivated",["ROOT/test/unit/auto_instrument/controller.rb:9:in `index'"]){Client.inactivated}
10
+ end
11
+ end
12
+
13
+ def create
14
+ @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["ROOT/test/unit/auto_instrument/controller.rb:14:in `create'"]){Client.new(params[:client])}
15
+ if ::ScoutApm::AutoInstrument("@client.save",["ROOT/test/unit/auto_instrument/controller.rb:15:in `create'"]){@client.save}
16
+ ::ScoutApm::AutoInstrument("redirect_to @client",["ROOT/test/unit/auto_instrument/controller.rb:16:in `create'"]){redirect_to @client}
17
+ else
18
+ # This line overrides the default rendering behavior, which
19
+ # would have been to render the "create" view.
20
+ ::ScoutApm::AutoInstrument("render \"new\"",["ROOT/test/unit/auto_instrument/controller.rb:20:in `create'"]){render "new"}
21
+ end
22
+ end
23
+
24
+ def edit
25
+ @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["ROOT/test/unit/auto_instrument/controller.rb:25:in `edit'"]){Client.new(params[:client])}
26
+
27
+ if ::ScoutApm::AutoInstrument("request.post?",["ROOT/test/unit/auto_instrument/controller.rb:27:in `edit'"]){request.post?}
28
+ ::ScoutApm::AutoInstrument("@client.transaction do...",["ROOT/test/unit/auto_instrument/controller.rb:28:in `edit'"]){@client.transaction do
29
+ @client.update_attributes(params[:client])
30
+ end}
31
+ end
32
+ end
33
+
34
+ def data
35
+ @clients = ::ScoutApm::AutoInstrument("Client.all",["ROOT/test/unit/auto_instrument/controller.rb:35:in `data'"]){Client.all}
36
+
37
+ formatter = ::ScoutApm::AutoInstrument("proc do |row|...",["ROOT/test/unit/auto_instrument/controller.rb:37:in `data'"]){proc do |row|
38
+ row.to_json
39
+ end}
40
+
41
+ ::ScoutApm::AutoInstrument("respond_with @clients.each(&formatter).join(\"\\n\"), :content_type => 'application/json; boundary=NL'",["ROOT/test/unit/auto_instrument/controller.rb:41:in `data'"]){respond_with @clients.each(&formatter).join("\n"), :content_type => 'application/json; boundary=NL'}
42
+ end
43
+
44
+ def things
45
+ x = {}
46
+ x[:this] ||= 'foo'
47
+ x[:that] &&= ::ScoutApm::AutoInstrument("'foo'.size",["ROOT/test/unit/auto_instrument/controller.rb:47:in `things'"]){'foo'.size}
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+
2
+ class ClientsController < ApplicationController
3
+ before_action :check_authorization
4
+
5
+ def index
6
+ if params[:status] == "activated"
7
+ @clients = Client.activated
8
+ else
9
+ @clients = Client.inactivated
10
+ end
11
+ end
12
+
13
+ def create
14
+ @client = Client.new(params[:client])
15
+ if @client.save
16
+ redirect_to @client
17
+ else
18
+ # This line overrides the default rendering behavior, which
19
+ # would have been to render the "create" view.
20
+ render "new"
21
+ end
22
+ end
23
+
24
+ def edit
25
+ @client = Client.new(params[:client])
26
+
27
+ if request.post?
28
+ @client.transaction do
29
+ @client.update_attributes(params[:client])
30
+ end
31
+ end
32
+ end
33
+
34
+ def data
35
+ @clients = Client.all
36
+
37
+ formatter = proc do |row|
38
+ row.to_json
39
+ end
40
+
41
+ respond_with @clients.each(&formatter).join("\n"), :content_type => 'application/json; boundary=NL'
42
+ end
43
+
44
+ def things
45
+ x = {}
46
+ x[:this] ||= 'foo'
47
+ x[:that] &&= 'foo'.size
48
+ end
49
+ end
@@ -0,0 +1,13 @@
1
+
2
+ class BrokenController < ApplicationController
3
+ rescue_from Exception do |e|
4
+ if e.is_a? Pundit::NotAuthorizedError
5
+ unauthorized_error
6
+ elsif e.is_a? ActionController::ParameterMissing
7
+ error(status: 422, message: e.message)
8
+ else
9
+ log_error(e)
10
+ error message: 'Internal error', exception: e
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+
2
+ class BrokenController < ApplicationController
3
+ rescue_from Exception do |e|
4
+ if e.is_a? Pundit::NotAuthorizedError
5
+ unauthorized_error
6
+ elsif e.is_a? ActionController::ParameterMissing
7
+ error(status: 422, message: e.message)
8
+ else
9
+ log_error(e)
10
+ error message: 'Internal error', exception: e
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/auto_instrument'
4
+
5
+ class AutoInstrumentTest < Minitest::Test
6
+ ROOT = File.expand_path("../../", __dir__)
7
+
8
+ def source_path(name)
9
+ File.expand_path("auto_instrument/#{name}.rb", __dir__)
10
+ end
11
+
12
+ def instrumented_path(name)
13
+ File.expand_path("auto_instrument/#{name}-instrumented.rb", __dir__)
14
+ end
15
+
16
+ def instrumented_source(name)
17
+ File.read(instrumented_path(name))
18
+ end
19
+
20
+ # Autoinstruments adds a backtrace to each created layer. This is the full path to the
21
+ # test controller.rb file, which will be different on different environments.
22
+ # This normalizes backtraces across environments.
23
+ def normalize_backtrace(string)
24
+ string.gsub(ROOT, "ROOT")
25
+ end
26
+
27
+ # Use this to automatically update the test fixtures.
28
+ def update_instrumented_source(name)
29
+ source = ::ScoutApm::AutoInstrument::Rails.rewrite(source_path(name))
30
+ source = normalize_backtrace(source)
31
+ File.write(instrumented_path(name),source)
32
+ end
33
+
34
+ def test_controller_rewrite
35
+ # update_instrumented_source("controller")
36
+
37
+ assert_equal instrumented_source("controller"),
38
+ normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("controller")))
39
+ end
40
+
41
+ def test_rescue_from_rewrite
42
+ # update_instrumented_source("rescue_from")
43
+
44
+ assert_equal instrumented_source("rescue_from"),
45
+ normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("rescue_from")))
46
+ end
47
+
48
+ def test_assignments_rewrite
49
+ # update_instrumented_source("assignments")
50
+
51
+ assert_equal instrumented_source("assignments"),
52
+ normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("assignments")))
53
+ end
54
+ end if defined? ScoutApm::AutoInstrument
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+
3
+ class ErrorBufferTest < Minitest::Test
4
+ class FakeError < StandardError
5
+ end
6
+
7
+ def test_captures_and_stores_exceptions_and_env
8
+ eb = ScoutApm::ErrorService::ErrorBuffer.new(context)
9
+ eb.capture(ex, env)
10
+ end
11
+
12
+ #### Helpers
13
+
14
+ def context
15
+ ScoutApm::AgentContext.new
16
+ end
17
+
18
+ def env
19
+ {}
20
+ end
21
+
22
+ def ex(msg="Whoops")
23
+ FakeError.new(msg)
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ require "test_helper"
2
+
3
+ class IgnoredExceptionsTest < Minitest::Test
4
+ class FakeError < StandardError
5
+ end
6
+
7
+ class SubclassFakeError < FakeError
8
+ end
9
+
10
+ def test_ignores_with_string_match
11
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["RuntimeError"])
12
+ assert ig.ignored?(RuntimeError.new("something went wrong"))
13
+ assert !ig.ignored?(FakeError.new("something went wrong"))
14
+ end
15
+
16
+ def test_ignores_with_block
17
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, [])
18
+ ig.add_callback { |e| e.message == "ignore me" }
19
+
20
+ should_ignore = RuntimeError.new("ignore me")
21
+ should_not_ignore = RuntimeError.new("super legit")
22
+
23
+ assert ig.ignored?(should_ignore)
24
+ assert !ig.ignored?(should_not_ignore)
25
+ end
26
+
27
+ def test_ignores_subclasses
28
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["IgnoredExceptionsTest::FakeError"])
29
+ assert ig.ignored?(SubclassFakeError.new("Subclass"))
30
+ end
31
+
32
+ # Check that a bad exception in the list doesn't stop the whole thing from working
33
+ def test_does_not_consider_unknown_errors
34
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["ThisDoesNotExist", "IgnoredExceptionsTest::FakeError"])
35
+ assert ig.ignored?(FakeError.new("ignore this one"))
36
+ end
37
+
38
+ def test_add_module
39
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, [])
40
+ ig.add(IgnoredExceptionsTest::FakeError)
41
+ assert ig.ignored?(FakeError.new("ignore this one"))
42
+ end
43
+
44
+ #### Helpers
45
+
46
+ def context
47
+ ScoutApm::AgentContext.new
48
+ end
49
+ end
@@ -38,7 +38,7 @@ class PeriodicCallbacksTest < Minitest::Test
38
38
  private
39
39
 
40
40
  def reporting_period
41
- rp = ScoutApm::StoreReportingPeriod.new(ScoutApm::AgentContext.new, Time.at(metadata[:agent_time].to_i))
41
+ rp = ScoutApm::StoreReportingPeriod.new(Time.at(metadata[:agent_time].to_i), ScoutApm::AgentContext.new)
42
42
  rp.absorb_metrics!(metrics)
43
43
  end
44
44
 
@@ -55,4 +55,4 @@ class PeriodicCallbacksTest < Minitest::Test
55
55
  {:app_root=>"/srv/rails_app", :unique_id=>"ID", :agent_version=>"2.4.10", :agent_time=>"1523287920", :agent_pid=>21581, :platform=>"ruby"}
56
56
  end
57
57
 
58
- end
58
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+ require 'sqlite3'
3
+ require 'active_record'
4
+
5
+ begin
6
+ require 'octoshark'
7
+ rescue LoadError
8
+ # Ignore
9
+ end
10
+
11
+ class ActiveRecordTest < Minitest::Test
12
+ def database_path
13
+ File.expand_path('test.sqlite3', DATA_FILE_DIR)
14
+ end
15
+
16
+ def setup
17
+ database = SQLite3::Database.new(database_path)
18
+ database.execute("DROP TABLE IF EXISTS users;")
19
+ database.execute("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100));")
20
+
21
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => database_path)
22
+ end
23
+
24
+ class User < ActiveRecord::Base
25
+ end
26
+
27
+ def test_instrumentation
28
+ recorder = FakeRecorder.new
29
+ agent_context.recorder = recorder
30
+
31
+ instrument = ScoutApm::Instruments::ActiveRecord.new(agent_context)
32
+ instrument.install
33
+
34
+ ScoutApm::Tracer.instrument("Controller", "foo/bar") do
35
+ user = User.create
36
+ end
37
+
38
+ assert 1, recorder.requests.size
39
+ end
40
+ end
@@ -71,6 +71,15 @@ class LayerChildrenSetTest < Minitest::Test
71
71
  limited_layers.each { |ml| assert_equal 5, ml.count }
72
72
  end
73
73
 
74
+ def test_works_with_marshal
75
+ s = SET.new(5)
76
+ 10.times do
77
+ s << make_layer("LayerType", "LayerName")
78
+ end
79
+
80
+ Marshal.dump(s)
81
+ end
82
+
74
83
  #############
75
84
  # Helpers #
76
85
  #############
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/request_histograms'
4
+
5
+ class RequestHistogramsTest < Minitest::Test
6
+ def test_as_json_without_activesupport
7
+ rh = ScoutApm::RequestHistograms.new
8
+
9
+ rh.add("foo", 1)
10
+ rh.add("foo", 2)
11
+ rh.add("bar", 3)
12
+
13
+ j = rh.as_json
14
+ assert_equal 2, j.size
15
+ assert_equal ["bar", "foo"], j.keys.sort
16
+ end
17
+ end
@@ -8,7 +8,7 @@ class PayloadSerializerTest < Minitest::Test
8
8
  :unique_id => "unique_idz",
9
9
  :agent_version => 123
10
10
  }
11
- payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [], [], {})
11
+ payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [], [], {}, [])
12
12
 
13
13
  # symbol keys turn to strings
14
14
  formatted_metadata = {
@@ -49,7 +49,7 @@ class PayloadSerializerTest < Minitest::Test
49
49
  stats.total_exclusive_time = 0.078132088
50
50
  }
51
51
  }
52
- payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, metrics, {}, [], [], [], {})
52
+ payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, metrics, {}, [], [], [], {}, [])
53
53
  formatted_metrics = [
54
54
  {
55
55
  "key" => {
@@ -69,7 +69,6 @@ class PayloadSerializerTest < Minitest::Test
69
69
  "min_call_time" => 0.000613518,
70
70
  "total_call_time" => 0.033245704,
71
71
  "total_exclusive_time" => 0.033245704,
72
- "traces" => []
73
72
  },
74
73
  {
75
74
  "key" => {
@@ -84,7 +83,6 @@ class PayloadSerializerTest < Minitest::Test
84
83
  "min_call_time" => 0.034881757,
85
84
  "total_call_time" => 0.113403176,
86
85
  "total_exclusive_time" => 0.078132088,
87
- "traces" => []
88
86
  }
89
87
  ]
90
88
  assert_equal formatted_metrics,
@@ -96,7 +94,7 @@ class PayloadSerializerTest < Minitest::Test
96
94
  :quotie => "here are some \"quotes\"",
97
95
  :payload_version => 2,
98
96
  }
99
- payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [], [], {})
97
+ payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [], [], {}, [])
100
98
 
101
99
  # symbol keys turn to strings
102
100
  formatted_metadata = {
@@ -110,4 +108,40 @@ class PayloadSerializerTest < Minitest::Test
110
108
  json = { "foo" => "\bbar\nbaz\r" }
111
109
  assert_equal json, JSON.parse(ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json))
112
110
  end
111
+
112
+ def test_escapes_escaped_quotes
113
+ # Some escapes haven't ever worked on 1.8.7, and is not the issue I'm
114
+ # fixing now. Remove this when we drop support for ancient ruby
115
+ skip if RUBY_VERSION == "1.8.7"
116
+
117
+ json = {"foo" => %q|`additional_details` = '{\"amount\":1}'|}
118
+ result = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json)
119
+ assert_equal json, JSON.parse(result)
120
+ end
121
+
122
+ def test_escapes_various_special_characters
123
+ # Some escapes haven't ever worked on 1.8.7, and is not the issue I'm
124
+ # fixing now. Remove this when we drop support for ancient ruby
125
+ skip if RUBY_VERSION == "1.8.7"
126
+
127
+ json = {"foo" => [
128
+ %Q|\fbar|,
129
+ %Q|\rbar|,
130
+ %Q|\nbar|,
131
+ %Q|\tbar|,
132
+ %Q|"bar|,
133
+ %Q|'bar|,
134
+ %Q|{bar|,
135
+ %Q|}bar|,
136
+ %Q|\\bar|,
137
+ if RUBY_VERSION == '1.8.7'
138
+ ""
139
+ else
140
+ %Q|\\\nbar|
141
+ end,
142
+ ]}
143
+
144
+ result = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json)
145
+ assert_equal json, JSON.parse(result)
146
+ end
113
147
  end