scout_apm 2.3.5 → 2.4.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +0 -23
  3. data/lib/scout_apm.rb +21 -10
  4. data/lib/scout_apm/agent.rb +98 -336
  5. data/lib/scout_apm/agent/exit_handler.rb +64 -0
  6. data/lib/scout_apm/agent/preconditions.rb +69 -0
  7. data/lib/scout_apm/agent_context.rb +226 -0
  8. data/lib/scout_apm/app_server_load.rb +20 -18
  9. data/lib/scout_apm/background_job_integrations/resque.rb +7 -8
  10. data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -8
  11. data/lib/scout_apm/background_recorder.rb +8 -3
  12. data/lib/scout_apm/background_worker.rb +14 -7
  13. data/lib/scout_apm/config.rb +35 -29
  14. data/lib/scout_apm/context.rb +11 -4
  15. data/lib/scout_apm/db_query_metric_set.rb +17 -5
  16. data/lib/scout_apm/debug.rb +1 -1
  17. data/lib/scout_apm/environment.rb +10 -14
  18. data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
  19. data/lib/scout_apm/git_revision.rb +13 -8
  20. data/lib/scout_apm/histogram.rb +1 -1
  21. data/lib/scout_apm/instant/middleware.rb +7 -7
  22. data/lib/scout_apm/instant_reporting.rb +7 -7
  23. data/lib/scout_apm/instrument_manager.rb +87 -0
  24. data/lib/scout_apm/instruments/action_controller_rails_2.rb +12 -7
  25. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +16 -11
  26. data/lib/scout_apm/instruments/action_view.rb +11 -7
  27. data/lib/scout_apm/instruments/active_record.rb +28 -51
  28. data/lib/scout_apm/instruments/elasticsearch.rb +10 -6
  29. data/lib/scout_apm/instruments/grape.rb +12 -8
  30. data/lib/scout_apm/instruments/http_client.rb +11 -10
  31. data/lib/scout_apm/instruments/influxdb.rb +10 -6
  32. data/lib/scout_apm/instruments/middleware_detailed.rb +11 -5
  33. data/lib/scout_apm/instruments/middleware_summary.rb +11 -5
  34. data/lib/scout_apm/instruments/mongoid.rb +10 -5
  35. data/lib/scout_apm/instruments/moped.rb +11 -6
  36. data/lib/scout_apm/instruments/net_http.rb +11 -9
  37. data/lib/scout_apm/instruments/percentile_sampler.rb +8 -6
  38. data/lib/scout_apm/instruments/process/process_cpu.rb +8 -4
  39. data/lib/scout_apm/instruments/process/process_memory.rb +15 -10
  40. data/lib/scout_apm/instruments/rails_router.rb +12 -6
  41. data/lib/scout_apm/instruments/redis.rb +10 -6
  42. data/lib/scout_apm/instruments/samplers.rb +11 -0
  43. data/lib/scout_apm/instruments/sinatra.rb +5 -4
  44. data/lib/scout_apm/layaway.rb +26 -39
  45. data/lib/scout_apm/layaway_file.rb +8 -3
  46. data/lib/scout_apm/layer.rb +1 -1
  47. data/lib/scout_apm/layer_converters/converter_base.rb +4 -2
  48. data/lib/scout_apm/layer_converters/database_converter.rb +1 -1
  49. data/lib/scout_apm/layer_converters/histograms.rb +2 -2
  50. data/lib/scout_apm/layer_converters/slow_job_converter.rb +4 -3
  51. data/lib/scout_apm/layer_converters/slow_request_converter.rb +5 -4
  52. data/lib/scout_apm/logger.rb +143 -0
  53. data/lib/scout_apm/middleware.rb +7 -9
  54. data/lib/scout_apm/periodic_work.rb +28 -0
  55. data/lib/scout_apm/remote/server.rb +0 -2
  56. data/lib/scout_apm/reporter.rb +14 -8
  57. data/lib/scout_apm/reporting.rb +135 -0
  58. data/lib/scout_apm/request_manager.rb +4 -7
  59. data/lib/scout_apm/serializers/payload_serializer.rb +1 -1
  60. data/lib/scout_apm/slow_job_policy.rb +6 -2
  61. data/lib/scout_apm/slow_job_record.rb +5 -5
  62. data/lib/scout_apm/slow_request_policy.rb +6 -2
  63. data/lib/scout_apm/slow_transaction.rb +5 -5
  64. data/lib/scout_apm/store.rb +22 -16
  65. data/lib/scout_apm/synchronous_recorder.rb +7 -3
  66. data/lib/scout_apm/tasks/doctor.rb +75 -0
  67. data/lib/scout_apm/tasks/support.rb +22 -0
  68. data/lib/scout_apm/tracer.rb +5 -5
  69. data/lib/scout_apm/tracked_request.rb +23 -35
  70. data/lib/scout_apm/utils/backtrace_parser.rb +1 -1
  71. data/lib/scout_apm/utils/installed_gems.rb +7 -3
  72. data/lib/scout_apm/utils/klass_helper.rb +8 -2
  73. data/lib/scout_apm/utils/scm.rb +1 -1
  74. data/lib/scout_apm/utils/sql_sanitizer.rb +4 -6
  75. data/lib/scout_apm/version.rb +1 -1
  76. data/lib/tasks/doctor.rake +11 -0
  77. data/test/test_helper.rb +15 -2
  78. data/test/unit/agent_test.rb +1 -54
  79. data/test/unit/config_test.rb +9 -5
  80. data/test/unit/context_test.rb +4 -4
  81. data/test/unit/db_query_metric_set_test.rb +11 -4
  82. data/test/unit/fake_store_test.rb +1 -1
  83. data/test/unit/git_revision_test.rb +3 -3
  84. data/test/unit/instruments/net_http_test.rb +2 -1
  85. data/test/unit/instruments/percentile_sampler_test.rb +5 -9
  86. data/test/unit/layaway_test.rb +10 -5
  87. data/test/unit/layer_converters/metric_converter_test.rb +2 -2
  88. data/test/unit/slow_request_policy_test.rb +7 -3
  89. data/test/unit/sql_sanitizer_test.rb +0 -6
  90. data/test/unit/store_test.rb +11 -8
  91. metadata +15 -7
  92. data/lib/scout_apm/agent/logging.rb +0 -74
  93. data/lib/scout_apm/agent/reporting.rb +0 -129
  94. data/lib/scout_apm/utils/null_logger.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f02661ce9cf968ffd2fc3efdd96f81a82b49f05
