scout_apm 2.6.10 → 3.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.rubocop.yml +3 -11
  4. data/CHANGELOG.markdown +4 -362
  5. data/Gemfile +1 -14
  6. data/README.markdown +7 -52
  7. data/Rakefile +1 -0
  8. data/ext/allocations/allocations.c +1 -7
  9. data/ext/allocations/extconf.rb +0 -1
  10. data/ext/rusage/rusage.c +0 -26
  11. data/ext/stacks/extconf.rb +37 -0
  12. data/ext/stacks/scout_atomics.h +86 -0
  13. data/ext/stacks/stacks.c +811 -0
  14. data/lib/scout_apm/agent/logging.rb +69 -0
  15. data/lib/scout_apm/agent/reporting.rb +126 -0
  16. data/lib/scout_apm/agent.rb +259 -138
  17. data/lib/scout_apm/app_server_load.rb +15 -41
  18. data/lib/scout_apm/attribute_arranger.rb +3 -14
  19. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -70
  20. data/lib/scout_apm/background_job_integrations/sidekiq.rb +24 -31
  21. data/lib/scout_apm/background_worker.rb +12 -23
  22. data/lib/scout_apm/capacity.rb +57 -0
  23. data/lib/scout_apm/config.rb +37 -206
  24. data/lib/scout_apm/context.rb +4 -20
  25. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  26. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  27. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  28. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
  29. data/lib/scout_apm/environment.rb +28 -42
  30. data/lib/scout_apm/fake_store.rb +0 -12
  31. data/lib/scout_apm/framework_integrations/rails_2.rb +1 -2
  32. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +6 -17
  33. data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
  34. data/lib/scout_apm/histogram.rb +3 -12
  35. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  36. data/lib/scout_apm/instant/middleware.rb +54 -202
  37. data/lib/scout_apm/instant_reporting.rb +7 -7
  38. data/lib/scout_apm/instruments/.DS_Store +0 -0
  39. data/lib/scout_apm/instruments/action_controller_rails_2.rb +9 -15
  40. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +76 -124
  41. data/lib/scout_apm/instruments/active_record.rb +29 -324
  42. data/lib/scout_apm/instruments/delayed_job.rb +57 -0
  43. data/lib/scout_apm/instruments/elasticsearch.rb +6 -10
  44. data/lib/scout_apm/instruments/grape.rb +9 -12
  45. data/lib/scout_apm/instruments/http_client.rb +7 -14
  46. data/lib/scout_apm/instruments/influxdb.rb +6 -10
  47. data/lib/scout_apm/instruments/middleware_detailed.rb +11 -15
  48. data/lib/scout_apm/instruments/middleware_summary.rb +5 -11
  49. data/lib/scout_apm/instruments/mongoid.rb +8 -39
  50. data/lib/scout_apm/instruments/moped.rb +6 -11
  51. data/lib/scout_apm/instruments/net_http.rb +9 -27
  52. data/lib/scout_apm/instruments/percentile_sampler.rb +23 -42
  53. data/lib/scout_apm/instruments/process/process_cpu.rb +6 -11
  54. data/lib/scout_apm/instruments/process/process_memory.rb +12 -17
  55. data/lib/scout_apm/instruments/rails_router.rb +6 -12
  56. data/lib/scout_apm/instruments/redis.rb +6 -10
  57. data/lib/scout_apm/instruments/sinatra.rb +4 -5
  58. data/lib/scout_apm/job_record.rb +2 -4
  59. data/lib/scout_apm/layaway.rb +34 -88
  60. data/lib/scout_apm/layaway_file.rb +3 -13
  61. data/lib/scout_apm/layer.rb +60 -25
  62. data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +6 -7
  63. data/lib/scout_apm/layer_converters/converter_base.rb +14 -203
  64. data/lib/scout_apm/layer_converters/depth_first_walker.rb +10 -22
  65. data/lib/scout_apm/layer_converters/error_converter.rb +8 -8
  66. data/lib/scout_apm/layer_converters/job_converter.rb +50 -37
  67. data/lib/scout_apm/layer_converters/metric_converter.rb +19 -18
  68. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +13 -13
  69. data/lib/scout_apm/layer_converters/slow_job_converter.rb +116 -52
  70. data/lib/scout_apm/layer_converters/slow_request_converter.rb +120 -51
  71. data/lib/scout_apm/metric_meta.rb +5 -0
  72. data/lib/scout_apm/metric_set.rb +1 -9
  73. data/lib/scout_apm/metric_stats.rb +8 -7
  74. data/lib/scout_apm/middleware.rb +9 -7
  75. data/lib/scout_apm/reporter.rb +24 -71
  76. data/lib/scout_apm/request_histograms.rb +0 -12
  77. data/lib/scout_apm/request_manager.rb +7 -5
  78. data/lib/scout_apm/scored_item_set.rb +0 -7
  79. data/lib/scout_apm/serializers/app_server_load_serializer.rb +0 -4
  80. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  81. data/lib/scout_apm/serializers/directive_serializer.rb +0 -4
  82. data/lib/scout_apm/serializers/payload_serializer.rb +4 -11
  83. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +16 -35
  84. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
  85. data/lib/scout_apm/server_integrations/passenger.rb +1 -1
  86. data/lib/scout_apm/server_integrations/puma.rb +2 -5
  87. data/lib/scout_apm/slow_job_policy.rb +13 -25
  88. data/lib/scout_apm/slow_job_record.rb +4 -13
  89. data/lib/scout_apm/slow_request_policy.rb +13 -25
  90. data/lib/scout_apm/slow_transaction.rb +5 -25
  91. data/lib/scout_apm/store.rb +32 -99
  92. data/lib/scout_apm/trace_compactor.rb +312 -0
  93. data/lib/scout_apm/tracer.rb +31 -35
  94. data/lib/scout_apm/tracked_request.rb +95 -262
  95. data/lib/scout_apm/utils/active_record_metric_name.rb +13 -88
  96. data/lib/scout_apm/utils/backtrace_parser.rb +4 -7
  97. data/lib/scout_apm/utils/fake_stacks.rb +87 -0
  98. data/lib/scout_apm/utils/installed_gems.rb +3 -7
  99. data/lib/scout_apm/utils/klass_helper.rb +2 -8
  100. data/lib/scout_apm/utils/null_logger.rb +13 -0
  101. data/lib/scout_apm/utils/sql_sanitizer.rb +5 -16
  102. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -7
  103. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -6
  104. data/lib/scout_apm/utils/unique_id.rb +0 -27
  105. data/lib/scout_apm/version.rb +2 -1
  106. data/lib/scout_apm.rb +25 -84
  107. data/scout_apm.gemspec +3 -17
  108. data/test/test_helper.rb +3 -57
  109. data/test/unit/agent_test.rb +54 -1
  110. data/test/unit/background_job_integrations/sidekiq_test.rb +3 -0
  111. data/test/unit/config_test.rb +12 -25
  112. data/test/unit/context_test.rb +4 -4
  113. data/test/unit/histogram_test.rb +4 -25
  114. data/test/unit/ignored_uris_test.rb +1 -1
  115. data/test/unit/instruments/active_record_instruments_test.rb +5 -0
  116. data/test/unit/layaway_test.rb +2 -62
  117. data/test/unit/serializers/payload_serializer_test.rb +15 -43
  118. data/test/unit/slow_request_policy_test.rb +6 -15
  119. data/test/unit/sql_sanitizer_test.rb +6 -53
  120. data/test/unit/store_test.rb +4 -73
  121. data/test/unit/utils/active_record_metric_name_test.rb +5 -59
  122. data/test/unit/utils/backtrace_parser_test.rb +1 -6
  123. data/tester.rb +53 -0
  124. metadata +28 -229
  125. data/.travis.yml +0 -26
  126. data/Guardfile +0 -43
  127. data/gems/README.md +0 -28
  128. data/gems/octoshark.gemfile +0 -4
  129. data/gems/rails3.gemfile +0 -5
  130. data/gems/rails4.gemfile +0 -4
  131. data/gems/rails5.gemfile +0 -4
  132. data/gems/rails6.gemfile +0 -4
  133. data/lib/scout_apm/agent/exit_handler.rb +0 -65
  134. data/lib/scout_apm/agent/preconditions.rb +0 -81
  135. data/lib/scout_apm/agent_context.rb +0 -261
  136. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +0 -31
  137. data/lib/scout_apm/auto_instrument/layer.rb +0 -23
  138. data/lib/scout_apm/auto_instrument/parser.rb +0 -27
  139. data/lib/scout_apm/auto_instrument/rails.rb +0 -175
  140. data/lib/scout_apm/auto_instrument.rb +0 -5
  141. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +0 -55
  142. data/lib/scout_apm/background_job_integrations/que.rb +0 -134
  143. data/lib/scout_apm/background_job_integrations/resque.rb +0 -88
  144. data/lib/scout_apm/background_job_integrations/shoryuken.rb +0 -124
  145. data/lib/scout_apm/background_job_integrations/sneakers.rb +0 -87
  146. data/lib/scout_apm/background_recorder.rb +0 -48
  147. data/lib/scout_apm/db_query_metric_set.rb +0 -97
  148. data/lib/scout_apm/db_query_metric_stats.rb +0 -102
  149. data/lib/scout_apm/debug.rb +0 -37
  150. data/lib/scout_apm/detailed_trace.rb +0 -217
  151. data/lib/scout_apm/error.rb +0 -27
  152. data/lib/scout_apm/error_service/error_buffer.rb +0 -39
  153. data/lib/scout_apm/error_service/error_record.rb +0 -211
  154. data/lib/scout_apm/error_service/ignored_exceptions.rb +0 -66
  155. data/lib/scout_apm/error_service/middleware.rb +0 -32
  156. data/lib/scout_apm/error_service/notifier.rb +0 -33
  157. data/lib/scout_apm/error_service/payload.rb +0 -47
  158. data/lib/scout_apm/error_service/periodic_work.rb +0 -17
  159. data/lib/scout_apm/error_service/railtie.rb +0 -11
  160. data/lib/scout_apm/error_service/sidekiq.rb +0 -80
  161. data/lib/scout_apm/error_service.rb +0 -32
  162. data/lib/scout_apm/extensions/config.rb +0 -87
  163. data/lib/scout_apm/extensions/transaction_callback_payload.rb +0 -74
  164. data/lib/scout_apm/git_revision.rb +0 -59
  165. data/lib/scout_apm/instrument_manager.rb +0 -88
  166. data/lib/scout_apm/instruments/action_view.rb +0 -141
  167. data/lib/scout_apm/instruments/http.rb +0 -48
  168. data/lib/scout_apm/instruments/memcached.rb +0 -43
  169. data/lib/scout_apm/instruments/resque.rb +0 -39
  170. data/lib/scout_apm/instruments/samplers.rb +0 -11
  171. data/lib/scout_apm/layer_children_set.rb +0 -86
  172. data/lib/scout_apm/layer_converters/database_converter.rb +0 -70
  173. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +0 -38
  174. data/lib/scout_apm/layer_converters/histograms.rb +0 -15
  175. data/lib/scout_apm/layer_converters/trace_converter.rb +0 -184
  176. data/lib/scout_apm/limited_layer.rb +0 -126
  177. data/lib/scout_apm/logger.rb +0 -158
  178. data/lib/scout_apm/periodic_work.rb +0 -47
  179. data/lib/scout_apm/rack.rb +0 -26
  180. data/lib/scout_apm/remote/message.rb +0 -27
  181. data/lib/scout_apm/remote/recorder.rb +0 -57
  182. data/lib/scout_apm/remote/router.rb +0 -49
  183. data/lib/scout_apm/remote/server.rb +0 -60
  184. data/lib/scout_apm/reporting.rb +0 -143
  185. data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +0 -15
  186. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
  187. data/lib/scout_apm/synchronous_recorder.rb +0 -30
  188. data/lib/scout_apm/tasks/doctor.rb +0 -75
  189. data/lib/scout_apm/tasks/support.rb +0 -22
  190. data/lib/scout_apm/transaction.rb +0 -13
  191. data/lib/scout_apm/transaction_time_consumed.rb +0 -51
  192. data/lib/scout_apm/utils/gzip_helper.rb +0 -24
  193. data/lib/scout_apm/utils/marshal_logging.rb +0 -90
  194. data/lib/scout_apm/utils/numbers.rb +0 -14
  195. data/lib/scout_apm/utils/scm.rb +0 -14
  196. data/lib/tasks/doctor.rake +0 -11
  197. data/test/tmp/README.md +0 -17
  198. data/test/unit/agent_context_test.rb +0 -15
  199. data/test/unit/auto_instrument/assignments-instrumented.rb +0 -31
  200. data/test/unit/auto_instrument/assignments.rb +0 -31
  201. data/test/unit/auto_instrument/controller-ast.txt +0 -57
  202. data/test/unit/auto_instrument/controller-instrumented.rb +0 -49
  203. data/test/unit/auto_instrument/controller.rb +0 -49
  204. data/test/unit/auto_instrument/rescue_from-instrumented.rb +0 -13
  205. data/test/unit/auto_instrument/rescue_from.rb +0 -13
  206. data/test/unit/auto_instrument_test.rb +0 -54
  207. data/test/unit/db_query_metric_set_test.rb +0 -67
  208. data/test/unit/db_query_metric_stats_test.rb +0 -113
  209. data/test/unit/error_service/error_buffer_test.rb +0 -25
  210. data/test/unit/error_service/ignored_exceptions_test.rb +0 -49
  211. data/test/unit/extensions/periodic_callbacks_test.rb +0 -58
  212. data/test/unit/extensions/transaction_callbacks_test.rb +0 -58
  213. data/test/unit/fake_store_test.rb +0 -10
  214. data/test/unit/git_revision_test.rb +0 -15
  215. data/test/unit/instruments/active_record_test.rb +0 -40
  216. data/test/unit/instruments/net_http_test.rb +0 -27
  217. data/test/unit/instruments/percentile_sampler_test.rb +0 -133
  218. data/test/unit/layer_children_set_test.rb +0 -97
  219. data/test/unit/layer_converters/depth_first_walker_test.rb +0 -70
  220. data/test/unit/layer_converters/metric_converter_test.rb +0 -22
  221. data/test/unit/layer_converters/stubs.rb +0 -33
  222. data/test/unit/limited_layer_test.rb +0 -53
  223. data/test/unit/logger_test.rb +0 -69
  224. data/test/unit/remote/test_message.rb +0 -13
  225. data/test/unit/remote/test_router.rb +0 -33
  226. data/test/unit/remote/test_server.rb +0 -15
  227. data/test/unit/request_histograms_test.rb +0 -17
  228. data/test/unit/tracer_test.rb +0 -76
  229. data/test/unit/tracked_request_test.rb +0 -71
  230. data/test/unit/transaction_test.rb +0 -14
  231. data/test/unit/transaction_time_consumed_test.rb +0 -46
  232. data/test/unit/utils/numbers_test.rb +0 -15
  233. data/test/unit/utils/scm.rb +0 -17
