newrelic_rpm 2.13.2 → 2.13.3.beta

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (92) hide show
  1. data/CHANGELOG +18 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +10 -5
  4. data/lib/conditional_vendored_metric_parser.rb +5 -0
  5. data/lib/new_relic/agent.rb +0 -1
  6. data/lib/new_relic/agent/agent.rb +1 -1
  7. data/lib/new_relic/agent/error_collector.rb +2 -0
  8. data/lib/new_relic/agent/instrumentation/data_mapper.rb +170 -47
  9. data/lib/new_relic/agent/instrumentation/memcache.rb +3 -1
  10. data/lib/new_relic/agent/instrumentation/metric_frame.rb +1 -1
  11. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +6 -0
  12. data/lib/new_relic/agent/instrumentation/sinatra.rb +4 -3
  13. data/lib/new_relic/agent/stats_engine/metric_stats.rb +134 -98
  14. data/lib/new_relic/commands/install.rb +10 -0
  15. data/lib/new_relic/control.rb +13 -0
  16. data/lib/new_relic/control/configuration.rb +1 -1
  17. data/lib/new_relic/control/frameworks/rails.rb +9 -5
  18. data/lib/new_relic/control/frameworks/rails3.rb +3 -7
  19. data/lib/new_relic/control/instrumentation.rb +2 -2
  20. data/lib/new_relic/delayed_job_injection.rb +1 -1
  21. data/lib/new_relic/rack/developer_mode.rb +1 -1
  22. data/lib/new_relic/recipes.rb +1 -1
  23. data/lib/new_relic/transaction_analysis.rb +1 -1
  24. data/lib/new_relic/transaction_sample.rb +3 -1
  25. data/lib/new_relic/version.rb +2 -2
  26. data/newrelic_rpm.gemspec +50 -19
  27. data/test/new_relic/agent/memcache_instrumentation_test.rb +29 -25
  28. data/ui/helpers/developer_mode_helper.rb +1 -11
  29. data/ui/views/newrelic/_segment.rhtml +0 -1
  30. data/ui/views/newrelic/_segment_row.rhtml +1 -3
  31. data/ui/views/newrelic/index.rhtml +12 -0
  32. data/ui/views/newrelic/threads.rhtml +8 -7
  33. data/vendor/gems/metric_parser-0.1.0.pre1/LICENSE +0 -0
  34. data/vendor/gems/metric_parser-0.1.0.pre1/README +0 -0
  35. data/vendor/gems/metric_parser-0.1.0.pre1/lib/metric_parser.rb +1 -0
  36. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +64 -0
  37. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/action_mailer.rb +14 -0
  38. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_merchant.rb +31 -0
  39. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_record.rb +33 -0
  40. data/{lib → vendor/gems/metric_parser-0.1.0.pre1/lib}/new_relic/metric_parser/apdex.rb +12 -11
  41. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/background_transaction.rb +7 -0
  42. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/client.rb +46 -0
  43. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller.rb +67 -0
  44. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_cpu.rb +43 -0
  45. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_ext.rb +17 -0
  46. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database.rb +48 -0
  47. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database_pool.rb +24 -0
  48. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net.rb +28 -0
  49. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net_parser.rb +17 -0
  50. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/errors.rb +11 -0
  51. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/external.rb +55 -0
  52. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/frontend.rb +40 -0
  53. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/gc.rb +20 -0
  54. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/hibernate_session.rb +7 -0
  55. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java.rb +31 -0
  56. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java_parser.rb +17 -0
  57. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp.rb +34 -0
  58. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp_tag.rb +7 -0
  59. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +55 -0
  60. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +122 -0
  61. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/orm.rb +27 -0
  62. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/other_transaction.rb +40 -0
  63. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet.rb +7 -0
  64. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_context_listener.rb +7 -0
  65. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_filter.rb +7 -0
  66. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr.rb +27 -0
  67. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr_request_handler.rb +15 -0
  68. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring.rb +54 -0
  69. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_controller.rb +6 -0
  70. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_view.rb +6 -0
  71. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_action.rb +20 -0
  72. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_result.rb +20 -0
  73. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/version.rb +5 -0
  74. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +66 -0
  75. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_frontend.rb +18 -0
  76. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_service.rb +14 -0
  77. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_transaction.rb +133 -0
  78. metadata +88 -39
  79. data/lib/new_relic/metric_parser.rb +0 -136
  80. data/lib/new_relic/metric_parser/action_mailer.rb +0 -9
  81. data/lib/new_relic/metric_parser/active_merchant.rb +0 -26
  82. data/lib/new_relic/metric_parser/active_record.rb +0 -28
  83. data/lib/new_relic/metric_parser/controller.rb +0 -62
  84. data/lib/new_relic/metric_parser/controller_cpu.rb +0 -38
  85. data/lib/new_relic/metric_parser/errors.rb +0 -6
  86. data/lib/new_relic/metric_parser/external.rb +0 -50
  87. data/lib/new_relic/metric_parser/mem_cache.rb +0 -50
  88. data/lib/new_relic/metric_parser/other_transaction.rb +0 -36
  89. data/lib/new_relic/metric_parser/view.rb +0 -61
  90. data/lib/new_relic/metric_parser/web_frontend.rb +0 -14
  91. data/lib/new_relic/metric_parser/web_service.rb +0 -9
  92. data/test/new_relic/metric_parser_test.rb +0 -226
