dolores_rpm 3.3.4.fork → 3.3.4.1.fork

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,8 @@
1
- v3.3.4.fork
2
- * Roll back to previous version of DataMapper instrumentation (current one does not work for our app)
1
+ v3.3.4.2
2
+ * Add additional instrumentation for DataMapper
3
+
4
+ v3.3.4.1
5
+ * Bug fix when rendering empty collection in Rails 3.1+
3
6
 
4
7
  v3.3.4
5
8
  * Rails 3 view instrumentation
data/dolores_rpm.gemspec CHANGED
@@ -5,12 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "dolores_rpm"
8
- s.version = "3.3.4.fork"
8
+ s.version = "3.3.4.1.fork"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bill Kayser", "Jon Guymon", "Justin George", "Darin Swanson"]
12
- s.date = "2012-04-27"
13
- s.description = "New Relic is a performance management system, developed by New Relic,\nInc (http://www.newrelic.com). This is a fork that uses an older version\nof the DataMapper instrumentation. Newer versions did not work for us.\n"
12
+ s.date = "2012-05-10"
13
+ s.description = "New Relic is a performance management system, developed by New Relic,\nInc (http://www.newrelic.com). New Relic provides you with deep\ninformation about the performance of your web application as it runs\nin production. The New Relic Ruby Agent is dual-purposed as a either a\nGem or plugin, hosted on\nhttp://github.com/newrelic/rpm/\n"
14
14
  s.email = "support@newrelic.com"
15
15
  s.executables = ["mongrel_rpm", "newrelic", "newrelic_cmd"]
