scout_apm 2.1.20 → 2.1.21
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.
- data/CHANGELOG.markdown +6 -0
- data/lib/scout_apm.rb +1 -0
- data/lib/scout_apm/agent.rb +1 -0
- data/lib/scout_apm/agent/reporting.rb +1 -1
- data/lib/scout_apm/debug.rb +37 -0
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +181 -57
- data/lib/scout_apm/version.rb +1 -1
- metadata +3 -2
data/CHANGELOG.markdown
CHANGED
data/lib/scout_apm.rb
CHANGED
data/lib/scout_apm/agent.rb
CHANGED
@@ -259,6 +259,7 @@ module ScoutApm
|
|
259
259
|
@background_worker = ScoutApm::BackgroundWorker.new
|
260
260
|
@background_worker_thread = Thread.new do
|
261
261
|
@background_worker.start {
|
262
|
+
ScoutApm::Debug.instance.call_periodic_hooks
|
262
263
|
ScoutApm::Agent.instance.process_metrics
|
263
264
|
clean_old_percentiles
|
264
265
|
}
|
@@ -72,7 +72,7 @@ module ScoutApm
|
|
72
72
|
log_deliver(metrics, slow_transactions, metadata, slow_jobs, histograms)
|
73
73
|
|
74
74
|
payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms)
|
75
|
-
|
75
|
+
logger.debug("Sending payload w/ Headers: #{headers.inspect}")
|
76
76
|
|
77
77
|
reporter.report(payload, headers)
|
78
78
|
rescue => e
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
class Debug
|
3
|
+
# see self.instance
|
4
|
+
@@instance = nil
|
5
|
+
|
6
|
+
def self.instance
|
7
|
+
@@instance ||= new
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_periodic_hook(&hook)
|
11
|
+
@periodic_hooks << hook
|
12
|
+
end
|
13
|
+
|
14
|
+
def call_periodic_hooks
|
15
|
+
@periodic_hooks.each do |hook|
|
16
|
+
begin
|
17
|
+
hook.call
|
18
|
+
rescue => e
|
19
|
+
logger.info("Periodic debug hook failed to run: #{e}\n\t#{e.backtrace.join("\n\t")}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue
|
23
|
+
# Something went super wrong for the inner rescue to not catch this. Just
|
24
|
+
# swallow the error. The debug tool should never crash the app.
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@periodic_hooks = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def logger
|
34
|
+
ScoutApm::Agent.instance.logger
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
(function(){var open=window.XMLHttpRequest.prototype.open;var send=window.XMLHttpRequest.prototype.send;function openReplacement(method,url,async,user,password){this._url=url;return open.apply(this,arguments);}
|
4
4
|
function sendReplacement(data){if(this.onload){this._onload=this.onload;}
|
5
5
|
this.onload=onLoadReplacement;return send.apply(this,arguments);}
|
6
|
-
function onLoadReplacement(){if(this._url.startsWith(window.location.protocol+"//"+window.location.host)||!this._url.startsWith("http")){try{traceText=this.getResponseHeader("X-scoutapminstant");if(traceText){setTimeout(function(){window.scoutInstant("addTrace",traceText)},0);}}catch(e){console.debug("Problem getting X-scoutapminstant header");}}
|
6
|
+
function onLoadReplacement(){if(this._url.startsWith(window.location.protocol+"//"+window.location.host)||!this._url.startsWith("http")){try{var traceText=this.getResponseHeader("X-scoutapminstant");if(traceText){setTimeout(function(){window.scoutInstant("addTrace",traceText)},0);}}catch(e){console.debug("Problem getting X-scoutapminstant header");}}
|
7
7
|
if(this._onload){return this._onload.apply(this,arguments);}}
|
8
8
|
window.XMLHttpRequest.prototype.open=openReplacement;window.XMLHttpRequest.prototype.send=sendReplacement;})();
|
9
|
-
</script>
|
9
|
+
</script>
|
@@ -5,6 +5,11 @@ module ScoutApm
|
|
5
5
|
class Page
|
6
6
|
def initialize(html)
|
7
7
|
@html = html
|
8
|
+
|
9
|
+
if html.is_a?(Array)
|
10
|
+
@html = html.inject("") { |memo, str| memo + str }
|
11
|
+
end
|
12
|
+
|
8
13
|
@to_add_to_head = []
|
9
14
|
@to_add_to_body = []
|
10
15
|
end
|
@@ -45,66 +50,185 @@ module ScoutApm
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def call(env)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
page = ScoutApm::Instant::Page.new(response.body)
|
79
|
-
page.add_to_head(ScoutApm::Instant::Util.read_asset("xmlhttp_instrumentation.html")) # This monkey-patches XMLHttpRequest. It could possibly be part of the main scout_instant.js too. Putting it here so it runs as soon as possible.
|
80
|
-
page.add_to_head("<link href='#{apm_host}/instant/scout_instant.css?cachebust=#{Time.now.to_i}' media='all' rel='stylesheet' />")
|
81
|
-
page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
|
82
|
-
page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")
|
83
|
-
|
84
|
-
if response.is_a?(ActionDispatch::Response)
|
85
|
-
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
|
86
|
-
# preserve the ActionDispatch::Response when applicable
|
87
|
-
response.body=[page.res]
|
88
|
-
[status, headers, response]
|
89
|
-
else
|
90
|
-
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page but not an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
|
91
|
-
# otherwise, just return an array
|
92
|
-
[status, headers, [page.res]]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
else
|
96
|
-
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
|
97
|
-
[status, headers, response]
|
98
|
-
end
|
99
|
-
else
|
100
|
-
# don't log anything here - this is the path for all assets served in development, and the log would get noisy
|
101
|
-
[status, headers, response]
|
102
|
-
end
|
53
|
+
rack_response = @app.call(env)
|
54
|
+
begin
|
55
|
+
DevTraceResponseManipulator.new(env, rack_response).call
|
56
|
+
rescue => e
|
57
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: Raised an exception: #{e.message}, #{e.backtrace}")
|
58
|
+
rack_response
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class DevTraceResponseManipulator
|
64
|
+
attr_reader :rack_response
|
65
|
+
attr_reader :rack_status, :rack_headers, :rack_body
|
66
|
+
attr_reader :env
|
67
|
+
|
68
|
+
def initialize(env, rack_response)
|
69
|
+
@env = env
|
70
|
+
@rack_response = rack_response
|
71
|
+
|
72
|
+
@rack_status = rack_response[0]
|
73
|
+
@rack_headers = rack_response[1]
|
74
|
+
@rack_body = rack_response[2]
|
75
|
+
end
|
76
|
+
|
77
|
+
def call
|
78
|
+
return rack_response unless preconditions_met?
|
79
|
+
|
80
|
+
if ajax_request?
|
81
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This is either AJAX or JSON. Path=#{path}; ContentType=#{content_type}")
|
82
|
+
adjust_ajax_header
|
103
83
|
else
|
104
|
-
|
105
|
-
|
84
|
+
adjust_html_response
|
85
|
+
end
|
86
|
+
|
87
|
+
rebuild_rack_response
|
88
|
+
end
|
89
|
+
|
90
|
+
###########################
|
91
|
+
# Precondition checking #
|
92
|
+
###########################
|
93
|
+
|
94
|
+
def preconditions_met?
|
95
|
+
if dev_trace_disabled?
|
96
|
+
logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
|
97
|
+
return false
|
106
98
|
end
|
99
|
+
|
100
|
+
# Don't attempt to instrument assets.
|
101
|
+
# Don't log this case, since it would be very noisy
|
102
|
+
logger.debug("DevTrace: dev asset ignored") and return false if development_asset?
|
103
|
+
|
104
|
+
# If we didn't have a tracked_request object, or we explicitly ignored
|
105
|
+
# this request, don't do any work.
|
106
|
+
logger.debug("DevTrace: no tracked request") and return false if tracked_request.nil? || tracked_request.ignoring_request?
|
107
|
+
|
108
|
+
# If we didn't get a trace, we can't show a trace...
|
109
|
+
if trace.nil?
|
110
|
+
logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
def dev_trace_disabled?
|
118
|
+
! ScoutApm::Agent.instance.config.value('dev_trace')
|
119
|
+
end
|
120
|
+
|
121
|
+
########################
|
122
|
+
# Response Injection #
|
123
|
+
########################
|
124
|
+
|
125
|
+
def rebuild_rack_response
|
126
|
+
[rack_status, rack_headers, rack_body]
|
127
|
+
end
|
128
|
+
|
129
|
+
def adjust_ajax_header
|
130
|
+
rack_headers['X-scoutapminstant'] = payload
|
131
|
+
end
|
132
|
+
|
133
|
+
def adjust_html_response
|
134
|
+
case true
|
135
|
+
when rails_response? then adjust_rails_response
|
136
|
+
when rack_proxy_response? then adjust_rack_proxy_response
|
137
|
+
else
|
138
|
+
# No action taken, we only adjust if we know exactly what we have.
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def rails_response?
|
143
|
+
rack_body.is_a?(ActionDispatch::Response)
|
144
|
+
end
|
145
|
+
|
146
|
+
def rack_proxy_response?
|
147
|
+
rack_body.is_a?(Rack::BodyProxy)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Preserve the ActionDispatch::Response object we're working with
|
151
|
+
def adjust_rails_response
|
152
|
+
logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
|
153
|
+
rack_body.body = [ html_manipulator.res ]
|
154
|
+
end
|
155
|
+
|
156
|
+
def adjust_rack_proxy_response
|
157
|
+
logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an Rack::BodyProxy. Path=#{path}; ContentType=#{content_type}")
|
158
|
+
@rack_body = [ html_manipulator.res ]
|
159
|
+
@rack_headers.delete("Content-Length")
|
160
|
+
end
|
161
|
+
|
162
|
+
def html_manipulator
|
163
|
+
@html_manipulator ||=
|
164
|
+
begin
|
165
|
+
page = ScoutApm::Instant::Page.new(rack_body.body)
|
166
|
+
|
167
|
+
# This monkey-patches XMLHttpRequest. It could possibly be part of the main scout_instant.js too. Putting it here so it runs as soon as possible.
|
168
|
+
page.add_to_head(ScoutApm::Instant::Util.read_asset("xmlhttp_instrumentation.html"))
|
169
|
+
|
170
|
+
# Add a link to CSS, then JS
|
171
|
+
page.add_to_head("<link href='#{apm_host}/instant/scout_instant.css?cachebust=#{Time.now.to_i}' media='all' rel='stylesheet' />")
|
172
|
+
page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
|
173
|
+
page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")
|
174
|
+
|
175
|
+
page
|
176
|
+
end
|
107
177
|
end
|
178
|
+
|
179
|
+
def ajax_request?
|
180
|
+
env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
|
181
|
+
end
|
182
|
+
|
183
|
+
def development_asset?
|
184
|
+
!rack_body.respond_to?(:body)
|
185
|
+
end
|
186
|
+
|
187
|
+
def path
|
188
|
+
env['PATH_INFO']
|
189
|
+
end
|
190
|
+
|
191
|
+
def content_type
|
192
|
+
rack_headers['Content-Type']
|
193
|
+
end
|
194
|
+
|
195
|
+
##############################
|
196
|
+
# APM Helpers & Shorthands #
|
197
|
+
##############################
|
198
|
+
|
199
|
+
def logger
|
200
|
+
ScoutApm::Agent.instance.logger
|
201
|
+
end
|
202
|
+
|
203
|
+
def tracked_request
|
204
|
+
@tracked_request ||= ScoutApm::RequestManager.lookup
|
205
|
+
end
|
206
|
+
|
207
|
+
def apm_host
|
208
|
+
ScoutApm::Agent.instance.config.value("direct_host")
|
209
|
+
end
|
210
|
+
|
211
|
+
def trace
|
212
|
+
@trace ||= LayerConverters::SlowRequestConverter.new(tracked_request).call
|
213
|
+
end
|
214
|
+
|
215
|
+
def payload
|
216
|
+
@payload ||=
|
217
|
+
begin
|
218
|
+
metadata = {
|
219
|
+
:app_root => ScoutApm::Environment.instance.root.to_s,
|
220
|
+
:unique_id => env['action_dispatch.request_id'], # note, this is a different unique_id than what "normal" payloads use
|
221
|
+
:agent_version => ScoutApm::VERSION,
|
222
|
+
:platform => "ruby",
|
223
|
+
}
|
224
|
+
|
225
|
+
hash = ScoutApm::Serializers::PayloadSerializerToJson.
|
226
|
+
rearrange_slow_transaction(trace).
|
227
|
+
merge!(:metadata => metadata)
|
228
|
+
ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
108
232
|
end
|
109
233
|
end
|
110
234
|
end
|
data/lib/scout_apm/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.21
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-01-
|
13
|
+
date: 2017-01-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: minitest
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- lib/scout_apm/call_set.rb
|
160
160
|
- lib/scout_apm/config.rb
|
161
161
|
- lib/scout_apm/context.rb
|
162
|
+
- lib/scout_apm/debug.rb
|
162
163
|
- lib/scout_apm/environment.rb
|
163
164
|
- lib/scout_apm/fake_store.rb
|
164
165
|
- lib/scout_apm/framework_integrations/rails_2.rb
|