@@ -1,9 +1,6 @@
1
1
  module ScoutApm
2
2
  module BackgroundJobIntegrations
3
3
  class DelayedJob
4
- ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper'.freeze
5
- DJ_PERFORMABLE_METHOD = 'Delayed::PerformableMethod'.freeze
6
-
7
4
  attr_reader :logger
8
5
 
9
6
  def name
@@ -11,78 +8,12 @@ module ScoutApm
11
8
  end
12
9
 
13
10
  def present?
14
- defined?(::Delayed::Job)
11
+ defined?(::Delayed::Job) && (File.basename($0) =~ /\Adelayed_job/)
15
12
  end
16
13
 
17
14
  def forking?
18
15
  false
19
16
  end
20
-
21
- def install
22
- plugin = Class.new(Delayed::Plugin) do
23
- require 'delayed_job'
24
-
25
- callbacks do |lifecycle|
26
- lifecycle.around(:invoke_job) do |job, *args, &block|
27
- ScoutApm::Agent.instance.start_background_worker unless ScoutApm::Agent.instance.background_worker_running?
28
-
29
- name = begin
30
- case job.payload_object.class.to_s
31
-
32
- # ActiveJob's class wraps the actual job class
33
- when ACTIVE_JOB_KLASS
34
- job.payload_object.job_data["job_class"]
35
-
36
- # An adhoc job, called like `@user.delay.fib(10)`.
37
- # returns a string like "User#fib"
38
- when DJ_PERFORMABLE_METHOD
39
- job.name
40
-
41
- # A "real" job called like `Delayed::Job.enqueue(MyJob.new)`
42
- # returns "MyJob"
43
- else
44
- job.payload_object.class.to_s
45
- end
46
- rescue
47
- # Fall back to whatever DJ thinks the name is.
48
- job.name
49
- end
50
-
51
- queue = job.queue || "default"
52
-
53
- req = ScoutApm::RequestManager.lookup
54
-
55
- begin
56
- latency = Time.now - [job.created_at, job.run_at].max
57
- req.annotate_request(:queue_latency => latency)
58
- rescue
59
- end
60
-
61
- queue_layer = ScoutApm::Layer.new('Queue', queue)
62
- job_layer = ScoutApm::Layer.new('Job', name)
63
-
64
- begin
65
- req.start_layer(queue_layer)
66
- started_queue = true
67
- req.start_layer(job_layer)
68
- started_job = true
69
-
70
- # Call the job itself.
71
- block.call(job, *args)
72
- rescue
73
- req.error!
74
- raise
75
- ensure
76
- req.stop_layer if started_job
77
- req.stop_layer if started_queue
78
- end
79
- end
80
- end
81
- end
82
-
83
- Delayed::Worker.plugins << plugin # ScoutApm::BackgroundJobIntegrations::DelayedJobPlugin
84
- end
85
17
  end
