dolores_rpm 3.3.4.fork → 3.3.4.1.fork

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 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