4
- data.tar.gz: 2f47f1ddebab08773b82679dad0f81a29442c7f3
3
+ metadata.gz: cd45ad6c823f8163081d1a4083f1740823351551
4
+ data.tar.gz: 822bdbc9113c78762a31258b166fe71786bb3448
5
5
  SHA512:
6
- metadata.gz: 52a809924c15851078d2f8e3ec820021b6ec018f767d08240d1481975a18747e27442c2742bc72be51692d5c35cf9a0d47188be6d386832bbd8d4bdb473a34d4
7
- data.tar.gz: 9d4359d4285532afef1e043412459360430e7ec95392f9fa4a705e3b428d8bddcab953425d1cf23335a8cdd9e9ec39d1e4ce79ef1406208b6d62246b975d43d4
6
+ metadata.gz: 6c041d9e89c7dd58588755d805ff88dce9d55ddc38733143e92b32cb1cc8c2d53f577540f69dcc6af8aabb217903ef11c8dc8fe8981e191edc5239c42a1e9317
7
+ data.tar.gz: 4d70647935de5bcd27fbaa04f3b4a5e657cb01f13e7384ea17f57ee6bbcf5c73b30bfeb7c37c66bda2688069c4b7c5af61e29098a61c293ba06e1f2a3c1358aa
data/CHANGELOG.markdown CHANGED
@@ -1,26 +1,3 @@
1
- # 2.3.5
2
-
3
- * More robust recovery from stale layaway files
4
- * Quiet logging when hitting unusual layaway file limits
5
- * Better naming for Sidekiq delayed method jobs
6
- * Webrick is only required if actually needed
7
-
8
- # 2.3.4
9
-
10
- * Capture 300 characters of a url from net/http and httpclient instruments (up from 100).
11
-
12
- # 2.3.3
13
-
14
- * Capture ActiveRecord calls that generate more complex queries
15
- * More aggressively determine names of complex queries (to determine "User/find", "Account/create" and similar)
16
- * Increases the maximum size of SQL queries that are sanitized to 16KB from 4 KB
17
- * Captures all SQL individual queries generated in a given AR call (previous only a single query was captured)
18
-
19
- # 2.3.2
20
-
21
- * More robust startup sequence when using `rails server` vs. directly launching an app server
22
- * Avoid incompatibility with 3rd party gems that aggressively obtain database connections
23
-
24
1
  # 2.3.1