86
18
  end
87
19
  end
88
-
@@ -40,10 +40,10 @@ module ScoutApm
40
40
  require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
41
41
 
42
42
  ::Sidekiq::Processor.class_eval do
43
- def initialize_with_scout(*args)
43
+ def initialize_with_scout(boss)
44
44
  agent = ::ScoutApm::Agent.instance
45
- agent.start
46
- initialize_without_scout(*args)
45
+ agent.start_background_worker
46
+ initialize_without_scout(boss)
47
47
  end
48
48
 
49
49
  alias_method :initialize_without_scout, :initialize
@@ -55,14 +55,27 @@ module ScoutApm
55
55
  # We insert this middleware into the Sidekiq stack, to capture each job,
56
56
  # and time them.
57
57
  class SidekiqMiddleware
58
+ ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
59
+
58
60
  def call(_worker, msg, queue)
59
61
  req = ScoutApm::RequestManager.lookup
62
+ req.job!
60
63
  req.annotate_request(:queue_latency => latency(msg))
61
64
 
65
+ queue_layer = ScoutApm::Layer.new('Queue', queue)
66
+ job_layer = ScoutApm::Layer.new('Job', job_class(msg))
67
+
68
+ if ScoutApm::Agent.instance.config.value('profile') && SidekiqMiddleware.version_supports_profiling?
69
+ # Capture ScoutProf if we can
70
+ #req.enable_profiled_thread!
71
+ #job_layer.set_root_class(job_class)
72
+ #job_layer.traced!
73
+ end
74
+
62
75
  begin