data/CHANGELOG CHANGED
@@ -1,3 +1,21 @@
1
+ v2.13.3
2
+ * Dalli instrumentation from Mike Perham (thanks Mike)
3
+ * Datamapper instrumentation from Jordan Ritter (thanks Jordan)
4
+ * Apdex now defaults to 0.5
5
+ !!! Please be aware that if you are not setting an apdex,
6
+ !!! this will cause a change in the apparent performance of your app.
7
+ * Make metric hashes threadsafe (fixes problems sending metrics in Jruby
8
+ threaded code)
9
+ * Delete obsolete links to metric docs in developer mode
10
+ * Detect gems when using Bundler
11
+ * Fix newrelic_ignore in Rails 3
12
+ * Break metric parser into a seperate vendored gem
13
+ * When using Unicorn, preload_app: true is recommended to get proper
14
+ after_fork behavior.
15
+
16
+ v2.13.2
17
+ * Remove a puts. Yes, a whole release for a puts.
18
+
1
19
  v2.13.1
2
20
  * Add missing require in rails 3 framework control
3
21
 
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2009 New Relic, Inc. All rights reserved.
1
+ Copyright (c) 2008-2010 New Relic, Inc. All rights reserved.
2
2
 
3
3
  Certain inventions disclosed in this file may be claimed within
4
4
  patents owned or patent applications filed by New Relic, Inc. or third