25
2
 
26
3
  * Fix DevTrace bug
data/lib/scout_apm.rb CHANGED
@@ -14,6 +14,7 @@ require 'socket'
14
14
  require 'thread'
15
15
  require 'time'
16
16
  require 'yaml'
17
+ require 'webrick'
17
18
 
18
19
  #####################################
19
20
  # Gem Requires
@@ -79,16 +80,18 @@ require 'scout_apm/instruments/elasticsearch'
79
80
  require 'scout_apm/instruments/active_record'
80
81
  require 'scout_apm/instruments/action_controller_rails_2'
81
82
  require 'scout_apm/instruments/action_controller_rails_3_rails4'
83
+ require 'scout_apm/instruments/action_view'
82
84
  require 'scout_apm/instruments/middleware_summary'
83
85
  require 'scout_apm/instruments/middleware_detailed' # Disabled by default, see the file for more details
84
86
  require 'scout_apm/instruments/rails_router'
85
87
  require 'scout_apm/instruments/grape'
86
88
  require 'scout_apm/instruments/sinatra'
89
+ require 'allocations'
90
+
87
91
  require 'scout_apm/instruments/process/process_cpu'
88
92
  require 'scout_apm/instruments/process/process_memory'
89
93
  require 'scout_apm/instruments/percentile_sampler'
90
- require 'scout_apm/instruments/action_view'
91
- require 'allocations'
94
+ require 'scout_apm/instruments/samplers'
92
95
 
93
96
  require 'scout_apm/app_server_load'
94
97
 
@@ -97,7 +100,6 @@ require 'scout_apm/utils/active_record_metric_name'
97
100
  require 'scout_apm/utils/backtrace_parser'
98
101
  require 'scout_apm/utils/installed_gems'
99
102
  require 'scout_apm/utils/klass_helper'
100
- require 'scout_apm/utils/null_logger'
101
103
  require 'scout_apm/utils/scm'
102
104
  require 'scout_apm/utils/sql_sanitizer'
103
105
  require 'scout_apm/utils/time'
@@ -108,8 +110,8 @@ require 'scout_apm/utils/gzip_helper'
108
110
  require 'scout_apm/config'
109
111
  require 'scout_apm/environment'
110
112
  require 'scout_apm/agent'
111
- require 'scout_apm/agent/logging'
112
- require 'scout_apm/agent/reporting'
113
+ require 'scout_apm/logger'
114
+ require 'scout_apm/reporting'
113
115
  require 'scout_apm/layaway'
114
116
  require 'scout_apm/layaway_file'
115
117
  require 'scout_apm/reporter'
@@ -162,6 +164,14 @@ require 'scout_apm/remote/message'
162
164
  require 'scout_apm/remote/recorder'
163
165
  require 'scout_apm/instruments/resque'
164
166
 
167
+ require 'scout_apm/agent_context'
168
+ require 'scout_apm/instrument_manager'
169
+ require 'scout_apm/periodic_work'
170
+ require 'scout_apm/agent/preconditions'
171
+ require 'scout_apm/agent/exit_handler'
172
+ require 'scout_apm/tasks/doctor'
173
+ require 'scout_apm/tasks/support'
174
+
165
175
  if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
166
176
  module ScoutApm
167
177
  class Railtie < Rails::Railtie
@@ -172,17 +182,18 @@ if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR
172
182
  app.middleware.use ScoutApm::Middleware
173
183
 
174
184
  # Attempt to start right away, this will work best for preloading apps, Unicorn & Puma & similar
175
- ScoutApm::Agent.instance.start
176
- end
177
- end
178
- class Railtie < Rails::Railtie
179
- initializer 'scout_apm.start' do |app|
185
+ ScoutApm::Agent.instance.install
186
+
180
187
  # Install the middleware every time in development mode.