63
- req.start_layer(ScoutApm::Layer.new('Queue', queue))
76
+ req.start_layer(queue_layer)
64
77
  started_queue = true
65
- req.start_layer(ScoutApm::Layer.new('Job', job_class(msg)))
78
+ req.start_layer(job_layer)
66
79
  started_job = true
67
80
 
68
81
  yield
@@ -76,36 +89,12 @@ module ScoutApm
76
89
  end
77
90
 
78
91
  UNKNOWN_CLASS_PLACEHOLDER = 'UnknownJob'.freeze
79
- ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
80
- DELAYED_WRAPPER_KLASS = 'Sidekiq::Extensions::DelayedClass'.freeze
81
-
82
92
 
83
93
  def job_class(msg)
84
94
  job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
85
-
86
95
  if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
87
- begin
88
- job_class = msg['wrapped']
89
- rescue
90
- ACTIVE_JOB_KLASS
91
- end
92
- elsif job_class == DELAYED_WRAPPER_KLASS
93
- begin
94
- # Extract the info out of the wrapper
95
- yml = msg['args'].first
96
- deserialized_args = YAML.load(yml)
97
- klass, method, *rest = deserialized_args
98
-
99
- # If this is an instance of a class, get the class itself
100
- # Prevents instances from coming through named like "#<Foo:0x007ffd7a9dd8a0>"
101
- klass = klass.class unless klass.is_a? Module
102
-
103
- job_class = [klass, method].map(&:to_s).join(".")
104
- rescue
105
- DELAYED_WRAPPER_KLASS
106
- end
96
+ job_class = msg['wrapped']
107
97
  end
