scout_apm 2.5.1 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +68 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +5 -5
  5. data/CHANGELOG.markdown +176 -3
  6. data/Gemfile +1 -7
  7. data/LICENSE.md +21 -28
  8. data/gems/README.md +28 -0
  9. data/gems/instruments.gemfile +6 -0
  10. data/gems/octoshark.gemfile +4 -0
  11. data/gems/rails3.gemfile +5 -0
  12. data/gems/rails4.gemfile +4 -0
  13. data/gems/rails5.gemfile +4 -0
  14. data/gems/rails6.gemfile +4 -0
  15. data/gems/sidekiq.gemfile +4 -0
  16. data/gems/typhoeus.gemfile +3 -0
  17. data/lib/scout_apm/agent/preconditions.rb +3 -3
  18. data/lib/scout_apm/agent.rb +22 -0
  19. data/lib/scout_apm/agent_context.rb +21 -2
  20. data/lib/scout_apm/app_server_load.rb +7 -2
  21. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
  22. data/lib/scout_apm/auto_instrument/layer.rb +23 -0
  23. data/lib/scout_apm/auto_instrument/parser.rb +27 -0
  24. data/lib/scout_apm/auto_instrument/rails.rb +174 -0
  25. data/lib/scout_apm/auto_instrument.rb +5 -0
  26. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
  27. data/lib/scout_apm/background_job_integrations/faktory.rb +103 -0
  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/shoryuken.rb +2 -0
  31. data/lib/scout_apm/background_job_integrations/sidekiq.rb +15 -10
  32. data/lib/scout_apm/background_job_integrations/sneakers.rb +11 -11
  33. data/lib/scout_apm/config.rb +54 -6
  34. data/lib/scout_apm/detailed_trace.rb +3 -2
  35. data/lib/scout_apm/environment.rb +18 -1
  36. data/lib/scout_apm/error.rb +27 -0
  37. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  38. data/lib/scout_apm/error_service/error_record.rb +211 -0
  39. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  40. data/lib/scout_apm/error_service/middleware.rb +32 -0
  41. data/lib/scout_apm/error_service/notifier.rb +33 -0
  42. data/lib/scout_apm/error_service/payload.rb +47 -0
  43. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  44. data/lib/scout_apm/error_service/railtie.rb +11 -0
  45. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  46. data/lib/scout_apm/error_service.rb +34 -0
  47. data/lib/scout_apm/exceptions.rb +12 -0
  48. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  49. data/lib/scout_apm/external_service_metric_set.rb +97 -0
  50. data/lib/scout_apm/external_service_metric_stats.rb +85 -0
  51. data/lib/scout_apm/fake_store.rb +3 -0
  52. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +7 -2
  53. data/lib/scout_apm/git_revision.rb +9 -0
  54. data/lib/scout_apm/ignored_uris.rb +3 -1
  55. data/lib/scout_apm/instant/middleware.rb +4 -1
  56. data/lib/scout_apm/instrument_manager.rb +22 -1
  57. data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -1
  58. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +53 -29
  59. data/lib/scout_apm/instruments/action_view.rb +30 -9
  60. data/lib/scout_apm/instruments/active_record.rb +69 -19
  61. data/lib/scout_apm/instruments/elasticsearch.rb +93 -42
  62. data/lib/scout_apm/instruments/grape.rb +1 -1
  63. data/lib/scout_apm/instruments/http.rb +68 -0
  64. data/lib/scout_apm/instruments/http_client.rb +33 -14
  65. data/lib/scout_apm/instruments/influxdb.rb +2 -2
  66. data/lib/scout_apm/instruments/memcached.rb +58 -0
  67. data/lib/scout_apm/instruments/middleware_detailed.rb +1 -1
  68. data/lib/scout_apm/instruments/middleware_summary.rb +1 -1
  69. data/lib/scout_apm/instruments/mongoid.rb +10 -5
  70. data/lib/scout_apm/instruments/moped.rb +44 -19
  71. data/lib/scout_apm/instruments/net_http.rb +51 -16
  72. data/lib/scout_apm/instruments/rails_router.rb +1 -1
  73. data/lib/scout_apm/instruments/redis.rb +27 -12
  74. data/lib/scout_apm/instruments/redis5.rb +59 -0
  75. data/lib/scout_apm/instruments/sinatra.rb +3 -1
  76. data/lib/scout_apm/instruments/typhoeus.rb +90 -0
  77. data/lib/scout_apm/job_record.rb +4 -2
  78. data/lib/scout_apm/layaway_file.rb +4 -0
  79. data/lib/scout_apm/layer.rb +5 -2
  80. data/lib/scout_apm/layer_children_set.rb +9 -8
  81. data/lib/scout_apm/layer_converters/external_service_converter.rb +65 -0
  82. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -0
  83. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +2 -0
  84. data/lib/scout_apm/layer_converters/trace_converter.rb +7 -4
  85. data/lib/scout_apm/logger.rb +5 -1
  86. data/lib/scout_apm/middleware.rb +1 -1
  87. data/lib/scout_apm/periodic_work.rb +19 -0
  88. data/lib/scout_apm/remote/message.rb +4 -0
  89. data/lib/scout_apm/remote/server.rb +13 -1
  90. data/lib/scout_apm/reporter.rb +8 -3
  91. data/lib/scout_apm/reporting.rb +2 -1
  92. data/lib/scout_apm/request_histograms.rb +8 -0
  93. data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
  94. data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
  95. data/lib/scout_apm/serializers/external_service_serializer_to_json.rb +15 -0
  96. data/lib/scout_apm/serializers/payload_serializer.rb +4 -3
  97. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +10 -3
  98. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  99. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  100. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  101. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  102. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  103. data/lib/scout_apm/slow_request_policy.rb +18 -77
  104. data/lib/scout_apm/store.rb +31 -1
  105. data/lib/scout_apm/tracer.rb +2 -2
  106. data/lib/scout_apm/tracked_request.rb +35 -4
  107. data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
  108. data/lib/scout_apm/utils/marshal_logging.rb +90 -0
  109. data/lib/scout_apm/utils/sql_sanitizer.rb +47 -7
  110. data/lib/scout_apm/version.rb +1 -1
  111. data/lib/scout_apm.rb +46 -1
  112. data/scout_apm.gemspec +14 -9
  113. data/test/test_helper.rb +2 -2
  114. data/test/tmp/README.md +17 -0
  115. data/test/unit/agent_context_test.rb +29 -0
  116. data/test/unit/auto_instrument/anonymous_block_value.rb +7 -0
  117. data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
  118. data/test/unit/auto_instrument/assignments.rb +31 -0
  119. data/test/unit/auto_instrument/controller-ast.txt +57 -0
  120. data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
  121. data/test/unit/auto_instrument/controller.rb +49 -0
  122. data/test/unit/auto_instrument/hanging_method.rb +6 -0
  123. data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
  124. data/test/unit/auto_instrument/rescue_from.rb +13 -0
  125. data/test/unit/auto_instrument_test.rb +62 -0
  126. data/test/unit/background_job_integrations/sidekiq_test.rb +17 -0
  127. data/test/unit/environment_test.rb +2 -2
  128. data/test/unit/error_service/error_buffer_test.rb +25 -0
  129. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  130. data/test/unit/external_service_metric_set_test.rb +67 -0
  131. data/test/unit/external_service_metric_stats_test.rb +106 -0
  132. data/test/unit/ignored_uris_test.rb +6 -0
  133. data/test/unit/instruments/active_record_test.rb +40 -0
  134. data/test/unit/instruments/http_client_test.rb +24 -0
  135. data/test/unit/instruments/http_test.rb +24 -0
  136. data/test/unit/instruments/moped_test.rb +24 -0
  137. data/test/unit/instruments/net_http_test.rb +11 -1
  138. data/test/unit/instruments/redis_test.rb +24 -0
  139. data/test/unit/instruments/typhoeus_test.rb +42 -0
  140. data/test/unit/layer_children_set_test.rb +9 -0
  141. data/test/unit/remote/{test_message.rb → message_test.rb} +0 -0
  142. data/test/unit/remote/{test_router.rb → route_test.rb} +0 -0
  143. data/test/unit/remote/{test_server.rb → server_test.rb} +4 -1
  144. data/test/unit/request_histograms_test.rb +17 -0
  145. data/test/unit/serializers/payload_serializer_test.rb +39 -3
  146. data/test/unit/slow_request_policy_test.rb +41 -13
  147. data/test/unit/sql_sanitizer_test.rb +106 -0
  148. data/test/unit/tracer_test.rb +25 -0
  149. metadata +118 -60
  150. data/.travis.yml +0 -25
  151. data/lib/scout_apm/instruments/.DS_Store +0 -0
  152. data/lib/scout_apm/slow_job_policy.rb +0 -111
  153. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -25
  154. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -26
  155. data/test/unit/instruments/active_record_instruments_test.rb +0 -5
  156. data/test/unit/slow_job_policy_test.rb +0 -6