181
188
  # The middleware is a noop if dev_trace is not enabled in config
182
189
  if Rails.env.development?
183
190
  app.middleware.use ScoutApm::Instant::Middleware
184
191
  end
185
192
  end
193
+
194
+ rake_tasks do
195
+ load "tasks/doctor.rake"
196
+ end
186
197
  end
187
198
  end
188
199
  else
@@ -1,244 +1,110 @@
1
1
  module ScoutApm
2
- # The agent gathers performance data from a Ruby application. One Agent instance is created per-Ruby process.
2
+ # The entry-point for the ScoutApm Agent.
3
3
  #
4
- # Each Agent object creates a worker thread (unless monitoring is disabled or we're forking).
5
- # The worker thread wakes up every +Agent#period+, merges in-memory metrics w/those saved to disk,
6
- # saves tshe merged data to disk, and sends it to the Scout server.
4
+ # Only one Agent instance is created per-Ruby process, and it coordinates the lifecycle of the monitoring.
5
+ # - initializes various data stores
6
+ # - coordinates configuration & logging
7
+ # - starts background threads, running periodically
8
+ # - installs shutdown hooks
7
9
  class Agent
8
10
  # see self.instance
9
11
  @@instance = nil
10
12
 
11
- # Accessors below are for associated classes
12
- attr_accessor :store
13
- attr_reader :recorder
14
- attr_accessor :layaway
15
- attr_accessor :config
16
- attr_accessor :logger
17
- attr_accessor :log_file # path to the log file
18
- attr_accessor :options # options passed to the agent when +#start+ is called.
19
- attr_accessor :metric_lookup # Hash used to lookup metric ids based on their name and scope
20
- attr_reader :slow_request_policy
21
- attr_reader :slow_job_policy
22
- attr_reader :process_start_time # used when creating slow transactions to report how far from startup the transaction was recorded.
23
- attr_reader :ignored_uris
13
+ attr_reader :context
24
14
 
25
- # Histogram of the cumulative requests since the start of the process
26
- attr_reader :request_histograms
15
+ attr_accessor :options # options passed to the agent when +#start+ is called.
27
16
 
28
- # Histogram of the requests, distinct by reporting period (minute)
29
- # { StoreReportingPeriodTimestamp => RequestHistograms }
30
- attr_reader :request_histograms_by_time
17
+ attr_reader :instrument_manager
31
18
 
32
19
  # All access to the agent is thru this class method to ensure multiple Agent instances are not initialized per-Ruby process.
33
20
  def self.instance(options = {})
34
21
  @@instance ||= self.new(options)
35
22
  end
36
23
 
37
- # Note - this doesn't start instruments or the worker thread. This is handled via +#start+ as we don't
38
- # want to start the worker thread or install instrumentation if (1) disabled for this environment (2) a worker thread shouldn't
39
- # be started (when forking).
24
+ # First call of the agent. Does very little so that the object can be created, and exist.
40
25
  def initialize(options = {})
41
- @started = false
42
- @shutting_down = false
43
- @process_start_time = Time.now
44
- @options ||= options
45
-
46
- # until the agent is started, there's no recorder
47
- @recorder = nil
48
-
49
- # Start up without attempting to load a configuration file. We need to be
50
- # able to lookup configuration options like "application_root" which would
51
- # then in turn influence where the configuration file came from.
52
- #
53
- # Later in initialization, we reset @config to include the file.
54
- @config = ScoutApm::Config.without_file
55
-
56
- @slow_request_policy = ScoutApm::SlowRequestPolicy.new
57
- @slow_job_policy = ScoutApm::SlowJobPolicy.new
58
- @request_histograms = ScoutApm::RequestHistograms.new
59
- @request_histograms_by_time = Hash.new { |h, k| h[k] = ScoutApm::RequestHistograms.new }
60
-
61
- @store = ScoutApm::Store.new
62
-
63
- @layaway = ScoutApm::Layaway.new(config, environment)
64
- @metric_lookup = Hash.new
65
-
66
- @installed_instruments = []
26
+ @options = options
27
+ @context = ScoutApm::AgentContext.new
67
28
  end