108
-
109
98
  job_class
110
99
  rescue
111
100
  UNKNOWN_CLASS_PLACEHOLDER
@@ -121,6 +110,10 @@ module ScoutApm
121
110
  rescue
122
111
  0
123
112
  end
124
- end
113
+
114
+ def self.version_supports_profiling?
115
+ @@sidekiq_supports_profling ||= defined?(::Sidekiq::VERSION) && Gem::Dependency.new('', '~> 4.0').match?('', ::Sidekiq::VERSION.to_s)
116
+ end
117
+ end # SidekiqMiddleware
125
118
  end
126
119
  end
@@ -6,24 +6,13 @@ module ScoutApm
6
6
 
7
7
  attr_reader :period
8
8
 
9
- attr_reader :context
10
-
11
- def initialize(context, period=DEFAULT_PERIOD)
12
- @context = context
9
+ def initialize(period=DEFAULT_PERIOD)
13
10
  @period = period
14
11
  @keep_running = true
15
12
  end
16
13
 
17
- def logger
18
- context.logger
19
- end
20
-
21
- def running?
22
- @keep_running
23
- end
24
-
25
14
  def stop
26
- logger.debug "Background Worker: stop requested"
15
+ ScoutApm::Agent.instance.logger.debug "Background Worker: stop requested"
27
16
  @keep_running = false
28
17
  end
29
18
 
@@ -36,13 +25,19 @@ module ScoutApm
36
25
  def start(&block)
37
26
  @task = block
38
27
 
39
- logger.debug "Background Worker: starting to run every #{period} seconds"
28
+ ScoutApm::Agent.instance.logger.debug "Background Worker: Starting Background Worker, running every #{period} seconds"
40
29
 
41
30
  # The first run should be 1 period of time from now
42
31
  next_time = Time.now + period
43
32
 
44
33
  loop do
45
34
  begin
35
+ # Bail out if @keep_running is false
36
+ unless @keep_running
37
+ ScoutApm::Agent.instance.logger.debug "Background Worker: breaking from loop"
38
+ break
39
+ end
40
+
46
41
  now = Time.now
47
42
 
48
43
  # Sleep the correct amount of time to reach next_time
@@ -52,12 +47,6 @@ module ScoutApm
52
47
  now = Time.now
53
48
  end
54
49
 
55
- # Bail out if @keep_running is false
56
- unless @keep_running
57
- logger.debug "Background Worker: breaking from loop"
58
- break
59
- end
60
-
61
50
  @task.call
62
51
 
63
52
  # Adjust the next time to run forward by @periods until it is in the future
@@ -65,9 +54,9 @@ module ScoutApm
65
54
  next_time += period
66
55
  end
67
56
  rescue
68
- logger.debug "Background Worker Exception!"
69
- logger.debug $!.message
70
- logger.debug $!.backtrace
57
+ ScoutApm::Agent.instance.logger.debug "Background Worker Exception!"
58
+ ScoutApm::Agent.instance.logger.debug $!.message
59
+ ScoutApm::Agent.instance.logger.debug $!.backtrace
71
60
  end
72
61
  end
73
62
  end
@@ -0,0 +1,57 @@
1
+ # Encapsulates logic for determining capacity utilization of the Ruby processes.
2
+ module ScoutApm
3
+ class Capacity
4
+ attr_reader :processing_start_time, :accumulated_time, :transaction_entry_time
5
+
6
+ def initialize
7
+ @processing_start_time = Time.now
8
+ @lock ||= Mutex.new # the transaction_entry_time could be modified while processing a request or when #process is called.
9
+ @accumulated_time = 0.0
10
+ end
11
+
12
+ # Called when a transaction is traced.
13
+ def start_transaction!
14
+ @lock.synchronize do
15
+ @transaction_entry_time = Time.now
16
+ end
17
+ end
18
+
19
+ # Called when a transaction completes to record its time used.
20
+ def finish_transaction!
21
+ @lock.synchronize do
22
+ if transaction_entry_time
23
+ @accumulated_time += (Time.now - transaction_entry_time).to_f
24
+ else
25
+ ScoutApm::Agent.instance.logger.warn "No transaction entry time. Not recording capacity metrics for transaction."
26
+ end
27
+ @transaction_entry_time = nil
28
+ end
29
+ end
30
+
31
+ # Ran when sending metrics to server. Reports capacity usage metrics.
32
+ def process
33
+ process_time = Time.now
34
+ ScoutApm::Agent.instance.logger.debug "Processing capacity usage for [#{@processing_start_time}] to [#{process_time}]. Time Spent: #{@accumulated_time}."
35
+ @lock.synchronize do
36
+ time_spent = @accumulated_time
37
+ @accumulated_time = 0.0
38
+ # If a transaction is still running, capture its running time up to now and
39
+ # reset the +transaction_entry_time+ to now.
40
+ if @transaction_entry_time
41
+ time_spent += (process_time - @transaction_entry_time).to_f
42
+ ScoutApm::Agent.instance.logger.debug "A transaction is running while calculating capacity. Start time: [#{transaction_entry_time}]. Will update the entry time to [#{process_time}]."
43
+ @transaction_entry_time = process_time # prevent from over-counting capacity usage. update the transaction start time to now.
44
+ end
45
+ time_spent = 0.0 if time_spent < 0.0
46
+
47
+ window = (process_time - processing_start_time).to_f # time period we are evaulating capacity usage.
48
+ window = 1.0 if window <= 0.0 # prevent divide-by-zero if clock adjusted.
49
+ capacity = time_spent / window
50
+ ScoutApm::Agent.instance.logger.debug "Instance/Capacity: #{capacity}"
51
+ ScoutApm::Agent.instance.store.track_one!("Instance", "Capacity", capacity)
52
+
53
+ @processing_start_time = process_time
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,89 +5,25 @@ require 'scout_apm/environment'
5
5
 