16
16
  s.extra_rdoc_files = [
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "cert/cacert.pem",
30
30
  "cert/oldsite.pem",
31
31
  "cert/site.pem",
32
+ "dolores_rpm.gemspec",
32
33
  "install.rb",
33
34
  "lib/conditional_vendored_dependency_detection.rb",
34
35
  "lib/conditional_vendored_metric_parser.rb",
@@ -129,7 +130,6 @@ Gem::Specification.new do |s|
129
130
  "lib/tasks/install.rake",
130
131
  "lib/tasks/tests.rake",
131
132
  "newrelic.yml",
132
- "dolores_rpm.gemspec",
133
133
  "recipes/newrelic.rb",
134
134
  "test/active_record_fixtures.rb",
135
135
  "test/config/newrelic.yml",
@@ -1,57 +1,276 @@
1
- # NewRelic instrumentation for DataMapper
2
- # For now, we have to refer to all db metrics as "ActiveRecord"
3
- if defined? DataMapper
1
+ ## NewRelic instrumentation for DataMapper
2
+ #
3
+ # Instrumenting DM has different key challenges versus AR:
4
+ #
5
+ # 1. The hooking of SQL logging in DM is decoupled from any knowledge of the
6
+ # Model#method that invoked it. But on the positive side, the duration is
7
+ # already calculated for you (and it happens inside the C-based DO code, so
8
+ # it's faster than a Ruby equivalent).
9
+ #
10
+ # 2. There are a lot more entry points that need to be hooked in order to
11
+ # understand call flow: DM::Model (model class) vs. DM::Resource (model
12
+ # instance) vs. DM::Collection (collection of model instances). And
13
+ # others.
14
+ #
15
+ # 3. Strategic Eager Loading (SEL) combined with separately-grouped
16
+ # lazy-loaded attributes presents a unique problem for tying resulting
17
+ # SEL-invoked SQL calls to their proper scope.
18
+ #
19
+ # NOTE: On using "Database" versus "ActiveRecord" as base metric name
20
+ #
21
+ # Using "Database" as the metric name base seems to properly identify methods
22
+ # as being DB-related in call graphs, but certain New Relic views that show
23
+ # aggregations of DB CPM, etc still seem to rely solely on "ActiveRecord"
24
+ # being the base name, thus AFAICT "Database" calls to this are lost. (Though
25
+ # I haven't yet tested "Database/SQL/{find/save/destroy/all}" yet, as it seems
26
+ # like an intuitively good name to use.)
27
+ #
28
+ # So far I think these are the rules:
29
+ #
30
+ # - ActiveRecord/{find/save/destroy} populates "Database Throughput" and
31
+ # "Database Response Time" in the Database tab. [non-scoped]
32
+ #
33
+ # - ActiveRecord/all populates the main Overview tab of DB time. (still
34
+ # unsure about this one). [non-scoped]
35
+ #
36
+ # These metrics are represented as :push_scope => false or included as the
37
+ # non-first metric in trace_execution_scoped() (docs say only first counts
38
+ # towards scope) so they don't show up ine normal call graph/trace.
39
+
40
+ DependencyDetection.defer do
41
+ @name = :data_mapper
4
42
 
5
- DataMapper::Model.class_eval do
6
- add_method_tracer :get, 'ActiveRecord/#{self.name}/find'
7
- add_method_tracer :first, 'ActiveRecord/#{self.name}/find'
8
- add_method_tracer :first_or_create, 'ActiveRecord/#{self.name}/find'
9
- add_method_tracer :all, 'ActiveRecord/#{self.name}/find_all'
43
+ depends_on do
44
+ defined?(::DataMapper)
45
+ end
46
+
47
+ depends_on do
48
+ defined?(DataMapper::Model)
49
+ end
50
+
51
+ depends_on do
52
+ defined?(DataMapper::Resource)
10
53
  end
11
- DataMapper::Resource.class_eval do
12
54
 
13
- @@my_sql_defined = defined? ActiveRecord::ConnectionAdapters::MysqlAdapter
14
- @@postgres_defined = defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
55
+ depends_on do
56
+ defined?(DataMapper::Collection)
57
+ end
58
+
59
+ executes do
60
+ NewRelic::Agent.logger.debug 'Installing DataMapper instrumentation'
61
+ end
62
+
63
+ executes do
64
+ DataMapper::Model.class_eval do
65
+ add_method_tracer :get, 'ActiveRecord/#{self.name}/get'
66
+ add_method_tracer :first, 'ActiveRecord/#{self.name}/first'
67
+ add_method_tracer :last, 'ActiveRecord/#{self.name}/last'
68
+ add_method_tracer :all, 'ActiveRecord/#{self.name}/all'
69
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
70
+ add_method_tracer :create!, 'ActiveRecord/#{self.name}/create'
71
+ add_method_tracer :update, 'ActiveRecord/#{self.name}/update'
72
+ add_method_tracer :update!, 'ActiveRecord/#{self.name}/update'
73
+ add_method_tracer :destroy, 'ActiveRecord/#{self.name}/destroy'
74
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.name}/destroy'
75
+
76
+ add_method_tracer :get, 'ActiveRecord/find', :push_scope => false
77
+ add_method_tracer :first, 'ActiveRecord/find', :push_scope => false
78
+ add_method_tracer :last, 'ActiveRecord/find', :push_scope => false
79
+ add_method_tracer :all, 'ActiveRecord/find', :push_scope => false
80
+ add_method_tracer :create, 'ActiveRecord/save', :push_scope => false
81
+ add_method_tracer :create!, 'ActiveRecord/save', :push_scope => false
82
+ add_method_tracer :update, 'ActiveRecord/save', :push_scope => false
83
+ add_method_tracer :update!, 'ActiveRecord/save', :push_scope => false
84
+ add_method_tracer :destroy, 'ActiveRecord/destroy', :push_scope => false
85
+ add_method_tracer :destroy!, 'ActiveRecord/destroy', :push_scope => false
86
+
87
+ add_method_tracer :get, 'ActiveRecord/all', :push_scope => false
88
+ add_method_tracer :first, 'ActiveRecord/all', :push_scope => false
89
+ add_method_tracer :last, 'ActiveRecord/all', :push_scope => false
90
+ add_method_tracer :all, 'ActiveRecord/all', :push_scope => false
91
+ add_method_tracer :create, 'ActiveRecord/all', :push_scope => false
92
+ add_method_tracer :create!, 'ActiveRecord/all', :push_scope => false
93
+ add_method_tracer :update, 'ActiveRecord/all', :push_scope => false
94
+ add_method_tracer :update!, 'ActiveRecord/all', :push_scope => false
95
+ add_method_tracer :destroy, 'ActiveRecord/all', :push_scope => false
96
+ add_method_tracer :destroy!, 'ActiveRecord/all', :push_scope => false
97
+
98
+ # For dm-aggregates and partial dm-ar-finders support:
99
+ for method in [ :aggregate, :find, :find_by_sql ] do
100
+ next unless method_defined? method
101
+ add_method_tracer(method, 'ActiveRecord/#{self.name}/' + method.to_s)
102
+ add_method_tracer(method, 'ActiveRecord/find', :push_scope => false)
103
+ add_method_tracer(method, 'ActiveRecord/all', :push_scope => false)
104
+ end
15
105
 
16
- for method in [:query] do
17
- add_method_tracer method, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/execute'
18
- add_method_tracer method, 'ActiveRecord/all', :push_scope => false
19
106
  end