@@ -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,62 @@
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
+
55
+ def test_hanging_method_rewrite
56
+ ::ScoutApm::AutoInstrument::Rails.rewrite(source_path("hanging_method"))
57
+ end
58
+
59
+ def test_anonymous_block_value
60
+ ::ScoutApm::AutoInstrument::Rails.rewrite(source_path("anonymous_block_value"))
61
+ end
62
+ end if defined? ScoutApm::AutoInstrument
@@ -3,8 +3,25 @@ require 'scout_apm/request_manager'
3
3
  require 'scout_apm/background_job_integrations/sidekiq'
4
4
 
5
5
  class SidekiqTest < Minitest::Test
6
+ SidekiqIntegration = ScoutApm::BackgroundJobIntegrations::Sidekiq
6
7
  SidekiqMiddleware = ScoutApm::BackgroundJobIntegrations::SidekiqMiddleware
7
8
 
9
+ ########################################
10
+ # Install
11
+ ########################################
12
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("sidekiq_install")
13
+ require 'sidekiq'
14
+
15
+ # Sidekiq::CLI needs to be defined in order for `Sidekiq.configure_server` to work
16
+ Sidekiq::CLI = nil
17
+
18
+ def test_starts_on_startup
19
+ ::ScoutApm::Agent.any_instance.expects(:start)
20
+ SidekiqIntegration.new.install
21
+ Sidekiq.options[:lifecycle_events][:startup].map(&:call)
22
+ end
23
+ end
24
+
8
25
  ########################################