6
6
  # Valid Config Options:
7
7
  #
8
- # This list is complete, but some are old and unused, or for developers of
9
- # scout_apm itself. See the documentation at https://docs.scoutapm.com for
10
- # customer-focused documentation.
11
- #
12
8
  # application_root - override the detected directory of the application
13
- # collect_remote_ip - automatically capture user's IP into a Trace's Context
14
- # compress_payload - true/false to enable gzipping of payload
15
9
  # data_file - override the default temporary storage location. Must be a location in a writable directory
16
- # dev_trace - true or false. Enables always-on tracing in development environmen only
10
+ # host - override the default hostname detection. Default varies by environment - either system hostname, or PAAS hostname
17
11
  # direct_host - override the default "direct" host. The direct_host bypasses the ingestion pipeline and goes directly to the webserver, and is primarily used for features under development.
18
- # enable_background_jobs - true or false
19
- # host - configuration used in development
20
- # hostname - override the default hostname detection. Default varies by environment - either system hostname, or PAAS hostname
21
12
  # key - the account key with Scout APM. Found in Settings in the Web UI
22
13
  # log_file_path - either a directory or "STDOUT".
23
14
  # log_level - DEBUG / INFO / WARN as usual
24
- # max_traces - Internal: An experiment in trace quality, this requires a server-side setting as well. Setting this to a higher value will make your app server work harder for no benefit.
25
15
  # monitor - true or false. False prevents any instrumentation from starting
26
16
  # name - override the name reported to APM. This is the name that shows in the Web UI
27
- # profile - turn on/off scoutprof (only applicable in Gem versions including scoutprof)
28
- # proxy - an http proxy
29
- # report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
30
- # scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
31
17
  # uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
32
- # remote_agent_host - Internal: What host to bind to, and also send messages to for remote. Default: 127.0.0.1.
33
- # remote_agent_port - What port to bind the remote webserver to
34
- # start_resque_server_instrument - Used in special situations with certain Resque installs
35
- # timeline_traces - true/false to enable sending of of the timeline trace format.
36
- # auto_instruments - true/false whether to install autoinstruments. Only installed if on a supported Ruby version.
37
- # auto_instruments_ignore - An array of file names to exclude from autoinstruments (Ex: ['application_controller']).
18
+ # report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
19
+ # dev_trace - true or false. Enables always-on tracing in development environmen only
20
+ # enable_background_jobs - true or false
38
21
  #
39
22
  # Any of these config settings can be set with an environment variable prefixed
40
23
  # by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
41
24
 
42
25
  module ScoutApm
43
26
  class Config
44
- KNOWN_CONFIG_OPTIONS = [
45
- 'application_root',
46
- 'async_recording',
47
- 'collect_remote_ip',
48
- 'compress_payload',
49
- 'config_file',
50
- 'data_file',
51
- 'database_metric_limit',
52
- 'database_metric_report_limit',
53
- 'detailed_middleware',
54
- 'dev_trace',
55
- 'direct_host',
56
- 'disabled_instruments',
57
- 'enable_background_jobs',
58
- 'host',
59
- 'hostname',
60
- 'ignore',
61
- 'key',
62
- 'log_class',
63
- 'log_file_path',
64
- 'log_level',
65
- 'log_stderr',
66
- 'log_stdout',
67
- 'max_traces',
68
- 'monitor',
69
- 'name',
70
- 'profile',
71
- 'proxy',
72
- 'remote_agent_host',
73
- 'remote_agent_port',
74
- 'report_format',
75
- 'revision_sha',
76
- 'scm_subdirectory',
77
- 'start_resque_server_instrument',
78
- 'ssl_cert_file',
79
- 'uri_reporting',
80
- 'instrument_http_url_length',
81
- 'timeline_traces',
82
- 'auto_instruments',
83
- 'auto_instruments_ignore',
84
-
85
- # Error Service Related Configuration
86
- 'errors_enabled',
87
- 'errors_ignored_exceptions',
88
- 'errors_filtered_params',
89
- 'errors_host',
90
- ]
91
27
 
92
28
  ################################################################################
93
29
  # Coersions
@@ -151,12 +87,6 @@ module ScoutApm
151
87
  end
152
88
  end
153
89
 