20
- for method in [:update, :save] do
21
- add_method_tracer method, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
22
- add_method_tracer method, 'ActiveRecord/save', :push_scope => false
107
+ end
108
+
109
+ executes do
110
+ DataMapper::Resource.class_eval do
111
+ add_method_tracer :update, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/update'
112
+ add_method_tracer :update!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/update'
113
+ add_method_tracer :save, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
114
+ add_method_tracer :save!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
115
+ add_method_tracer :destroy, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
116
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
117
+
118
+ add_method_tracer :update, 'ActiveRecord/all', :push_scope => false
119
+ add_method_tracer :update!, 'ActiveRecord/save', :push_scope => false
120
+ add_method_tracer :save, 'ActiveRecord/save', :push_scope => false
121
+ add_method_tracer :save!, 'ActiveRecord/save', :push_scope => false
122
+ add_method_tracer :destroy, 'ActiveRecord/destroy', :push_scope => false
123
+ add_method_tracer :destroy!, 'ActiveRecord/destroy', :push_scope => false
124
+
125
+ add_method_tracer :update, 'ActiveRecord/all', :push_scope => false
126
+ add_method_tracer :update!, 'ActiveRecord/all', :push_scope => false
127
+ add_method_tracer :save, 'ActiveRecord/all', :push_scope => false
128
+ add_method_tracer :save!, 'ActiveRecord/all', :push_scope => false
129
+ add_method_tracer :destroy, 'ActiveRecord/all', :push_scope => false
130
+ add_method_tracer :destroy!, 'ActiveRecord/all', :push_scope => false
23
131
  end