@@ -5,7 +5,7 @@ New Relic, Inc (http://www.newrelic.com). RPM provides you with deep
5
5
  information about the performance of your Ruby on Rails or Merb
6
6
  application as it runs in production. The New Relic Agent is
7
7
  dual-purposed as a either a Rails plugin or a Gem, hosted on
8
- github[http://github.com/newrelic/rpm/tree/master] and Rubyforge.
8
+ github[http://github.com/newrelic/rpm/tree/master].
9
9
 
10
10
  The New Relic Agent runs in one of two modes:
11
11
 
@@ -60,12 +60,17 @@ and then in a <tt>~/.newrelic</tt> directory
60
60
 
61
61
  === Rails Installation
62
62
 
63
- You can install the agent as a plug-in or a Gem. To install the agent
64
- as a Rails plug-in, do the following:
63
+ You can install the agent as a Gem:
65
64
 
66
- script/plugin install http://newrelic.rubyforge.org/svn/newrelic_rpm
65
+ For Bundler:
66
+
67
+ Add the following line to your Gemfile:
68
+
69
+ gem 'newrelic_rpm'
70
+
71
+ For Rails 2.x without Bundler:
67
72
 
68
- If using the Agent as a gem, edit +environment.rb+ and add to the initalizer block:
73
+ edit +environment.rb+ and add to the initalizer block:
69
74
 
70
75
  config.gem "newrelic_rpm"
71
76
 
@@ -0,0 +1,5 @@
1
+ unless defined?(NewRelic) && defined?(NewRelic::MetricParser)
2
+ vendored_metric_parser = File.expand_path('../../vendor/gems/metric_parser-0.1.0.pre1/lib/', __FILE__)
3
+ $:.unshift vendored_metric_parser unless $:.include?(vendored_metric_parser)
4
+ require 'metric_parser'
5
+ end
@@ -63,7 +63,6 @@ module NewRelic
63
63
  require 'new_relic/metrics'
64
64
  require 'new_relic/metric_spec'
65
65
  require 'new_relic/metric_data'
66
- require 'new_relic/metric_parser'
67
66
  require 'new_relic/collection_helper'
68
67
  require 'new_relic/transaction_analysis'
69
68
  require 'new_relic/transaction_sample'
@@ -67,7 +67,7 @@ module NewRelic
67
67
  metric = options['metric']
68
68
  metric ||= options['uri'] # normalize this with url rules
69
69
  raise "metric or uri arguments required" unless metric
70
- metric_info = NewRelic::MetricParser.for_metric_named(metric)
70
+ metric_info = NewRelic::MetricParser::MetricParser.for_metric_named(metric)
71
71
 
72
72
  if metric_info.is_web_transaction?
73
73
  NewRelic::Agent::Instrumentation::MetricFrame.record_apdex(metric_info, duration_seconds, duration_seconds, is_error)
@@ -105,6 +105,8 @@ module NewRelic
105
105
  end
106
106
  end
107
107
  exception
108
+ rescue Exception => e
109
+ log.error("Error capturing an error, yodawg. #{e}")
108
110
  end
109
111
 
110
112
  # Get the errors currently queued up. Unsent errors are left
@@ -1,58 +1,181 @@
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 RPM 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.
1
39
 
2
- # NewRelic instrumentation for DataMapper
3
- # For now, we have to refer to all db metrics as "ActiveRecord"
4
- if defined? DataMapper
40
+ if defined? ::DataMapper
5
41
 
6
- DataMapper::Model.class_eval do
7
- add_method_tracer :get, 'ActiveRecord/#{self.name}/find'
8
- add_method_tracer :first, 'ActiveRecord/#{self.name}/find'
9
- add_method_tracer :first_or_create, 'ActiveRecord/#{self.name}/find'
10
- add_method_tracer :all, 'ActiveRecord/#{self.name}/find_all'
11
- end
12
- DataMapper::Resource.class_eval do
42
+ # DM::Model class methods
43
+ ::DataMapper::Model.class_eval do
13
44
 
14
- @@my_sql_defined = defined? ActiveRecord::ConnectionAdapters::MysqlAdapter
15
- @@postgres_defined = defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
45
+ add_method_tracer :get, 'ActiveRecord/#{self.name}/get'
46
+ add_method_tracer :first, 'ActiveRecord/#{self.name}/first'
47
+ add_method_tracer :last, 'ActiveRecord/#{self.name}/last'
48
+ add_method_tracer :all, 'ActiveRecord/#{self.name}/all'
16
49
 
17
- for method in [:query] do
18
- add_method_tracer method, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/execute'
19
- add_method_tracer method, 'ActiveRecord/all', :push_scope => false
20
- end
21
- for method in [:update, :save] do
22
- add_method_tracer method, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
23
- add_method_tracer method, 'ActiveRecord/save', :push_scope => false
24
- end
25
- add_method_tracer :destroy, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
26
- add_method_tracer :destroy, 'ActiveRecord/destroy', :push_scope => false
27
-
28
- def log_with_newrelic_instrumentation(sql, name, &block)
29
- # if we aren't in a blamed context, then add one so that we can
30
- # see that controllers are calling SQL directly we check
31
- # scope_depth vs 2 since the controller is 1
32
- if NewRelic::Agent.instance.transaction_sampler.scope_depth < 2
33
- self.class.trace_method_execution "Database/DirectSQL", true, true do
34
- log_with_capture_sql(sql, name, &block)
35
- end
36
- else
37
- log_with_capture_sql(sql, name, &block)
38
- end
50
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
51
+ add_method_tracer :create!, 'ActiveRecord/#{self.name}/create'
52
+ add_method_tracer :update, 'ActiveRecord/#{self.name}/update'
53
+ add_method_tracer :update!, 'ActiveRecord/#{self.name}/update'
54
+ add_method_tracer :destroy, 'ActiveRecord/#{self.name}/destroy'
55
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.name}/destroy'
56
+
57
+ # For dm-aggregates and partial dm-ar-finders support:
58
+ for method in [ :aggregate, :find, :find_by_sql ] do
59
+ next unless method_defined? method
60
+ add_method_tracer(method, 'ActiveRecord/#{self.name}/' + method.to_s)
39
61
  end
40
62
 
41
- def log_with_capture_sql(sql, name, &block)
42
- if @@my_sql_defined && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
43
- config = @config
44
- elsif @@postgres_defined && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
45
- config = @config
46
- else
47
- config = nil
48
- end
63
+ end
64
+
65
+ # DM's Model instance (Resource) methods
66
+ ::DataMapper::Resource.class_eval do
67
+
68
+ add_method_tracer :update, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/update'
69
+ add_method_tracer :update!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/update'
70
+ add_method_tracer :save, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
71
+ add_method_tracer :save!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/save'
72
+ add_method_tracer :destroy, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
73
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/destroy'
49
74
 
50
- t0 = Time.now
51
- result = log_without_newrelic_instrumentation(sql, name, &block)
75
+ end
76
+
77
+ # DM's Collection instance methods
78
+ ::DataMapper::Collection.class_eval do
79
+
80
+ add_method_tracer :get, 'ActiveRecord/#{self.name}/get'
81
+ add_method_tracer :first, 'ActiveRecord/#{self.name}/first'
82
+ add_method_tracer :last, 'ActiveRecord/#{self.name}/last'
83
+ add_method_tracer :all, 'ActiveRecord/#{self.name}/all'
84
+
85
+ add_method_tracer :lazy_load, 'ActiveRecord/#{self.name}/lazy_load'
52
86
 
53
- NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, config, (Time.now - t0).to_f)
87
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
88
+ add_method_tracer :create!, 'ActiveRecord/#{self.name}/create'
89
+ add_method_tracer :update, 'ActiveRecord/#{self.name}/update'
90
+ add_method_tracer :update!, 'ActiveRecord/#{self.name}/update'
91
+ add_method_tracer :destroy, 'ActiveRecord/#{self.name}/destroy'
92
+ add_method_tracer :destroy!, 'ActiveRecord/#{self.name}/destroy'
54
93
 
55
- result
94
+ # For dm-aggregates support:
95
+ for method in [ :aggregate ] do
96
+ next unless method_defined? method
97
+ add_method_tracer(method, 'ActiveRecord/#{self.name}/' + method.to_s)
56
98
  end
99
+
57
100
  end
58
- end
101
+
102
+ # Catch the two entry points into DM::Repository::Adapter that bypass CRUD
103
+ # (for when SQL is run directly).
104
+ ::DataMapper::Adapters::DataObjectsAdapter.class_eval do
105
+
106
+ add_method_tracer :select, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/select'
107
+ add_method_tracer :execute, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/execute'
108
+
109
+ end if defined? ::DataMapper::Adapters::DataObjectsAdapter
110
+
111
+ # DM::Validations overrides Model#create, but currently in a way that makes it
112
+ # impossible to instrument from one place. I've got a patch pending inclusion
113
+ # to make it instrumentable by putting the create method inside ClassMethods.
114
+ # This will pick it up if/when that patch is accepted.
115
+ ::DataMapper::Validations::ClassMethods.class_eval do
116
+
117
+ next unless method_defined? :create
118
+ add_method_tracer :create, 'ActiveRecord/#{self.name}/create'
119
+
120
+ end if defined? ::DataMapper::Validations::ClassMethods
121
+
122
+ # NOTE: DM::Transaction basically calls commit() twice, so as-is it will show
123
+ # up in traces twice -- second time subordinate to the first's scope. Works
124
+ # well enough.
125
+ ::DataMapper::Transaction.module_eval do
126
+ add_method_tracer :commit, 'ActiveRecord/#{self.class.name[/[^:]*$/]}/commit'
127
+ end if defined? ::DataMapper::Transaction
128
+
129
+ module NewRelic
130
+ module Agent
131
+ module Instrumentation
132
+ module DataMapperInstrumentation
133
+
134
+ def self.included(klass)
135
+ klass.class_eval do
136
+ alias_method :log_without_newrelic_instrumentation, :log
137
+ alias_method :log, :log_with_newrelic_instrumentation
138
+ end
139
+ end
140
+
141
+ # Unlike in AR, log is called in DM after the query actually ran,
142
+ # complete with metrics. Since DO has already calculated the
143
+ # duration, there's nothing more to measure, so just record and log.
144
+ #
145
+ # We rely on the assumption that all possible entry points have been
146
+ # hooked with tracers, ensuring that notice_sql attaches this SQL to
147
+ # the proper call scope.
148
+ def log_with_newrelic_instrumentation(msg)
149
+ return unless NewRelic::Agent.is_execution_traced?
150
+ return unless operation = case msg.query
151
+ when /^\s*select/i then 'find'
152
+ when /^\s*(update|insert)/i then 'save'
153
+ when /^\s*delete/i then 'destroy'
154
+ else nil
155
+ end
156
+
157
+ # FYI: self.to_s will yield connection URI string.
158
+ duration = msg.duration / 1000000.0
159
+
160
+ # Attach SQL to current segment/scope.
161
+ NewRelic::Agent.instance.transaction_sampler.notice_sql(msg.query, nil, duration)
162
+
163
+ # Record query duration associated with each of the desired metrics.
164
+ metrics = [ "ActiveRecord/#{operation}", 'ActiveRecord/all' ]
165
+ metrics.each do |metric|
166
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope(metric).trace_call(duration)
167
+ end
168
+ ensure
169
+ log_without_newrelic_instrumentation(msg)
170
+ end
171
+
172
+ end # DataMapperInstrumentation
173
+ end # Instrumentation
174
+ end # Agent
175
+ end # NewRelic
176
+
177
+ ::DataObjects::Connection.class_eval do
178
+ include ::NewRelic::Agent::Instrumentation::DataMapperInstrumentation
179
+ end if defined? ::DataObjects::Connection
180
+
181
+ end # if defined? DataMapper
@@ -3,6 +3,7 @@
3
3
  # See:
4
4
  # http://www.deveiate.org/code/Ruby-MemCache/ (Gem: Ruby-MemCache)
5
5
  # http://seattlerb.rubyforge.org/memcache-client/ (Gem: memcache-client)
6
+ # http://github.com/mperham/dalli (Gem: dalli)
6
7
  unless NewRelic::Control.instance['disable_memcache_instrumentation']
7
8
 
8
9
  def self.instrument_method(the_class, method_name)
@@ -32,9 +33,10 @@ unless NewRelic::Control.instance['disable_memcache_instrumentation']
32
33
 
33
34
 
34
35
 
35
- %w[get get_multi set add incr decr delete replace append prepand cas].each do | method_name |
36
+ %w[get get_multi set add incr decr delete replace append prepend cas].each do | method_name |
36
37
  instrument_method(::MemCache, method_name) if defined? ::MemCache
37
38
  instrument_method(::Memcached, method_name) if defined? ::Memcached
39
+ instrument_method(::Dalli::Client, method_name) if defined? ::Dalli::Client
38
40
  end
39
41
 
40
42
  end
@@ -75,7 +75,7 @@ module NewRelic
75
75
  # Make sure you unwind every push with a pop call.
76
76
  def push(m)
77
77
  NewRelic::Agent.instance.transaction_sampler.notice_first_scope_push(start)
78
- @path_stack.push NewRelic::MetricParser.for_metric_named(m)
78
+ @path_stack.push NewRelic::MetricParser::MetricParser.for_metric_named(m)
79
79
  end
80
80
 
81
81
  # Indicate that you don't want to keep the currently saved transaction
@@ -23,6 +23,12 @@ module NewRelic
23
23
  end
24
24
 
25
25
  def process_action(*args)
26
+ # skip instrumentation if we are in an ignored action
27
+ if _is_filtered?('do_not_trace')
28
+ NewRelic::Agent.disable_all_tracing do
29
+ return super
30
+ end
31
+ end
26
32
 
27
33
  perform_action_with_newrelic_trace(:category => :controller, :name => self.action_name, :params => request.filtered_parameters, :class_name => self.class.name) do
28
34
  super
@@ -25,10 +25,11 @@ if defined?(Sinatra::Base)
25
25
  end
26
26
  end
27
27
  end
28
- # strip of leading ^ and / chars and trailing $ and /
29
- name.gsub!(%r{^[/^]*(.*?)[/\$]*$}, '\1')
28
+ # strip off leading ^ and / chars and trailing $ and /
29
+ name.gsub!(%r{^[/^]*(.*?)[/\$\?]*$}, '\1')
30
30
  name = 'root' if name.empty?
31
- perform_action_with_newrelic_trace(:category => :sinatra, :name => name) do
31
+ name = @request.request_method + ' ' + name if @request && @request.respond_to?(:request_method)
32
+ perform_action_with_newrelic_trace(:category => :sinatra, :name => name, :params => @request.params) do
32
33
  route_eval_without_newrelic(&block_arg)
33
34
  end
34
35
  end
@@ -1,118 +1,154 @@
1
1
  module NewRelic
2
- module Agent
3
- class StatsEngine
4
- module MetricStats
5
- # The stats hash hashes either a metric name for an unscoped metric,
6
- # or a metric_spec for a scoped metric value.
7
- def lookup_stat(metric_name)
8
- stats_hash[metric_name]
9
- end
10
-
11
- def metrics
12
- stats_hash.keys.map(&:to_s)
13
- end
14
-
15
- def get_stats_no_scope(metric_name)
16
- stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
17
- end
18
-
19
- # This version allows a caller to pass a stat class to use
20
- #
21
- def get_custom_stats(metric_name, stat_class)
22
- stats_hash[metric_name] ||= stat_class.new
23
- end
24
-
25
- # If use_scope is true, two chained metrics are created, one with scope and one without
26
- # If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
27
- def get_stats(metric_name, use_scope = true, scoped_metric_only = false, scope = nil)
28
- scope ||= scope_name if use_scope
29
- if scoped_metric_only
30
- spec = NewRelic::MetricSpec.new metric_name, scope
31
- stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
32
- else
33
- stats = stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
34
- if scope && scope != metric_name
35
- spec = NewRelic::MetricSpec.new metric_name, scope
36
- scoped_stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
37
- stats = scoped_stats
2
+ module Agent
3
+ class StatsEngine
4
+ module MetricStats
5
+ class SynchronizedHash < Hash
6
+ def initialize(*args)
7
+ @mutex = Mutex.new
8
+ super
38
9
  end
39
- end
40
- stats
41
- end
42
-
43
- def lookup_stats(metric_name, scope_name = nil)
44
- stats_hash[NewRelic::MetricSpec.new(metric_name, scope_name)] ||
45
- stats_hash[metric_name]
46
- end
47
- # Harvest the timeslice data. First recombine current statss
48
- # with any previously
49
- # unsent metrics, clear out stats cache, and return the current
50
- # stats.
51
- # ---
52
- # Note: this is not synchronized. There is still some risk in this and
53
- # we will revisit later to see if we can make this more robust without
54
- # sacrificing efficiency.
55
- # +++
56
- def harvest_timeslice_data(previous_timeslice_data, metric_ids)
57
- timeslice_data = {}
58
- poll harvest_samplers
59
- stats_hash.keys.each do | metric_spec |
60
-
61
-
62
- # get a copy of the stats collected since the last harvest, and clear
63
- # the stats inside our hash table for the next time slice.
64
- stats = stats_hash[metric_spec]
65
-
66
- # we have an optimization for unscoped metrics
67
- if !(metric_spec.is_a? NewRelic::MetricSpec)
68
- metric_spec = NewRelic::MetricSpec.new metric_spec
10
+
11
+ def []=(*args)
12
+ @mutex.synchronize {
13
+ super
14
+ }
69
15
  end
70
16
 
71
- if stats.nil?
72
- raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
17
+ def [](*args)
18
+ @mutex.synchronize {
19
+ super
20
+ }
73
21
  end
74
22
 
75
- stats_copy = stats.clone
76
- stats.reset
23
+ def clear(*args)
24
+ @mutex.synchronize {
25
+ super
26
+ }
27
+ end
77
28
 
78
- # if the previous timeslice data has not been reported (due to an error of some sort)
79
- # then we need to merge this timeslice with the previously accumulated - but not sent
80
- # data
81
- previous_metric_data = previous_timeslice_data[metric_spec]
82
- stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
83
- stats_copy.round!
29
+ def delete(*args)
30
+ @mutex.synchronize {
31
+ super
32
+ }
33
+ end
34
+
35
+ def delete_if(*args)
36
+ @mutex.synchronize {
37
+ super
38
+ }
39
+ end
40
+ end
41
+
42
+ # The stats hash hashes either a metric name for an unscoped metric,
43
+ # or a metric_spec for a scoped metric value.
44
+ def lookup_stat(metric_name)
45
+ stats_hash[metric_name]
46
+ end
84
47
 
85
- # don't bother collecting and reporting stats that have zero-values for this timeslice.
86
- # significant performance boost and storage savings.
87
- unless stats_copy.is_reset?
48
+ def metrics
49
+ stats_hash.keys.map(&:to_s)
50
+ end
88
51
 
89
- id = metric_ids[metric_spec]
90
- metric_spec_for_transport = id ? nil : metric_spec
52
+ def get_stats_no_scope(metric_name)
53
+ stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
54
+ end
91
55
 
92
- metric_data = NewRelic::MetricData.new(metric_spec_for_transport, stats_copy, id)
56
+ # This version allows a caller to pass a stat class to use
57
+ #
58
+ def get_custom_stats(metric_name, stat_class)
59
+ stats_hash[metric_name] ||= stat_class.new
60
+ end
93
61
 
94
- timeslice_data[metric_spec] = metric_data
95
- end
62
+ # If use_scope is true, two chained metrics are created, one with scope and one without
63
+ # If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
64
+ def get_stats(metric_name, use_scope = true, scoped_metric_only = false, scope = nil)
65
+ scope ||= scope_name if use_scope
66
+ if scoped_metric_only
67
+ spec = NewRelic::MetricSpec.new metric_name, scope
68
+ stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
69
+ else
70
+ stats = stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
71
+ if scope && scope != metric_name
72
+ spec = NewRelic::MetricSpec.new metric_name, scope
73
+ stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
74
+ end
75
+ end
76
+ stats
96
77
  end
97
78
 
98
- timeslice_data
99
- end
79
+ def lookup_stats(metric_name, scope_name = nil)
80
+ stats_hash[NewRelic::MetricSpec.new(metric_name, scope_name)] ||
81
+ stats_hash[metric_name]
82
+ end
83
+ # Harvest the timeslice data. First recombine current statss
84
+ # with any previously
85
+ # unsent metrics, clear out stats cache, and return the current
86
+ # stats.
87
+ # ---
88
+ # Note: this is not synchronized. There is still some risk in this and
89
+ # we will revisit later to see if we can make this more robust without
90
+ # sacrificing efficiency.
91
+ # +++
92
+ def harvest_timeslice_data(previous_timeslice_data, metric_ids)
93
+ timeslice_data = {}
94
+ poll harvest_samplers
95
+ stats_hash.keys.each do | metric_spec |
96
+
97
+
98
+ # get a copy of the stats collected since the last harvest, and clear
99
+ # the stats inside our hash table for the next time slice.
100
+ stats = stats_hash[metric_spec]
101
+
102
+ # we have an optimization for unscoped metrics
103
+ if !(metric_spec.is_a? NewRelic::MetricSpec)
104
+ metric_spec = NewRelic::MetricSpec.new metric_spec
105
+ end
106
+
107
+ if stats.nil?
108
+ raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
109
+ end
110
+
111
+ stats_copy = stats.clone
112
+ stats.reset
113
+
114
+ # if the previous timeslice data has not been reported (due to an error of some sort)
115
+ # then we need to merge this timeslice with the previously accumulated - but not sent
116
+ # data
117
+ previous_metric_data = previous_timeslice_data[metric_spec]
118
+ stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
119
+ stats_copy.round!
120
+
121
+ # don't bother collecting and reporting stats that have zero-values for this timeslice.
122
+ # significant performance boost and storage savings.
123
+ unless stats_copy.is_reset?
124
+
125
+ id = metric_ids[metric_spec]
126
+ metric_spec_for_transport = id ? nil : metric_spec
127
+
128
+ metric_data = NewRelic::MetricData.new(metric_spec_for_transport, stats_copy, id)
129
+
130
+ timeslice_data[metric_spec] = metric_data
131
+ end
132
+ end
133
+
134
+ timeslice_data
135
+ end
100
136
 
101
- # Remove all stats. For test code only.
102
- def clear_stats
103
- stats_hash.clear
104
- NewRelic::Agent::BusyCalculator.reset
105
- end
137
+ # Remove all stats. For test code only.
138
+ def clear_stats
139
+ stats_hash.clear
140
+ NewRelic::Agent::BusyCalculator.reset
141
+ end
106
142
 
107
- # Reset each of the stats, such as when a new passenger instance starts up.
108
- def reset_stats
109
- stats_hash.values.each { |s| s.reset }
110
- end
143
+ # Reset each of the stats, such as when a new passenger instance starts up.
144
+ def reset_stats
145
+ stats_hash.values.each { |s| s.reset }
146
+ end
111
147
 
112
- def stats_hash
113
- @stats_hash ||= {}
148
+ def stats_hash
149
+ @stats_hash ||= SynchronizedHash.new
150
+ end
114
151
  end
115
152
  end
116
153
  end
117
154
  end
118
- end