9
26
  # Middleware
10
27
  ########################################
@@ -44,8 +44,8 @@ class EnvironmentTest < Minitest::Test
44
44
  end
45
45
 
46
46
  def clean_fake_rails
47
- Kernel.const_unset("Rails") if defined?(Kernel::Rails)
48
- Kernel.const_unset("ActionController") if defined?(Kernel::ActionController)
47
+ Kernel.send(:remove_const, "Rails") if defined?(Kernel::Rails)
48
+ Kernel.send(:remove_const, "ActionController") if defined?(Kernel::ActionController)
49
49
  end
50
50
 
51
51
  def fake_sinatra
@@ -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
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/external_service_metric_set'
4
+
5
+ module ScoutApm
6
+ class ExternalServiceMetricSetTest < Minitest::Test
7
+ def test_hard_limit
8
+ config = make_fake_config(
9
+ 'external_service_metric_limit' => 5, # The hard limit on db metrics
10
+ 'external_service_metric_report_limit' => 2
11
+ )
12
+ context = ScoutApm::AgentContext.new().tap{|c| c.config = config }
13
+ set = ExternalServiceMetricSet.new(context)
14
+
15
+ set << fake_stat("a", 10)
16
+ set << fake_stat("b", 20)
17
+ set << fake_stat("c", 30)
18
+ set << fake_stat("d", 40)
19
+ set << fake_stat("e", 50)
20
+ set << fake_stat("f", 60)
21
+
22
+ assert_equal 5, set.metrics.size
23
+ end
24
+
25
+ def test_report_limit
26
+ config = make_fake_config(
27
+ 'external_service_metric_limit' => 50, # much larger max, uninterested in hitting it.
28
+ 'external_service_metric_report_limit' => 2
29
+ )
30
+ context = ScoutApm::AgentContext.new().tap{|c| c.config = config }
31
+ set = ExternalServiceMetricSet.new(context)
32
+ set << fake_stat("a", 10)
33
+ set << fake_stat("b", 20)
34
+ set << fake_stat("c", 30)
35
+ set << fake_stat("d", 40)
36
+ set << fake_stat("e", 50)
37
+ set << fake_stat("f", 60)
38
+
39
+ assert_equal 2, set.metrics_to_report.size
40
+ assert_equal ["f","e"], set.metrics_to_report.map{|m| m.key}
41
+ end
42
+
43
+ def test_combine
44
+ config = make_fake_config(
45
+ 'external_service_metric_limit' => 5, # The hard limit on db metrics
46
+ 'external_service_metric_report_limit' => 2
47
+ )
48
+ context = ScoutApm::AgentContext.new().tap{|c| c.config = config }
49
+ set1 = ExternalServiceMetricSet.new(context)
50
+ set1 << fake_stat("a", 10)
51
+ set1 << fake_stat("b", 20)
52
+ set2 = ExternalServiceMetricSet.new(context)
53
+ set2 << fake_stat("c", 10)
54
+ set2 << fake_stat("d", 20)
55
+
56
+ combined = set1.combine!(set2)
57
+ assert_equal ["a", "b", "c", "d"], combined.metrics.map{|_k, m| m.key}.sort
58
+ end
59
+
60
+ def fake_stat(key, call_time)
61
+ OpenStruct.new(
62
+ :key => key,
63
+ :call_time => call_time
64
+ )
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,106 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/external_service_metric_stats'
4
+
5
+ module ScoutApm
6
+ class ExternalServiceMetricStatsTest < Minitest::Test
7
+ def test_as_json_empty_stats
8
+ stat = build("example.com", "GET", "Controller/public/index", 1, 10)
9
+
10
+ assert_equal({
11
+ :domain_name => "example.com",
12
+ :operation => "GET",
13
+ :call_count => 1,
14
+ :transaction_count => 0,
15
+ :scope => "Controller/public/index",
16
+ :histogram => [[10.0, 1]],
17
+
18
+ :max_call_time => 10.0,
19
+ :min_call_time => 10.0,
20
+ :call_time => 10.0
21
+ }, stat.as_json)
22
+ end
23
+
24
+ def test_increment_transaction_count
25
+ stat = build()
26
+ assert_equal 0, stat.transaction_count
27
+
28
+ stat.increment_transaction_count!
29
+
30
+ assert_equal 1, stat.transaction_count
31
+ end
32
+
33
+ def test_key_name
34
+ stat = build("example.com", "GET", "Controller/public/index")
35
+ assert_equal ["example.com", "GET", "Controller/public/index"], stat.key
36
+ end
37
+
38
+ def test_combine_min_call_time_picks_smallest
39
+ stat1, stat2 = build_pair
40
+ assert_equal 5.1, stat1.combine!(stat2).min_call_time
41
+ end
42
+
43
+ def test_combine_max_call_time_picks_largest
44
+ stat1, stat2 = build_pair
45
+ assert_equal 8.2, stat1.combine!(stat2).max_call_time
46
+ end
47
+
48
+ def test_combine_call_counts_adds
49
+ stat1, stat2 = build_pair
50
+ assert_equal 5, stat1.combine!(stat2).call_count
51
+ end
52
+
53
+ def test_combine_transaction_count_adds
54
+ stat1, stat2 = build_pair
55
+ 2.times { stat1.increment_transaction_count! }
56
+ 3.times { stat2.increment_transaction_count! }
57
+
58
+ assert_equal 5, stat1.combine!(stat2).call_count
59
+ end
60
+
61
+ def test_combine_doesnt_merge_with_self
62
+ stat = build
63
+ merged = stat.combine!(stat)
64
+
65
+ assert_equal DEFAULTS[:call_count], merged.call_count
66
+ assert_equal DEFAULTS[:call_time], merged.call_time
67
+ end
68
+
69
+ # A.combine!(B) should be the the same as B.combine!(A)
70
+ # Have to be a bit careful, since combine! is destructive, so make two pairs
71
+ # with same data to do both sides, then check that they result in the same
72
+ # answer
73
+ [:transaction_count, :call_count, :max_call_time, :min_call_time].each do |attr|
74
+ define_method :"test_combine_#{attr}_is_symmetric" do
75
+ stat1_a, stat2_a = build_pair
76
+ stat1_b, stat2_b = build_pair
77
+ merged_a = stat1_a.combine!(stat2_a)
78
+ merged_b = stat2_b.combine!(stat1_b)
79
+
80
+ assert_equal merged_a.send(attr), merged_b.send(attr)
81
+ end
82
+ end
83
+
84
+ #############
85
+ # Helpers #
86
+ #############
87
+ DEFAULTS = {
88
+ :call_count => 1,
89
+ :call_time => 10.0,
90
+ }
91
+
92
+ def build(domain_name="example.com",
93
+ operation="GET",
94
+ scope="Controller/public/index",
95
+ call_count=DEFAULTS[:call_count],
96
+ call_time=DEFAULTS[:call_time])
97
+ ExternalServiceMetricStats.new(domain_name, operation, scope, call_count, call_time)
98
+ end
99
+
100
+ def build_pair
101
+ stat1 = build("example.com", "GET", "Controller/public/index", 2, 5.1)
102
+ stat2 = build("example.com", "GET", "Controller/public/index", 3, 8.2)
103
+ [stat1, stat2]
104
+ end
105
+ end
106
+ end
@@ -13,4 +13,10 @@ class IgnoredUrlsTest < Minitest::Test
13
13
  i = ScoutApm::IgnoredUris.new(["/slow", "/health"])