68
29
 
69
- def environment
70
- ScoutApm::Environment.instance
30
+ def logger
31
+ context.logger
71
32
  end
72
33
 
73
- def apm_enabled?
74
- config.value('monitor')
75
- end
34
+ # Finishes setting up the instrumentation, configuration, and attempts to start the agent.
35
+ def install(force=false)
36
+ context.config = ScoutApm::Config.with_file(context, context.config.value("config_file"))
76
37
 
77
- # If true, the agent will start regardless of safety checks. Currently just used for testing.
78
- def force?
79
- @options[:force]
80
- end
38
+ context.logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized"
81
39
 
82
- def preconditions_met?(options={})
83
- if !apm_enabled?
84
- logger.warn "Monitoring isn't enabled for the [#{environment.env}] environment. #{force? ? 'Forcing agent to start' : 'Not starting agent'}"
85
- return false unless force?
86
- end
40
+ @instrument_manager = ScoutApm::InstrumentManager.new(context)
41
+ @instrument_manager.install! if should_load_instruments? || force
87
42
 
88
- if !environment.application_name
89
- logger.warn "An application name could not be determined. Specify the :name value in scout_apm.yml. #{force? ? 'Forcing agent to start' : 'Not starting agent'}."
90
- return false unless force?
91
- end
43
+ install_background_job_integrations
44
+ install_app_server_integration
92
45
 
93
- if environment.interactive?
94
- logger.warn "Agent attempting to load in interactive mode. #{force? ? 'Forcing agent to start' : 'Not starting agent'}"
95
- return false unless force?
96
- end
46
+ # XXX: Should this happen at application start?
47
+ # Should this ever happen after fork?
48
+ # We start a thread in this, which can screw stuff up when we then fork.
49
+ AppServerLoad.new(context).run
97
50
 
98
- if app_server_missing?(options) && background_job_missing?
99
- if force?
100
- logger.warn "Agent starting (forced)"
101
- else
102
- logger.warn "Deferring agent start. Standing by for first request"
103
- end
104
- return false unless force?
105
- end
51
+ logger.info "Scout Agent [#{ScoutApm::VERSION}] installed"
106
52
 
107
- if started?
108
- logger.warn "Already started agent."
109
- return false
110
- end
53
+ context.installed!
111
54
 
112
- if defined?(::ScoutRails)
113
- logger.warn "ScoutAPM is incompatible with the old Scout Rails plugin. Please remove scout_rails from your Gemfile"
114
- return false unless force?
55
+ if ScoutApm::Agent::Preconditions.check?(context) || force
56
+ start
115
57
  end
116
-
117
- true
118
58
  end
119
59
 
120
- # This is called via +ScoutApm::Agent.instance.start+ when ScoutApm is required in a Ruby application.
121
- # It initializes the agent and starts the worker thread (if appropiate).
122
- def start(options = {})
123
- @options.merge!(options)
60
+ # Unconditionally starts the agent. This includes verifying instruments are
61
+ # installed, and starting the background worker.
62
+ #
63
+ # Does not attempt to start twice.
64
+ def start
65
+ return if context.started?
66
+ install unless context.installed?
124
67
 
125
- @config = ScoutApm::Config.with_file(@config.value("config_file"))
126
- layaway.config = config
68
+ context.started!
127
69
 
128
- init_logger
129
- logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
130
-
131
- @recorder = create_recorder
132
-
133
- @config.log_settings
134
-
135
- @ignored_uris = ScoutApm::IgnoredUris.new(config.value('ignore'))
136
-
137
- load_instruments if should_load_instruments?(options)
138
-
139
- if !@config.any_keys_found?
140
- logger.info("No configuration file loaded, and no configuration found in ENV. " +
141
- "For assistance configuring Scout, visit " +
142
- "http://help.apm.scoutapp.com/#configuration-options")
143
- end
70
+ log_environment
144
71
 