154
- class IntegerCoercion
155
- def coerce(val)
156
- val.to_i
157
- end
158
- end
159
-
160
90
  # Simply returns the passed in value, without change
161
91
  class NullCoercion
162
92
  def coerce(val)
@@ -166,25 +96,10 @@ module ScoutApm
166
96
 
167
97
 
168
98
  SETTING_COERCIONS = {
169
- 'async_recording' => BooleanCoercion.new,
170
- 'detailed_middleware' => BooleanCoercion.new,
171
- 'dev_trace' => BooleanCoercion.new,
172
- 'enable_background_jobs' => BooleanCoercion.new,
173
- 'ignore' => JsonCoercion.new,
174
- 'max_traces' => IntegerCoercion.new,
175
- 'monitor' => BooleanCoercion.new,
176
- 'collect_remote_ip' => BooleanCoercion.new,
177
- 'compress_payload' => BooleanCoercion.new,
178
- 'database_metric_limit' => IntegerCoercion.new,
179
- 'database_metric_report_limit' => IntegerCoercion.new,
180
- 'instrument_http_url_length' => IntegerCoercion.new,
181
- 'start_resque_server_instrument' => BooleanCoercion.new,
182
- 'timeline_traces' => BooleanCoercion.new,
183
- 'auto_instruments' => BooleanCoercion.new,
184
- 'auto_instruments_ignore' => JsonCoercion.new,
185
- 'errors_enabled' => BooleanCoercion.new,
186
- 'errors_ignored_exceptions' => JsonCoercion.new,
187
- 'errors_filtered_params' => JsonCoercion.new,
99
+ "monitor" => BooleanCoercion.new,
100
+ "enable_background_jobs" => BooleanCoercion.new,
101
+ "dev_trace" => BooleanCoercion.new,
102
+ "ignore" => JsonCoercion.new,
188
103
  }
189
104
 
190
105
 
@@ -194,44 +109,34 @@ module ScoutApm
194
109
 
195
110
  # Load up a config instance without attempting to load a file.
196
111
  # Useful for bootstrapping.
197
- def self.without_file(context)
112
+ def self.without_file
198
113
  overlays = [
199
114
  ConfigEnvironment.new,
200
115
  ConfigDefaults.new,
201
116
  ConfigNull.new,
202
117
  ]
203
- new(context, overlays)
118
+ new(overlays)
204
119
  end
205
120
 
206
121
  # Load up a config instance, attempting to load a yaml file. Allows a
207
122
  # definite location if requested, or will attempt to load the default
208
123
  # configuration file: APP_ROOT/config/scout_apm.yml
209
- def self.with_file(context, file_path=nil, config={})
124
+ def self.with_file(file_path=nil, config={})
210
125
  overlays = [
211
126
  ConfigEnvironment.new,
212
- ConfigFile.new(context, file_path, config),
127
+ ConfigFile.new(file_path, config),
213
128
  ConfigDefaults.new,
214
129
  ConfigNull.new,
215
130
  ]
216
- new(context, overlays)
131
+ new(overlays)
217
132
  end
218
133
 
219
- def initialize(context, overlays)
220
- @context = context
134
+ def initialize(overlays)
221
135
  @overlays = Array(overlays)
222
136
  end
223
137
 
224
- # For a given key, what is the first overlay says that it can handle it?
225
- def overlay_for_key(key)
226
- @overlays.detect{ |overlay| overlay.has_key?(key) }
227
- end
228
-
229
138
  def value(key)
230
- if ! KNOWN_CONFIG_OPTIONS.include?(key)
231
- logger.debug("Requested looking up a unknown configuration key: #{key} (not a problem. Evaluate and add to config.rb)")
232
- end
233
-
234
- o = overlay_for_key(key)
139
+ o = @overlays.detect{ |overlay| overlay.has_key?(key) }
235
140
  raw_value = if o
236
141
  o.value(key)
237
142
  else
@@ -243,63 +148,18 @@ module ScoutApm
243
148
  coercion.coerce(raw_value)
244
149
  end
245
150
 
246
- # Did we load anything for configuration?
247
- def any_keys_found?
248
- @overlays.any? { |overlay| overlay.any_keys_found? }
249
- end
250
-
251
- # Returns an array of config keys, values, and source
252
- # {key: "monitor", value: "true", source: "environment"}
253
- #
254
- def all_settings
255
- KNOWN_CONFIG_OPTIONS.inject([]) do |memo, key|
256
- o = overlay_for_key(key)
257
- memo << {:key => key, :value => value(key).inspect, :source => o.name}
258
- end
259
- end
260
-
261
- def log_settings(logger)
262
- logger.debug(
263
- "Resolved Setting Values:\n" +
264
- all_settings.map{|hsh| "#{hsh[:source]} - #{hsh[:key]}: #{hsh[:value]}"}.join("\n")
265
- )
266
- end
267
-
268
- def logger
269
- @context.logger
270
- end
271
-
272
151
  class ConfigDefaults