14
14
  assert_equal false, i.ignore?("/users/2/health")
15
15
  end
16
+
17
+ def test_does_not_ignore_empty_string
18
+ i = ScoutApm::IgnoredUris.new(["", "/admin"])
19
+ assert_equal false, i.ignore?("/users/2/health")
20
+ assert_equal true, i.ignore?("/admin/dashboard")
21
+ end
16
22
  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(prepend: false)
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
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/http_client'
5
+
6
+ require 'httpclient'
7
+
8
+ class HttpClientTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::HttpClient.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::HTTPClient.ancestors.include?(ScoutApm::Instruments::HttpClientInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::HTTPClient.ancestors.include?(ScoutApm::Instruments::HttpClientInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/http'
5
+
6
+ require 'http'
7
+
8
+ class HttpTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::HTTP.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::HTTP::Client.ancestors.include?(ScoutApm::Instruments::HTTPInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::HTTP::Client.ancestors.include?(ScoutApm::Instruments::HTTPInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/moped'
5
+
6
+ require 'moped'
7
+
8
+ class MopedTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::Moped.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::Moped::Node.ancestors.include?(ScoutApm::Instruments::MopedInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::Moped::Node.ancestors.include?(ScoutApm::Instruments::MopedInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -7,7 +7,9 @@ require 'addressable/uri'
7
7
  class NetHttpTest < Minitest::Test
8
8
  def setup
9
9
  @context = ScoutApm::AgentContext.new
10
- ScoutApm::Instruments::NetHttp.new(@context).install
10
+ @instance = ScoutApm::Instruments::NetHttp.new(@context)
11
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
12
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
11
13
  end
12
14
 
13
15
  def test_request_scout_description_for_uri
@@ -24,4 +26,12 @@ class NetHttpTest < Minitest::Test
24
26
  req = Net::HTTP::Get.new(Addressable::URI.parse('http://example.org/here'))
25
27
  assert_equal '/here', Net::HTTP.new('').request_scout_description(req)
26
28
  end
29
+
30
+ def test_installs_using_proper_method
31
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
32
+ assert Net::HTTP.ancestors.include?(ScoutApm::Instruments::NetHttpInstrumentationPrepend)
33
+ else
34
+ assert_equal false, Net::HTTP.ancestors.include?(ScoutApm::Instruments::NetHttpInstrumentationPrepend)
35
+ end
36
+ end
27
37
  end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/redis5'
5
+
6
+ require 'redis'
7
+
8
+ class RedisTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::Redis5.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::Redis::Client.ancestors.include?(ScoutApm::Instruments::Redis5ClientInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::Redis::Client.ancestors.include?(ScoutApm::Instruments::Redis5ClientInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("typhoeus")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/typhoeus'
5
+
6
+ require 'typhoeus'
7
+
8
+ class TyphoeusTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @recorder = FakeRecorder.new
12
+ ScoutApm::Agent.instance.context.recorder = @recorder
13
+ ScoutApm::Instruments::Typhoeus.new(@context).install(prepend: false)
14
+ end
15
+
16
+ def test_instruments_typhoeus_hydra
17
+ hydra = Typhoeus::Hydra.new
18
+ 2.times.map{ hydra.queue(Typhoeus::Request.new("example.com", followlocation: true)) }
19
+
20
+ assert_equal "2 requests", hydra.scout_desc
21
+
22
+ hydra.run
23
+ assert_equal "0 requests", hydra.scout_desc
24
+ assert_recorded(@recorder, "HTTP", "Hydra", "2 requests")
25
+ end
26
+
27
+ def test_instruments_typhoeus
28
+ Typhoeus.get("example.com", followlocation: true)
29
+ assert_recorded(@recorder, "HTTP", "get", "example.com")
30
+ end
31
+
32
+ private
33
+
34
+ def assert_recorded(recorder, type, name, desc = nil)
35
+ req = recorder.requests.first
36
+ assert req, "recorder recorded no layers"
37
+ assert_equal type, req.root_layer.type
38
+ assert_equal name, req.root_layer.name
39
+ assert_equal desc, req.root_layer.desc if !desc.nil?
40
+ end
41
+ end
42
+ 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
  #############
File without changes
@@ -8,8 +8,11 @@ class TestRemoteServer < Minitest::Test
8
8
  logger_io = StringIO.new
9
9
  server = ScoutApm::Remote::Server.new(bind, port, router, Logger.new(logger_io))
10
10
 
11
+ # Cannot test this if we can't require webrick. Ruby 3 stopped including by default
12
+ skip unless server.require_webrick
13
+
11
14
  server.start
12
- sleep 0.01 # Let the server finish starting. The assert should instead allow a time
15
+ sleep 0.05 # Let the server finish starting. The assert should instead allow a time
13
16
  assert server.running?
14
17
  end
15
18
  end
@@ -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