145
- return false unless preconditions_met?(options)
146
- @started = true
147
- logger.info "Starting monitoring for [#{environment.application_name}]. Framework [#{environment.framework}] App Server [#{environment.app_server}] Background Job Framework [#{environment.background_job_name}]."
148
-
149
- [ ScoutApm::Instruments::Process::ProcessCpu.new(environment.processors, logger),
150
- ScoutApm::Instruments::Process::ProcessMemory.new(logger),
151
- ScoutApm::Instruments::PercentileSampler.new(logger, request_histograms_by_time),
152
- ].each { |s| store.add_sampler(s) }
153
-
154
- app_server_load_hook
155
-
156
- if environment.background_job_integration
157
- environment.background_job_integration.install
158
- logger.info "Installed Background Job Integration [#{environment.background_job_name}]"
159
- end
160
-
161
- # start_background_worker? is true on non-forking servers, and directly
162
- # starts the background worker. On forking servers, a server-specific
163
- # hook is inserted to start the background worker after forking.
164
- if start_background_worker?
165
- start_background_worker
166
- logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized"
167
- else
168
- environment.app_server_integration.install
169
- logger.info "Scout Agent [#{ScoutApm::VERSION}] loaded in [#{environment.app_server}] master process. Monitoring will start after server forks its workers."
170
- end
72
+ start_background_worker
171
73
  end
172
74
 
173
- # Sends a ping to APM right away, smoothes out onboarding
174
- # Collects up any relevant info (framework, app server, system time, ruby version, etc)
175
- def app_server_load_hook
176
- @app_server_load ||= AppServerLoad.new.run
177
- end
75
+ def log_environment
76
+ bg_names = context.environment.background_job_integrations.map{|bg| bg.name }.join(", ")
178
77
 
179
- def exit_handler_supported?
180
- if environment.sinatra?
181
- logger.debug "Exit handler not supported for Sinatra"
182
- false
183
- elsif environment.jruby?
184
- logger.debug "Exit handler not supported for JRuby"
185
- false
186
- elsif environment.rubinius?
187
- logger.debug "Exit handler not supported for Rubinius"
188
- false
189
- else
190
- true
191
- end
78
+ logger.info(
79
+ "Scout Agent [#{ScoutApm::VERSION}] starting for [#{context.environment.application_name}] " +
80
+ "Framework [#{context.environment.framework}] " +
81
+ "App Server [#{context.environment.app_server}] " +
82
+ "Background Job Framework [#{bg_names}] " +
83
+ "Hostname [#{context.environment.hostname}]"
84
+ )
192
85
  end
193
86
 
194
- # at_exit, calls Agent#shutdown to wrapup metric reporting.
195
- def install_exit_handler
196
- logger.debug "Shutdown handler not supported" and return unless exit_handler_supported?
197
- logger.debug "Installing Shutdown Handler"
198
-
199
- at_exit do
200
- logger.info "Shutting down Scout Agent"
201
- # MRI 1.9 bug drops exit codes.
202
- # http://bugs.ruby-lang.org/issues/5218
203
- if environment.ruby_19?
204
- status = $!.status if $!.is_a?(SystemExit)
205
- shutdown
206
- exit status if status
207
- else
208
- shutdown
209
- end
87
+ # Attempts to install all background job integrations. This can come up if
88
+ # an app has both Resque and Sidekiq - we want both to be installed if
89
+ # possible, it's no harm to have the "wrong" one also installed while running.
90
+ def install_background_job_integrations
91
+ context.environment.background_job_integrations.each do |int|
92
+ int.install
93
+ logger.info "Installed Background Job Integration [#{int.name}]"
210
94
  end
211
95
  end
212
96
 
213
- # Called via an at_exit handler, it:
214
- # (1) Stops the background worker
215
- # (2) Stores metrics locally (forcing current-minute metrics to be written)
216
- # It does not attempt to actually report metrics.
217
- def shutdown
218
- logger.info "Shutting down ScoutApm"
219
-
220
- return if !started?
221
-
222
- return if @shutdown
223
- @shutdown = true
224
-
225
- if @background_worker
226
- logger.info("Stopping background worker")
227
- @background_worker.stop
228
- store.write_to_layaway(layaway, :force)
229
- if @background_worker_thread.alive?
230
- @background_worker_thread.wakeup
231
- @background_worker_thread.join
232
- end
233
- end
234
- end
235
-
236
- def started?
237
- @started
97
+ # This sets up the background worker thread to run at the correct time,
98
+ # either immediately, or after a fork into the actual unicorn/puma/etc
99
+ # worker
100
+ def install_app_server_integration
101
+ context.environment.app_server_integration.install
102
+ logger.info "Installed Application Server Integration [#{context.environment.app_server}]."
238
103
  end
