oneapm_rpm 1.1.3 → 1.2.0
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.
- checksums.yaml +4 -4
- data/lib/one_apm/agent.rb +4 -4
- data/lib/one_apm/agent/agent/connect.rb +3 -49
- data/lib/one_apm/agent/agent/container_data_manager.rb +2 -10
- data/lib/one_apm/agent/agent/forkable_dispatcher_functions.rb +4 -2
- data/lib/one_apm/agent/agent/helpers.rb +2 -0
- data/lib/one_apm/agent/agent/start.rb +3 -3
- data/lib/one_apm/agent/agent/start_worker_thread.rb +2 -0
- data/lib/one_apm/agent/database/active_record_helper.rb +1 -0
- data/lib/one_apm/agent/{datastores.rb → datastore.rb} +2 -2
- data/lib/one_apm/agent/{datastores → datastore}/metric_helper.rb +1 -1
- data/lib/one_apm/agent/{datastores → datastore}/mongo.rb +1 -1
- data/lib/one_apm/agent/{datastores → datastore}/mongo/metric_translator.rb +4 -4
- data/lib/one_apm/agent/{datastores → datastore}/mongo/obfuscator.rb +1 -1
- data/lib/one_apm/agent/{datastores → datastore}/mongo/statement_formatter.rb +2 -2
- data/lib/one_apm/agent/threading/agent_thread.rb +50 -50
- data/lib/one_apm/agent/threading/thread_profile.rb +1 -4
- data/lib/one_apm/collector/commands/thread_profiler_session.rb +1 -1
- data/lib/one_apm/collector/containers/agent_command_router.rb +1 -1
- data/lib/one_apm/collector/containers/transaction_sampler.rb +7 -1
- data/lib/one_apm/collector/containers/utilization_data.rb +3 -4
- data/lib/one_apm/{agent → collector}/sampler.rb +1 -1
- data/lib/one_apm/{agent → collector}/samplers/cpu_sampler.rb +4 -4
- data/lib/one_apm/{agent → collector}/samplers/delayed_job_sampler.rb +3 -14
- data/lib/one_apm/{agent → collector}/samplers/memory_sampler.rb +13 -15
- data/lib/one_apm/{agent → collector}/samplers/object_sampler.rb +3 -3
- data/lib/one_apm/{agent → collector}/samplers/vm_sampler.rb +2 -2
- data/lib/one_apm/collector/{forked_process_service.rb → support/forked_process_service.rb} +1 -1
- data/lib/one_apm/{agent → collector/support}/sampler_collection.rb +2 -2
- data/lib/one_apm/configuration/default_source.rb +7 -2
- data/lib/one_apm/frameworks/rails.rb +18 -0
- data/lib/one_apm/inst/nosql/memcache.rb +2 -2
- data/lib/one_apm/inst/nosql/mongo.rb +6 -6
- data/lib/one_apm/inst/nosql/mongo_moped.rb +3 -3
- data/lib/one_apm/inst/nosql/redis.rb +4 -4
- data/lib/one_apm/manager.rb +38 -6
- data/lib/one_apm/metrics/metric_spec.rb +1 -4
- data/lib/one_apm/probe/instrumentation.rb +2 -5
- data/lib/one_apm/rack/developer_mode.rb +221 -0
- data/lib/one_apm/rack/developer_mode/helper.rb +299 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_explain_plans.html.erb +25 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_home_right.html.erb +18 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_sample.html.erb +20 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_segment.html.erb +24 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_segment_limit_message.html.erb +1 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_segment_row.html.erb +11 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_show_sample_detail.html.erb +30 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_show_sample_sql.html.erb +19 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_show_sample_summary.html.erb +25 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_sql_row.html.erb +15 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_stack_trace.html.erb +14 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/_summary_table.html.erb +12 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/images/arrow-close.png +0 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/images/arrow-open.png +0 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/images/oneapm_logo.png +0 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/javascript/c3.min.js +5 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/javascript/d3.min.js +5 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/javascript/jquery.min.js +4 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/javascript/transaction_sample.js +120 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/stylesheets/bootstrap.min.css +5 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/stylesheets/c3.css +158 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/assets/stylesheets/style.css +149 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/explain_sql.html.erb +53 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/index.html.erb +33 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/layout.html.erb +46 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/sample_not_found.html.erb +2 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/show_sample.html.erb +61 -0
- data/lib/one_apm/rack/developer_mode/views/oneapm/threads.html.erb +53 -0
- data/lib/one_apm/{agent/threading → support/backtrace}/backtrace_node.rb +0 -0
- data/lib/one_apm/{agent/threading → support/backtrace}/backtrace_service.rb +0 -0
- data/lib/one_apm/support/environment_report.rb +6 -38
- data/lib/one_apm/support/system_info.rb +1 -6
- data/lib/one_apm/transaction/composite_segment.rb +30 -0
- data/lib/one_apm/transaction/sample_buffer/developer_mode_sample_buffer.rb +58 -0
- data/lib/one_apm/transaction/segment_summary.rb +0 -5
- data/lib/one_apm/transaction/summary_segment.rb +24 -0
- data/lib/one_apm/transaction/transaction_sample.rb +2 -0
- data/lib/one_apm/version.rb +2 -2
- metadata +51 -19
- data/lib/one_apm/configuration/autostart.rb +0 -41
@@ -14,7 +14,7 @@ LibraryDetection.defer do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
executes do
|
17
|
-
require 'one_apm/agent/
|
17
|
+
require 'one_apm/agent/datastore'
|
18
18
|
|
19
19
|
::Redis::Client.class_eval do
|
20
20
|
# Support older versions of Redis::Client that used the method
|
@@ -28,7 +28,7 @@ LibraryDetection.defer do
|
|
28
28
|
_send_to_one_apm(args, elapsed)
|
29
29
|
end
|
30
30
|
|
31
|
-
OneApm::Agent::
|
31
|
+
OneApm::Agent::Datastore.wrap("Redis", method_name, nil, callback) do
|
32
32
|
call_without_oneapm_trace(*args, &blk)
|
33
33
|
end
|
34
34
|
end
|
@@ -57,7 +57,7 @@ LibraryDetection.defer do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
OneApm::Agent::
|
60
|
+
OneApm::Agent::Datastore.wrap("Redis", "pipelined", nil, callback) do
|
61
61
|
call_pipelined_without_oneapm_trace commands, *rest
|
62
62
|
end
|
63
63
|
end
|
@@ -76,7 +76,7 @@ LibraryDetection.defer do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
-
OneApm::Agent::
|
79
|
+
OneApm::Agent::Datastore.notice_statement(args.inspect, elapsed)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
data/lib/one_apm/manager.rb
CHANGED
@@ -35,12 +35,12 @@ require 'one_apm/support/forked_process_channel'
|
|
35
35
|
|
36
36
|
require 'one_apm/configuration'
|
37
37
|
|
38
|
-
require 'one_apm/
|
39
|
-
require 'one_apm/
|
40
|
-
require 'one_apm/
|
41
|
-
require 'one_apm/
|
42
|
-
require 'one_apm/
|
43
|
-
require 'one_apm/
|
38
|
+
require 'one_apm/collector/sampler'
|
39
|
+
require 'one_apm/collector/samplers/cpu_sampler'
|
40
|
+
require 'one_apm/collector/samplers/memory_sampler'
|
41
|
+
require 'one_apm/collector/samplers/object_sampler'
|
42
|
+
require 'one_apm/collector/samplers/delayed_job_sampler'
|
43
|
+
require 'one_apm/collector/samplers/vm_sampler'
|
44
44
|
|
45
45
|
module OneApm
|
46
46
|
module Manager
|
@@ -245,5 +245,37 @@ module OneApm
|
|
245
245
|
require File.expand_path(path)
|
246
246
|
end
|
247
247
|
|
248
|
+
def agent_should_start?
|
249
|
+
!blacklisted_constants? &&
|
250
|
+
!blacklisted_executables? &&
|
251
|
+
!in_blacklisted_rake_task?
|
252
|
+
end
|
253
|
+
|
254
|
+
def blacklisted_constants?
|
255
|
+
blacklisted?(OneApm::Manager.config[:'autostart.blacklisted_constants']) do |name|
|
256
|
+
OneApm::LanguageSupport.constant_is_defined?(name)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def blacklisted_executables?
|
261
|
+
blacklisted?(OneApm::Manager.config[:'autostart.blacklisted_executables']) do |bin|
|
262
|
+
File.basename($0) == bin
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def blacklisted?(value, &block)
|
267
|
+
value.split(/\s*,\s*/).any?(&block)
|
268
|
+
end
|
269
|
+
|
270
|
+
def in_blacklisted_rake_task?
|
271
|
+
tasks = begin
|
272
|
+
::Rake.application.top_level_tasks
|
273
|
+
rescue => e
|
274
|
+
OneApm::Manager.logger.debug("Not in Rake environment so skipping blacklisted_rake_tasks check: #{e}")
|
275
|
+
[]
|
276
|
+
end
|
277
|
+
!(tasks & OneApm::Manager.config[:'autostart.blacklisted_rake_tasks'].split(/\s*,\s*/)).empty?
|
278
|
+
end
|
279
|
+
|
248
280
|
end
|
249
281
|
end
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
# this struct uniquely defines a metric, optionally inside
|
4
|
-
# the call scope of another metric
|
5
|
-
# the call scope of another metric
|
6
3
|
module OneApm
|
7
4
|
class MetricSpec
|
8
5
|
attr_reader :name, :scope
|
@@ -79,4 +76,4 @@ module OneApm
|
|
79
76
|
return (self.scope || '') <=> (o.scope || '')
|
80
77
|
end
|
81
78
|
end
|
82
|
-
end
|
79
|
+
end
|
@@ -6,14 +6,11 @@ module OneApm
|
|
6
6
|
class Probe
|
7
7
|
module Instrumentation
|
8
8
|
|
9
|
-
# install instrumentations for the current framework
|
10
9
|
def install_instrumentation
|
11
10
|
return if @instrumented
|
12
11
|
|
13
12
|
@instrumented = true
|
14
13
|
|
15
|
-
# Instrumentation for the key code points inside rails for monitoring by OneApm.
|
16
|
-
# note this file is loaded only if the oneapm agent is enabled (through config/oneapm.yml)
|
17
14
|
instrumentation_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'inst'))
|
18
15
|
Dir.glob("#{instrumentation_path}/**/*.rb").each do |inst_file|
|
19
16
|
load_instrumentation_files inst_file
|
@@ -30,7 +27,6 @@ module OneApm
|
|
30
27
|
LibraryDetection.detect!
|
31
28
|
end
|
32
29
|
|
33
|
-
# require specified instrumentation
|
34
30
|
def require_instrumentation file
|
35
31
|
require file
|
36
32
|
rescue => e
|
@@ -38,7 +34,6 @@ module OneApm
|
|
38
34
|
end
|
39
35
|
|
40
36
|
def install_shim
|
41
|
-
# Once we install instrumentation, you can't undo that by installing the shim.
|
42
37
|
if @instrumented
|
43
38
|
OneApm::Manager.logger.error "Cannot install the Agent shim after instrumentation has already been installed!"
|
44
39
|
OneApm::Manager.logger.error caller.join("\n")
|
@@ -54,7 +49,9 @@ module OneApm
|
|
54
49
|
@instrumentation_files << pattern
|
55
50
|
end
|
56
51
|
end
|
52
|
+
|
57
53
|
end
|
54
|
+
|
58
55
|
include Instrumentation
|
59
56
|
end
|
60
57
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
require 'rack/request'
|
5
|
+
require 'rack/response'
|
6
|
+
require 'rack/file'
|
7
|
+
|
8
|
+
require 'one_apm/support/collection_helper'
|
9
|
+
require 'one_apm/rack/middleware_base'
|
10
|
+
require 'one_apm/rack/middleware_wrapper'
|
11
|
+
|
12
|
+
require 'one_apm/transaction/transaction_sample'
|
13
|
+
require 'one_apm/transaction/transaction_analysis'
|
14
|
+
|
15
|
+
require 'one_apm/rack/developer_mode/helper'
|
16
|
+
|
17
|
+
module OneApm
|
18
|
+
module Rack
|
19
|
+
class DeveloperMode < MiddlewareBase
|
20
|
+
|
21
|
+
VIEW_PATH = File.expand_path('../developer_mode/views/', __FILE__)
|
22
|
+
|
23
|
+
include OneApm::DeveloperModeHelper
|
24
|
+
|
25
|
+
def traced_call(env)
|
26
|
+
return @app.call(env) unless /^\/oneapm/ =~ ::Rack::Request.new(env).path_info
|
27
|
+
dup._call(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def _call(env)
|
33
|
+
OneApm::Manager.ignore_transaction
|
34
|
+
|
35
|
+
@req = ::Rack::Request.new(env)
|
36
|
+
@rendered = false
|
37
|
+
case @req.path_info
|
38
|
+
when /assets/
|
39
|
+
::Rack::File.new(VIEW_PATH).call(env)
|
40
|
+
when /index/
|
41
|
+
index
|
42
|
+
when /threads/
|
43
|
+
threads
|
44
|
+
when /reset/
|
45
|
+
reset
|
46
|
+
when /show_sample_summary/
|
47
|
+
show_sample_data
|
48
|
+
when /show_sample_detail/
|
49
|
+
show_sample_data
|
50
|
+
when /show_sample_sql/
|
51
|
+
show_sample_data
|
52
|
+
when /explain_sql/
|
53
|
+
explain_sql
|
54
|
+
when /^\/oneapm\/?$/
|
55
|
+
index
|
56
|
+
else
|
57
|
+
@app.call(env)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def index
|
64
|
+
get_samples
|
65
|
+
render(:index)
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset
|
69
|
+
OneApm::Manager.agent.transaction_sampler.reset!
|
70
|
+
OneApm::Manager.agent.sql_sampler.reset!
|
71
|
+
::Rack::Response.new{|r| r.redirect('/oneapm/')}.finish
|
72
|
+
end
|
73
|
+
|
74
|
+
def explain_sql
|
75
|
+
get_segment
|
76
|
+
|
77
|
+
return render(:sample_not_found) unless @sample
|
78
|
+
|
79
|
+
@sql = @segment[:sql]
|
80
|
+
@trace = @segment[:backtrace]
|
81
|
+
|
82
|
+
if OneApm::Manager.agent.record_sql == :obfuscated
|
83
|
+
@obfuscated_sql = @segment.obfuscated_sql
|
84
|
+
end
|
85
|
+
|
86
|
+
_headers, explanations = @segment.explain_sql
|
87
|
+
if explanations
|
88
|
+
@explanation = explanations
|
89
|
+
if !@explanation.blank?
|
90
|
+
if @explanation.first.length < OneApm::MYSQL_EXPLAIN_COLUMNS.length
|
91
|
+
@row_headers = nil
|
92
|
+
else
|
93
|
+
@row_headers = OneApm::MYSQL_EXPLAIN_COLUMNS
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
render(:explain_sql)
|
99
|
+
end
|
100
|
+
|
101
|
+
def threads
|
102
|
+
render(:threads)
|
103
|
+
end
|
104
|
+
|
105
|
+
def render(view, layout=true)
|
106
|
+
add_rack_array = true
|
107
|
+
if view.is_a? Hash
|
108
|
+
layout = false
|
109
|
+
if view[:object]
|
110
|
+
# object *is* used here, as it is capture in the binding below
|
111
|
+
object = view[:object]
|
112
|
+
end
|
113
|
+
|
114
|
+
if view[:collection]
|
115
|
+
return view[:collection].map do |obj|
|
116
|
+
render({:partial => view[:partial], :object => obj})
|
117
|
+
end.join(' ')
|
118
|
+
end
|
119
|
+
|
120
|
+
if view[:partial]
|
121
|
+
add_rack_array = false
|
122
|
+
view = "_#{view[:partial]}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
binding = Proc.new {}.binding
|
126
|
+
if layout
|
127
|
+
body = render_with_layout(view) do
|
128
|
+
render_without_layout(view, binding)
|
129
|
+
end
|
130
|
+
else
|
131
|
+
body = render_without_layout(view, binding)
|
132
|
+
end
|
133
|
+
if add_rack_array
|
134
|
+
::Rack::Response.new(body, 200, {'Content-Type' => 'text/html'}).finish
|
135
|
+
else
|
136
|
+
body
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# You have to call this with a block - the contents returned from
|
141
|
+
# that block are interpolated into the layout
|
142
|
+
def render_with_layout(view)
|
143
|
+
body = ERB.new(File.read(File.join(VIEW_PATH, 'oneapm/layout.html.erb')))
|
144
|
+
body.result(Proc.new {}.binding)
|
145
|
+
end
|
146
|
+
|
147
|
+
# you have to pass a binding to this (a proc) so that ERB can have
|
148
|
+
# access to helper functions and local variables
|
149
|
+
def render_without_layout(view, binding)
|
150
|
+
ERB.new(File.read(File.join(VIEW_PATH, 'oneapm', view.to_s + '.html.erb')), nil, nil, 'frobnitz').result(binding)
|
151
|
+
end
|
152
|
+
|
153
|
+
def content_tag(tag, contents, opts={})
|
154
|
+
opt_values = opts.map {|k, v| "#{k}=\"#{v}\"" }.join(' ')
|
155
|
+
"<#{tag} #{opt_values}>#{contents}</#{tag}>"
|
156
|
+
end
|
157
|
+
|
158
|
+
def sample
|
159
|
+
@sample || @samples[0]
|
160
|
+
end
|
161
|
+
|
162
|
+
def params
|
163
|
+
@req.params
|
164
|
+
end
|
165
|
+
|
166
|
+
def segment
|
167
|
+
@segment
|
168
|
+
end
|
169
|
+
|
170
|
+
def show_sample_data
|
171
|
+
get_sample
|
172
|
+
|
173
|
+
return render(:sample_not_found) unless @sample
|
174
|
+
|
175
|
+
controller_metric = @sample.transaction_name
|
176
|
+
|
177
|
+
@sample_controller_name = controller_metric
|
178
|
+
|
179
|
+
@sql_segments = @sample.sql_segments
|
180
|
+
if params['d']
|
181
|
+
@sql_segments.sort!{|a,b| b.duration <=> a.duration }
|
182
|
+
end
|
183
|
+
|
184
|
+
sort_method = params['sort'] || :total_time
|
185
|
+
@profile_options = {:min_percent => 0.5, :sort_method => sort_method.to_sym}
|
186
|
+
|
187
|
+
render(:show_sample)
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_samples
|
191
|
+
@samples = OneApm::Manager.agent.transaction_sampler.dev_mode_sample_buffer.samples.select do |sample|
|
192
|
+
sample.params[:path] != nil
|
193
|
+
end
|
194
|
+
|
195
|
+
return @samples = @samples.sort_by(&:duration).reverse if params['h']
|
196
|
+
return @samples = @samples.sort{|x,y| x.params[:uri] <=> y.params[:uri]} if params['u']
|
197
|
+
@samples = @samples.reverse
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_sample
|
201
|
+
get_samples
|
202
|
+
id = params['id']
|
203
|
+
sample_id = id.to_i
|
204
|
+
@samples.each do |s|
|
205
|
+
if s.sample_id == sample_id
|
206
|
+
@sample = s
|
207
|
+
return
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_segment
|
213
|
+
get_sample
|
214
|
+
return unless @sample
|
215
|
+
|
216
|
+
segment_id = params['segment'].to_i
|
217
|
+
@segment = @sample.find_segment(segment_id)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,299 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'one_apm/support/collection_helper'
|
5
|
+
|
6
|
+
module OneApm::DeveloperModeHelper
|
7
|
+
include OneApm::CollectionHelper
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# limit of how many detail/SQL rows we display - very large data sets (~10000+) crash browsers
|
12
|
+
def trace_row_display_limit
|
13
|
+
2000
|
14
|
+
end
|
15
|
+
|
16
|
+
def trace_row_display_limit_reached
|
17
|
+
(!@detail_segment_count.nil? && @detail_segment_count > trace_row_display_limit) || @sample.sql_segments.length > trace_row_display_limit
|
18
|
+
end
|
19
|
+
|
20
|
+
# return the highest level in the call stack for the trace that is not rails or
|
21
|
+
# oneapm agent code
|
22
|
+
def application_caller(trace)
|
23
|
+
trace = strip_oa_from_backtrace(trace) unless params[:show_nr]
|
24
|
+
trace.each do |trace_line|
|
25
|
+
file, _line, gem = file_and_line(trace_line)
|
26
|
+
unless file && exclude_file_from_stack_trace?(file, false, gem)
|
27
|
+
return trace_line
|
28
|
+
end
|
29
|
+
end
|
30
|
+
trace.last
|
31
|
+
end
|
32
|
+
|
33
|
+
def application_stack_trace(trace, include_rails = false)
|
34
|
+
trace = strip_oa_from_backtrace(trace) unless params[:show_nr]
|
35
|
+
trace.reject do |trace_line|
|
36
|
+
file, _line, gem = file_and_line(trace_line)
|
37
|
+
file && exclude_file_from_stack_trace?(file, include_rails, gem)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_backtrace
|
42
|
+
if @segment[:backtrace]
|
43
|
+
content_tag('h3', '调用堆栈') +
|
44
|
+
render(:partial => 'stack_trace')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def h(text)
|
49
|
+
text
|
50
|
+
end
|
51
|
+
|
52
|
+
def agent_views_path(path)
|
53
|
+
path
|
54
|
+
end
|
55
|
+
|
56
|
+
# write the metric label for a segment metric in the detail view
|
57
|
+
def write_segment_label(segment)
|
58
|
+
link_to_function(segment.metric_name, "toggle_row_class($(this).closest('td').find('a')[0])")
|
59
|
+
end
|
60
|
+
|
61
|
+
# write the metric label for a segment metric in the summary table of metrics
|
62
|
+
def write_summary_segment_label(segment)
|
63
|
+
segment.metric_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def write_stack_trace_line(trace_line)
|
67
|
+
trace_line
|
68
|
+
end
|
69
|
+
|
70
|
+
# print the formatted timestamp for a segment
|
71
|
+
def timestamp(segment)
|
72
|
+
sprintf("%1.3f", segment.entry_timestamp)
|
73
|
+
end
|
74
|
+
|
75
|
+
def format_timestamp(time)
|
76
|
+
time.strftime("%H:%M:%S")
|
77
|
+
end
|
78
|
+
|
79
|
+
def colorize(value, yellow_threshold = 0.05, red_threshold = 0.15, s=to_ms(value))
|
80
|
+
if value > yellow_threshold
|
81
|
+
color = (value > red_threshold ? 'red' : 'orange')
|
82
|
+
"<font color=#{color}>#{s}</font>"
|
83
|
+
else
|
84
|
+
"#{s}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def expanded_image_path()
|
89
|
+
'/oneapm/assets/images/arrow-open.png'
|
90
|
+
end
|
91
|
+
|
92
|
+
def collapsed_image_path()
|
93
|
+
'/oneapm/assets/images/arrow-close.png'
|
94
|
+
end
|
95
|
+
|
96
|
+
def explain_sql_url(segment)
|
97
|
+
"explain_sql?id=#{@sample.sample_id}&segment=#{segment.segment_id}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def segment_duration_value(segment)
|
101
|
+
link_to colorize(segment.duration, 0.05, 0.15, "#{with_delimiter(to_ms(segment.duration))} ms"), explain_sql_url(segment)
|
102
|
+
end
|
103
|
+
|
104
|
+
def line_wrap_sql(sql)
|
105
|
+
sql.gsub(/\,/,', ').squeeze(' ') if sql
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_sample_details(sample)
|
109
|
+
@indentation_depth=0
|
110
|
+
# skip past the root segments to the first child, which is always the controller
|
111
|
+
first_segment = sample.root_segment.called_segments.first
|
112
|
+
|
113
|
+
# render the segments, then the css classes to indent them
|
114
|
+
render_segment_details(first_segment).to_s + render_indentation_classes(@indentation_depth).to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def expand_segment_image(segment, depth)
|
118
|
+
if depth > 0
|
119
|
+
if !segment.called_segments.empty?
|
120
|
+
row_class =segment_child_row_class(segment)
|
121
|
+
link_to_function("<img src=\"#{collapsed_image_path}\" id=\"image_#{row_class}\" class_for_children=\"#{row_class}\" class=\"#{(!segment.called_segments.empty?) ? 'parent_segment_image' : 'child_segment_image'}\" />", "toggle_row_class(this)")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def segment_child_row_class(segment)
|
127
|
+
"segment#{segment.segment_id}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def segment_row_classes(segment, depth)
|
131
|
+
classes = []
|
132
|
+
|
133
|
+
classes << "segment#{segment.parent_segment.segment_id}" if depth > 1
|
134
|
+
|
135
|
+
classes << "view_segment" if segment.metric_name.index('View') == 0
|
136
|
+
classes << "summary_segment" if segment.is_a?(OneApm::TransactionSample::CompositeSegment)
|
137
|
+
|
138
|
+
classes.join(' ')
|
139
|
+
end
|
140
|
+
|
141
|
+
# render_segment_details should be called before calling this method
|
142
|
+
def render_indentation_classes(depth)
|
143
|
+
styles = []
|
144
|
+
(1..depth).each do |d|
|
145
|
+
styles << ".segment_indent_level#{d} { display: inline-block; margin-left: #{(d-1)*10}px }"
|
146
|
+
end
|
147
|
+
content_tag("style", styles.join(' '))
|
148
|
+
end
|
149
|
+
|
150
|
+
def explain_sql_link(segment, child_sql = false)
|
151
|
+
link_to 'SQL', explain_sql_url(segment)
|
152
|
+
end
|
153
|
+
|
154
|
+
def explain_sql_links(segment)
|
155
|
+
if segment[:sql]
|
156
|
+
explain_sql_link segment
|
157
|
+
else
|
158
|
+
links = []
|
159
|
+
segment.called_segments.each do |child|
|
160
|
+
if child[:sql]
|
161
|
+
links << explain_sql_link(child, true)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
links[0..1].join(', ') + (links.length > 2?', ...':'')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
# return three objects, the file path, the line in the file, and the gem the file belongs to
|
170
|
+
# if found
|
171
|
+
def file_and_line(stack_trace_line)
|
172
|
+
if stack_trace_line =~ /^(?:(\w+) \([\d.]*\) )?(.*):(\d+)/
|
173
|
+
return $2, $3, $1
|
174
|
+
else
|
175
|
+
return nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def render_segment_details(segment, depth=0)
|
180
|
+
@detail_segment_count ||= 0
|
181
|
+
@detail_segment_count += 1
|
182
|
+
|
183
|
+
return '' if @detail_segment_count > trace_row_display_limit
|
184
|
+
|
185
|
+
@indentation_depth = depth if depth > @indentation_depth
|
186
|
+
repeat = nil
|
187
|
+
if segment.is_a?(OneApm::TransactionSample::CompositeSegment)
|
188
|
+
html = ''
|
189
|
+
else
|
190
|
+
repeat = segment.parent_segment.detail_segments.length if segment.parent_segment.is_a?(OneApm::TransactionSample::CompositeSegment)
|
191
|
+
html = render(:partial => 'segment', :object => [segment, depth, repeat])
|
192
|
+
depth += 1
|
193
|
+
end
|
194
|
+
|
195
|
+
segment.called_segments.each do |child|
|
196
|
+
html << render_segment_details(child, depth)
|
197
|
+
end
|
198
|
+
|
199
|
+
html
|
200
|
+
end
|
201
|
+
|
202
|
+
def exclude_file_from_stack_trace?(file, include_rails, gem=nil)
|
203
|
+
return false if include_rails
|
204
|
+
return true if file !~ /\.(rb|java)/
|
205
|
+
return true if %w[rack activerecord activeresource activesupport actionpack railties].include? gem
|
206
|
+
%w[/actionmailer/
|
207
|
+
/activerecord
|
208
|
+
/activeresource
|
209
|
+
/activesupport
|
210
|
+
/lib/mongrel
|
211
|
+
/actionpack
|
212
|
+
/passenger/
|
213
|
+
/railties
|
214
|
+
benchmark.rb].each { |s| return true if file.include? s }
|
215
|
+
false
|
216
|
+
end
|
217
|
+
|
218
|
+
def link_to(name, location)
|
219
|
+
location = "/oneapm/#{location}" unless /:\/\// =~ location
|
220
|
+
"<a href=\"#{location}\">#{name}</a>"
|
221
|
+
end
|
222
|
+
|
223
|
+
def link_to_if(predicate, text, location="")
|
224
|
+
if predicate
|
225
|
+
link_to(text, location)
|
226
|
+
else
|
227
|
+
text
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def link_to_unless_current(text, hash)
|
232
|
+
unless params[hash.keys[0].to_s]
|
233
|
+
link_to(text,"?#{hash.keys[0]}=#{hash.values[0]}")
|
234
|
+
else
|
235
|
+
text
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def cycle(even, odd)
|
240
|
+
@cycle ||= 'a'
|
241
|
+
if @cycle == 'a'
|
242
|
+
@cycle = 'b'
|
243
|
+
even
|
244
|
+
else
|
245
|
+
@cycle = 'a'
|
246
|
+
odd
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def link_to_function(title, javascript)
|
251
|
+
"<a href=\"#\" onclick=\"#{javascript}; return false;\">#{title}</a>"
|
252
|
+
end
|
253
|
+
|
254
|
+
def mime_type_from_extension(extension)
|
255
|
+
extension = extension[/[^.]*$/].dncase
|
256
|
+
case extension
|
257
|
+
when 'png'; 'image/png'
|
258
|
+
when 'gif'; 'image/gif'
|
259
|
+
when 'jpg'; 'image/jpg'
|
260
|
+
when 'css'; 'text/css'
|
261
|
+
when 'js'; 'text/javascript'
|
262
|
+
else 'text/plain'
|
263
|
+
end
|
264
|
+
end
|
265
|
+
def to_ms(number)
|
266
|
+
(number*1000).round
|
267
|
+
end
|
268
|
+
def to_percentage(value)
|
269
|
+
(value * 100).round if value
|
270
|
+
end
|
271
|
+
def with_delimiter(val)
|
272
|
+
return '0' if val.nil?
|
273
|
+
parts = val.to_s.split('.')
|
274
|
+
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
|
275
|
+
parts.join '.'
|
276
|
+
end
|
277
|
+
|
278
|
+
SORT_REPLACEMENTS = {
|
279
|
+
"Total" => :total_time,
|
280
|
+
"Self" => :self_time,
|
281
|
+
"Child" => :children_time,
|
282
|
+
"Wait" => :wait_time
|
283
|
+
}
|
284
|
+
|
285
|
+
def profile_table(sample, options)
|
286
|
+
out = StringIO.new
|
287
|
+
printer = RubyProf::GraphHtmlPrinter.new(sample.profile)
|
288
|
+
printer.print(out, options)
|
289
|
+
out = out.string[/<body>(.*)<\/body>/im, 0].gsub('<table>', '<table class=profile>')
|
290
|
+
SORT_REPLACEMENTS.each do |text, param|
|
291
|
+
replacement = (options[:sort_method] == param) ?
|
292
|
+
"<th> #{text} ↓</th>" :
|
293
|
+
"<th>#{link_to text, "show_sample_summary?id=#{sample.sample_id}&sort=#{param}"}</th>"
|
294
|
+
|
295
|
+
out.gsub!(/<th> +#{text}<\/th>/, replacement)
|
296
|
+
end
|
297
|
+
out
|
298
|
+
end
|
299
|
+
end
|