scout_apm 2.3.5 → 2.4.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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