239
104
 
240
- def shutting_down?
241
- @shutdown == true
105
+ # If true, the agent will start regardless of safety checks.
106
+ def force?
107
+ @options[:force]
242
108
  end
243
109
 
244
110
  # The worker thread will automatically start UNLESS:
@@ -246,167 +112,63 @@ module ScoutApm
246
112
  # * A supported application server is detected, but it forks. In this case,
247
113
  # the agent is started in the forked process.
248
114
  def start_background_worker?
249
- return true if environment.app_server == :thin
250
- return true if environment.app_server == :webrick
251
115
  return true if force?
252
- return !environment.forking?
116
+ return !context.environment.forking?
253
117
  end
254
118
 
255
- def background_worker_running?
256
- @background_worker_thread &&
257
- @background_worker_thread.alive? &&
258
- @background_worker &&
259
- @background_worker.running?
119
+ def should_load_instruments?
120
+ return true if context.config.value('dev_trace')
121
+ # XXX: If monitor is true, we want to install, right?
122
+ # return false if context.config.value('monitor')
123
+ context.environment.app_server_integration.found? || context.environment.background_job_integration
260
124
  end
261
125
 
262
- # Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for +Agent#period+ and when it wakes,
263
- # processes data, either saving it to disk or reporting to Scout.
264
- # => true if thread & worker got started
265
- # => false if it wasn't started (either due to already running, or other preconditions)
266
- def start_background_worker(quiet=false)
267
- if !apm_enabled?
268
- logger.debug "Not starting background worker as monitoring isn't enabled." unless quiet
126
+ #################################
127
+ # Background Worker Lifecycle #
128
+ #################################
129
+
130
+ def start_background_worker
131
+ if !context.config.value('monitor')
132
+ logger.debug "Not starting background worker as monitoring isn't enabled."
269
133
  return false
270
134
  end
135
+
271
136
  if background_worker_running?
272
- logger.info "Not starting background worker, already started" unless quiet
273
- return false
274
- end
275
- if shutting_down?
276
- logger.info "Not starting background worker, already in process of shutting down" unless quiet
277
- return false
137
+ logger.info "Not starting background worker, already started"
138
+ return
278
139
  end
279
140
 
280
141
  logger.info "Initializing worker thread."
281
142
 
282
- install_exit_handler
143
+ ScoutApm::Agent::ExitHandler.new(context).install
283
144
 
284
- @recorder = create_recorder
285
- logger.info("Recorder is now: #{@recorder.class}")
145
+ periodic_work = ScoutApm::PeriodicWork.new(context)
286
146
 
287
- @background_worker = ScoutApm::BackgroundWorker.new
147
+ @background_worker = ScoutApm::BackgroundWorker.new(context)
288
148
  @background_worker_thread = Thread.new do
289
149
  @background_worker.start {
290
- ScoutApm::Debug.instance.call_periodic_hooks
291
- ScoutApm::Agent.instance.process_metrics
292
- clean_old_percentiles
150
+ periodic_work.run
293
151
  }
294
152
  end
295
-
296
- return true
297
- end
298
-
299
- def clean_old_percentiles
300
- request_histograms_by_time.
301
- keys.
302
- select {|timestamp| timestamp.age_in_seconds > 60 * 10 }.
303
- each {|old_timestamp| request_histograms_by_time.delete(old_timestamp) }
304
- end
305
-
306
- # If we want to skip the app_server_check, then we must load it.
307
- def should_load_instruments?(options={})
308
- return true if options[:skip_app_server_check]
309
- return true if config.value('dev_trace')
310
- return false if !apm_enabled?
311
- environment.app_server_integration.found? || !background_job_missing?
312
153
  end
313
154
 