273
152
  DEFAULTS = {
274
- 'compress_payload' => true,
275
- 'detailed_middleware' => false,
276
- 'dev_trace' => false,
153
+ 'host' => 'https://checkin.scoutapp.com',
277
154
  'direct_host' => 'https://apm.scoutapp.com',
155
+ 'log_level' => 'info',
156
+ 'uri_reporting' => 'full_path',
157
+ 'report_format' => 'json',
278
158
  'disabled_instruments' => [],
279
159
  'enable_background_jobs' => true,
280
- 'host' => 'https://checkin.scoutapp.com',
281
160
  'ignore' => [],
282
- 'log_level' => 'info',
283
- 'max_traces' => 10,
284
- 'profile' => true, # for scoutprof
285
- 'report_format' => 'json',
286
- 'scm_subdirectory' => '',
287
- 'uri_reporting' => 'full_path',
288
- 'remote_agent_host' => '127.0.0.1',
289
- 'remote_agent_port' => 7721, # picked at random
290
- 'database_metric_limit' => 5000, # The hard limit on db metrics
291
- 'database_metric_report_limit' => 1000,
292
- 'instrument_http_url_length' => 300,
293
- 'start_resque_server_instrument' => true, # still only starts if Resque is detected
294
- 'collect_remote_ip' => true,
295
- 'timeline_traces' => true,
296
- 'auto_instruments' => false,
297
- 'auto_instruments_ignore' => [],
298
- 'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
299
- 'errors_enabled' => false,
300
- 'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
301
- 'errors_filtered_params' => %w(password s3-key),
302
- 'errors_host' => 'https://errors.scoutapm.com',
161
+ 'dev_trace' => false,
162
+ 'profile' => true # for scoutprof
303
163
  }.freeze
304
164
 
305
165
  def value(key)
@@ -309,15 +169,6 @@ module ScoutApm
309
169
  def has_key?(key)
310
170
  DEFAULTS.has_key?(key)
311
171
  end
312
-
313
- # Defaults are here, but not counted as user specified.
314
- def any_keys_found?
315
- false
316
- end
317
-
318
- def name
319
- "defaults"
320
- end
321
172
  end
322
173
 
323
174
 
@@ -332,14 +183,6 @@ module ScoutApm
332
183
  def has_key?(*)
333
184
  true
334
185
  end
335
-
336
- def any_keys_found?
337
- false
338
- end
339
-
340
- def name
341
- "no-config"
342
- end
343
186
  end
344
187
 
345
188
  class ConfigEnvironment
@@ -355,24 +198,13 @@ module ScoutApm
355
198
  def key_to_env_key(key)
356
199
  'SCOUT_' + key.upcase
357
200
  end
358
-
359
- def any_keys_found?
360
- KNOWN_CONFIG_OPTIONS.any? { |option|
361
- ENV.has_key?(key_to_env_key(option))
362
- }
363
- end
364
-
365
- def name
366
- "environment"
367
- end
368
201
  end
369
202
 
370
203
  # Attempts to load a configuration file, and parse it as YAML. If the file
371
204
  # is not found, inaccessbile, or unparsable, log a message to that effect,
372
205
  # and move on.
373
206
  class ConfigFile
374
- def initialize(context, file_path=nil, config={})
375
- @context = context
207
+ def initialize(file_path=nil, config={})
376
208
  @config = config || {}
377
209
  @resolved_file_path = file_path || determine_file_path
378
210
  load_file(@resolved_file_path)
@@ -391,20 +223,8 @@ module ScoutApm
391
223
  @settings.has_key?(key)
392
224
  end
393
225
 
394
- def any_keys_found?
395
- KNOWN_CONFIG_OPTIONS.any? { |option|
396
- @settings.has_key?(option)
397
- }
398
- end
399
-
400
- def name
401
- "config-file"
402
- end
403
-
404
226
  private
405
227
 
406
- attr_reader :context
407
-
408
228
  def load_file(file)
409
229
  @settings = {}
410
230
  if !File.exist?(@resolved_file_path)
@@ -434,21 +254,32 @@ module ScoutApm
434
254
  @file_loaded = false
435
255
  end
436
256
  rescue Exception => e # Explicit `Exception` handling to catch SyntaxError and anything else that ERB or YAML may throw
437
- logger.info("Failed loading configuration file (#{@resolved_file_path}): #{e.message}. ScoutAPM will continue starting with configuration from ENV and defaults")
257
+ logger.debug("Failed loading configuration file: #{e.message}. ScoutAPM will continue starting with configuration from ENV and defaults")
438
258
  @file_loaded = false
439
259
  end
440
260
  end
441
261
 
442
262
  def determine_file_path
443
- File.join(context.environment.root, "config", "scout_apm.yml")
263
+ File.join(ScoutApm::Environment.instance.root, "config", "scout_apm.yml")
444
264
  end
445
265
 
446
266
  def app_environment
447
- @config[:environment] || context.environment.env
267
+ @config[:environment] || ScoutApm::Environment.instance.env
448
268
  end
449
269
 
450
270
  def logger
451
- context.logger
271
+ if ScoutApm::Agent.instance.logger
272
+ return ScoutApm::Agent.instance.logger
273
+ else
274
+ l = Logger.new(STDOUT)
275
+ if ENV["SCOUT_LOG_LEVEL"] == "debug"
276
+ l.level = Logger::DEBUG
277
+ else
278
+ l.level = Logger::INFO
279
+ end
280
+
281
+ return l
282
+ end
452
283
  end
453
284
  end
454
285
  end