24
- add_method_tracer :destroy, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
25
- add_method_tracer :destroy, 'ActiveRecord/destroy', :push_scope => false
26
-
27
- def log_with_newrelic_instrumentation(sql, name, &block)
28
- # if we aren't in a blamed context, then add one so that we can
29
- # see that controllers are calling SQL directly we check
30
- # scope_depth vs 2 since the controller is 1
31
- if NewRelic::Agent.instance.transaction_sampler.scope_depth < 2
32
- self.class.trace_method_execution "Database/DirectSQL", true, true do
33
- log_with_capture_sql(sql, name, &block)
34
- end
35
- else
36
- log_with_capture_sql(sql, name, &block)
132
+ end
133
+
134
+ executes do
135
+ DataMapper::Collection.class_eval do
136
+ # DM's Collection instance methods
137
+ add_method_tracer :get, 'ActiveRecord/#{self.name}/get'
138
+ add_method_tracer :first, 'ActiveRecord/#{self.name}/first'
139
+ add_method_tracer :last, 'ActiveRecord/#{self.name}/last'
140
+ add_method_tracer :all, 'ActiveRecord/#{self.name}/all'
141
+
142
+ add_method_tracer :lazy_load, 'ActiveRecord/#{self.name}/lazy_load'
143
+
144
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
145
+ add_method_tracer :create!, 'ActiveRecord/#{self.name}/create'
146
+ add_method_tracer :update, 'ActiveRecord/#{self.name}/update'
147
+ add_method_tracer :update!, 'ActiveRecord/#{self.name}/update'
148
+ add_method_tracer :destroy, 'ActiveRecord/#{self.name}/destroy'
149
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.name}/destroy'
150
+
151
+ # For dm-aggregates support:
152
+ for method in [ :aggregate ] do
153
+ next unless method_defined? method
154
+ add_method_tracer(method, 'ActiveRecord/#{self.name}/' + method.to_s)
155
+ add_method_tracer(method, 'ActiveRecord/find', :push_scope => false
156
+ add_method_tracer(method, 'ActiveRecord/all', :push_scope => false
37
157
  end
158
+
38
159
  end
39
-
40
- def log_with_capture_sql(sql, name, &block)
41
- if @@my_sql_defined && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
42
- config = @config
43
- elsif @@postgres_defined && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
44
- config = @config
45
- else
46
- config = nil
47
- end
48
-
49
- t0 = Time.now
50
- result = log_without_newrelic_instrumentation(sql, name, &block)
51
-
52
- NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, config, (Time.now - t0).to_f)
53
-
54
- result
160
+ end
161
+ end
162
+
163
+ DependencyDetection.defer do
164
+
165
+ depends_on do
166
+ defined?(DataMapper) && defined?(DataMapper::Adapters) && defined?(DataMapper::Adapters::DataObjectsAdapter)
167
+ end
168
+
169
+ executes do
170
+
171
+ # Catch the two entry points into DM::Repository::Adapter that bypass CRUD
172
+ # (for when SQL is run directly).
173
+ DataMapper::Adapters::DataObjectsAdapter.class_eval do
174
+
175
+ add_method_tracer :select, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/select'
176
+ add_method_tracer :execute, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/execute'
177
+
178
+ end
179
+ end
180
+ end
181
+
182
+ DependencyDetection.defer do
183
+
184
+ depends_on do
185
+ defined?(DataMapper) && defined?(DataMapper::Validations) && defined?(DataMapper::Validations::ClassMethods)
186
+ end
187
+
188
+ # DM::Validations overrides Model#create, but currently in a way that makes it
189
+ # impossible to instrument from one place. I've got a patch pending inclusion
190
+ # to make it instrumentable by putting the create method inside ClassMethods.
191
+ # This will pick it up if/when that patch is accepted.
192
+ executes do
193
+ DataMapper::Validations::ClassMethods.class_eval do
194
+ next unless method_defined? :create
195
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
196
+ end
197
+ end
198
+ end
199
+
200
+ DependencyDetection.defer do
201
+
202
+ depends_on do
203
+ defined?(DataMapper) && defined?(DataMapper::Transaction)
204
+ end
205
+
206
+ # NOTE: DM::Transaction basically calls commit() twice, so as-is it will show
207
+ # up in traces twice -- second time subordinate to the first's scope. Works
208
+ # well enough.
209
+ executes do
210
+ DataMapper::Transaction.module_eval do
211
+ add_method_tracer :commit, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/commit'
212
+ end
213
+ end
214
+ end
215
+
216
+
217
+ module NewRelic
218
+ module Agent
219
+ module Instrumentation
220
+ module DataMapperInstrumentation
221
+
222
+ def self.included(klass)
223
+ klass.class_eval do
224
+ alias_method :log_without_newrelic_instrumentation, :log
225
+ alias_method :log, :log_with_newrelic_instrumentation
226
+ end
227
+ end
228
+
229
+ # Unlike in AR, log is called in DM after the query actually ran,
230
+ # complete with metrics. Since DO has already calculated the
231
+ # duration, there's nothing more to measure, so just record and log.
232
+ #
233
+ # We rely on the assumption that all possible entry points have been
234
+ # hooked with tracers, ensuring that notice_sql attaches this SQL to
235
+ # the proper call scope.
236
+ def log_with_newrelic_instrumentation(msg)
237
+ return unless NewRelic::Agent.is_execution_traced?
238
+ return unless operation = case msg.query
239
+ when /^\s*select/i then 'find'
240
+ when /^\s*(update|insert)/i then 'save'
241
+ when /^\s*delete/i then 'destroy'
242
+ else nil
243
+ end
244
+
245
+ # FYI: self.to_s will yield connection URI string.
246
+ duration = msg.duration / 1000000.0
247
+
248
+ # Attach SQL to current segment/scope.
249
+ NewRelic::Agent.instance.transaction_sampler.notice_sql(msg.query, nil, duration)
250
+
251
+ # Record query duration associated with each of the desired metrics.
252
+ metrics = [ "ActiveRecord/#{operation}", 'ActiveRecord/all' ]
253
+ metrics.each do |metric|
254
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope(metric).trace_call(duration)
255
+ end
256
+ ensure
257
+ log_without_newrelic_instrumentation(msg)
258
+ end
259
+
260
+ end # DataMapperInstrumentation
261
+ end # Instrumentation
262
+ end # Agent
263
+ end # NewRelic
264
+
265
+ DependencyDetection.defer do
266
+
267
+ depends_on do
268
+ defined?(DataObjects) && defined?(DataObjects::Connection)
269
+ end
270
+
271
+ executes do
272
+ DataObjects::Connection.class_eval do
273
+ include ::NewRelic::Agent::Instrumentation::DataMapperInstrumentation
55
274
  end
56
275
  end
57
276
  end
@@ -43,6 +43,8 @@ module NewRelic
43
43
  def template_metric(identifier, options = {})
44
44
  if options[:file]
45
45
  "file"
46
+ elsif identifier.nil?
47
+ "(unknown)"
46
48
  elsif identifier.include? '/' # this is a filepath
47
49
  identifier.split('/')[-2..-1].join('/')
48
50
  else
@@ -148,7 +150,8 @@ DependencyDetection.defer do
148
150
  def render_with_newrelic(context, options)
149
151
  # This is needed for rails 3.2 compatibility
150
152
  @details = extract_details(options) if respond_to? :extract_details
151
- str = "View/#{NewRelic::Agent::Instrumentation::Rails3::ActionView::NewRelic.template_metric(determine_template(options).identifier, options)}/Rendering"
153
+ identifier = determine_template(options) ? determine_template(options).identifier : nil
154
+ str = "View/#{NewRelic::Agent::Instrumentation::Rails3::ActionView::NewRelic.template_metric(identifier, options)}/Rendering"
152
155
  trace_execution_scoped str do
153
156
  render_without_newrelic(context, options)
154
157
  end
@@ -163,7 +166,8 @@ DependencyDetection.defer do
163
166
 
164
167
  def render_with_newrelic(*args, &block)
165
168
  setup(*args, &block)
166
- str = "View/#{NewRelic::Agent::Instrumentation::Rails3::ActionView::NewRelic.template_metric(find_partial.identifier)}/Partial"
169
+ identifier = find_partial ? find_partial.identifier : nil
170
+ str = "View/#{NewRelic::Agent::Instrumentation::Rails3::ActionView::NewRelic.template_metric(identifier)}/Partial"
167
171
  trace_execution_scoped str do
168
172
  render_without_newrelic(*args, &block)
169
173
  end
@@ -41,6 +41,7 @@ module NewRelic
41
41
  if @sample.count_segments == @segment_limit
42
42
  NewRelic::Control.instance.log.debug("Segment limit of #{@segment_limit} reached, ceasing collection.")
43
43
  end
44
+ @current_segment
44
45
  end
45
46
  end
46
47
 
@@ -4,7 +4,7 @@ module NewRelic
4
4
  MAJOR = 3
5
5
  MINOR = 3
6
6
  TINY = 4
7
- BUILD = 'fork' # Set to nil for a release, 'beta1', 'alpha', etc for prerelease builds
7
+ BUILD = '1.fork' # Set to nil for a release, 'beta1', 'alpha', etc for prerelease builds
8
8
  STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.')
9
9
  end
10
10
 
@@ -6,6 +6,14 @@ class NewRelic::Agent::TransationSampleBuilderTest < Test::Unit::TestCase
6
6
  @builder = NewRelic::Agent::TransactionSampleBuilder.new
7
7
  end
8
8
 
9
+ # if it doesn't the core app tests will break. Not strictly necessary but
10
+ # we'll enforce it with this test for now.
11
+ def test_trace_entry_returns_segment
12
+ segment = @builder.trace_entry("/Foo/Bar", Time.now)
13
+ assert segment, "Segment should not be nil"
14
+ assert segment.is_a?(NewRelic::TransactionSample::Segment), "Segment should not be a #{segment.class.name}"
15
+ end
16
+
9
17
  def test_build_sample
10
18
  build_segment("a") do
11
19
  build_segment("aa") do
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dolores_rpm
3
3
  version: !ruby/object:Gem::Version
4
- hash: -101810583
5
- prerelease: 6
4
+ hash: 1250137308
5
+ prerelease: 8
6
6
  segments:
7
7
  - 3
8
8
  - 3
9
9
  - 4
10
+ - 1
10
11
  - fork
11
- version: 3.3.4.fork
12
+ version: 3.3.4.1.fork
12
13
  platform: ruby
13
14
  authors:
14
15
  - Bill Kayser
@@ -19,7 +20,7 @@ autorequire:
19
20
  bindir: bin
20
21
  cert_chain: []
21
22
 
22
- date: 2012-04-27 00:00:00 Z
23
+ date: 2012-05-10 00:00:00 Z
23
24
  dependencies:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: jeweler
@@ -65,8 +66,11 @@ dependencies:
65
66
  version_requirements: *id003
66
67
  description: |
67
68
  New Relic is a performance management system, developed by New Relic,
68
- Inc (http://www.newrelic.com). This is a fork that uses an older version
69
- of the DataMapper instrumentation. Newer versions did not work for us.
69
+ Inc (http://www.newrelic.com). New Relic provides you with deep
70
+ information about the performance of your web application as it runs
71
+ in production. The New Relic Ruby Agent is dual-purposed as a either a
72
+ Gem or plugin, hosted on
73
+ http://github.com/newrelic/rpm/
70
74
 
71
75
  email: support@newrelic.com
72
76
  executables:
@@ -90,6 +94,7 @@ files:
90
94
  - cert/cacert.pem
91
95
  - cert/oldsite.pem
92
96
  - cert/site.pem
97
+ - dolores_rpm.gemspec
93
98
  - install.rb
94
99
  - lib/conditional_vendored_dependency_detection.rb
95
100
  - lib/conditional_vendored_metric_parser.rb
@@ -190,7 +195,6 @@ files:
190
195
  - lib/tasks/install.rake
191
196
  - lib/tasks/tests.rake
192
197
  - newrelic.yml
193
- - dolores_rpm.gemspec
194
198
  - recipes/newrelic.rb
195
199
  - test/active_record_fixtures.rb
196
200
  - test/config/newrelic.yml