314
- # Loads the instrumention logic.
315
- def load_instruments
316
- case environment.framework
317
- when :rails then
318
- install_instrument(ScoutApm::Instruments::ActionControllerRails2)
319
- when :rails3_or_4 then
320
- install_instrument(ScoutApm::Instruments::ActionControllerRails3Rails4)
321
- install_instrument(ScoutApm::Instruments::RailsRouter)
322
-
323
- if config.value("detailed_middleware")
324
- install_instrument(ScoutApm::Instruments::MiddlewareDetailed)
325
- else
326
- install_instrument(ScoutApm::Instruments::MiddlewareSummary)
155
+ def stop_background_worker
156
+ if @background_worker
157
+ logger.info("Stopping background worker")
158
+ @background_worker.stop
159
+ context.store.write_to_layaway(context.layaway, :force)
160
+ if @background_worker_thread.alive?
161
+ @background_worker_thread.wakeup
162
+ @background_worker_thread.join
327
163
  end
328
164
  end
329
-
330
- install_instrument(ScoutApm::Instruments::ActionView)
331
- install_instrument(ScoutApm::Instruments::ActiveRecord)
332
- install_instrument(ScoutApm::Instruments::Moped)
333
- install_instrument(ScoutApm::Instruments::Mongoid)
334
- install_instrument(ScoutApm::Instruments::NetHttp)
335
- install_instrument(ScoutApm::Instruments::HttpClient)
336
- install_instrument(ScoutApm::Instruments::Redis)
337
- install_instrument(ScoutApm::Instruments::InfluxDB)
338
- install_instrument(ScoutApm::Instruments::Elasticsearch)
339
- install_instrument(ScoutApm::Instruments::Grape)
340
- rescue
341
- logger.warn "Exception loading instruments:"
342
- logger.warn $!.message
343
- logger.warn $!.backtrace
344
165
  end
345
166
 
346
- def install_instrument(instrument_klass)
347
- # Don't attempt to install the same instrument twice
348
- return if @installed_instruments.any? { |already_installed_instrument| instrument_klass === already_installed_instrument }
349
-
350
- # Allow users to skip individual instruments via the config file
351
- instrument_short_name = instrument_klass.name.split("::").last
352
- if (config.value("disabled_instruments") || []).include?(instrument_short_name)
353
- logger.info "Skipping Disabled Instrument: #{instrument_short_name} - To re-enable, change `disabled_instruments` key in scout_apm.yml"
354
- return
355
- end
356
-
357
- instance = instrument_klass.new
358
- @installed_instruments << instance
359
- instance.install
360
- end
361
-
362
- def app_server_missing?(options = {})
363
- !environment.app_server_integration(true).found? && !options[:skip_app_server_check]
364
- end
365
-
366
- def background_job_missing?(options = {})
367
- environment.background_job_integration.nil? && !options[:skip_background_job_check]
368
- end
369
-
370
- def clear_recorder
371
- @recorder = nil
372
- end
373
-
374
- def create_recorder
375
- if @recorder
376
- return @recorder
377
- end
378
-
379
- if config.value("async_recording")
380
- logger.debug("Using asynchronous recording")
381
- ScoutApm::BackgroundRecorder.new(logger).start
382
- else
383
- logger.debug("Using synchronous recording")
384
- ScoutApm::SynchronousRecorder.new(logger).start
385
- end
386
- end
387
-
388
- def start_remote_server(bind, port)
389
- return if @remote_server && @remote_server.running?
390
-
391
- logger.info("Starting Remote Agent Server")
392
-
393
- # Start the listening web server only in parent process.
394
- @remote_server = ScoutApm::Remote::Server.new(
395
- bind,
396
- port,
397
- ScoutApm::Remote::Router.new(ScoutApm::SynchronousRecorder.new(logger), logger),
398
- logger
399
- )
400
-
401
- @remote_server.start
402
- end
403
-
404
- # Execute this in the child process of a remote agent. The parent is
405
- # expected to have its accepting webserver up and running
406
- def use_remote_recorder(host, port)
407
- logger.debug("Becoming Remote Agent (reporting to: #{host}:#{port})")
408
- @recorder = ScoutApm::Remote::Recorder.new(host, port, logger)
409
- @store = ScoutApm::FakeStore.new
167
+ def background_worker_running?
168
+ @background_worker_thread &&
169
+ @background_worker_thread.alive? &&
170
+ @background_worker &&
171
+ @background_worker.running?
410
172
  end
411
173
  end
412
174
  end