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.
- data/CHANGELOG +18 -0
- data/LICENSE +1 -1
- data/README.rdoc +10 -5
- data/lib/conditional_vendored_metric_parser.rb +5 -0
- data/lib/new_relic/agent.rb +0 -1
- data/lib/new_relic/agent/agent.rb +1 -1
- data/lib/new_relic/agent/error_collector.rb +2 -0
- data/lib/new_relic/agent/instrumentation/data_mapper.rb +170 -47
- data/lib/new_relic/agent/instrumentation/memcache.rb +3 -1
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +1 -1
- data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +6 -0
- data/lib/new_relic/agent/instrumentation/sinatra.rb +4 -3
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +134 -98
- data/lib/new_relic/commands/install.rb +10 -0
- data/lib/new_relic/control.rb +13 -0
- data/lib/new_relic/control/configuration.rb +1 -1
- data/lib/new_relic/control/frameworks/rails.rb +9 -5
- data/lib/new_relic/control/frameworks/rails3.rb +3 -7
- data/lib/new_relic/control/instrumentation.rb +2 -2
- data/lib/new_relic/delayed_job_injection.rb +1 -1
- data/lib/new_relic/rack/developer_mode.rb +1 -1
- data/lib/new_relic/recipes.rb +1 -1
- data/lib/new_relic/transaction_analysis.rb +1 -1
- data/lib/new_relic/transaction_sample.rb +3 -1
- data/lib/new_relic/version.rb +2 -2
- data/newrelic_rpm.gemspec +50 -19
- data/test/new_relic/agent/memcache_instrumentation_test.rb +29 -25
- data/ui/helpers/developer_mode_helper.rb +1 -11
- data/ui/views/newrelic/_segment.rhtml +0 -1
- data/ui/views/newrelic/_segment_row.rhtml +1 -3
- data/ui/views/newrelic/index.rhtml +12 -0
- data/ui/views/newrelic/threads.rhtml +8 -7
- data/vendor/gems/metric_parser-0.1.0.pre1/LICENSE +0 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/README +0 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/metric_parser.rb +1 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +64 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/action_mailer.rb +14 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_merchant.rb +31 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_record.rb +33 -0
- data/{lib → vendor/gems/metric_parser-0.1.0.pre1/lib}/new_relic/metric_parser/apdex.rb +12 -11
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/background_transaction.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/client.rb +46 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller.rb +67 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_cpu.rb +43 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_ext.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database.rb +48 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database_pool.rb +24 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net.rb +28 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net_parser.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/errors.rb +11 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/external.rb +55 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/frontend.rb +40 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/gc.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/hibernate_session.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java.rb +31 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java_parser.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp.rb +34 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp_tag.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +55 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +122 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/orm.rb +27 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/other_transaction.rb +40 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_context_listener.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_filter.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr.rb +27 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr_request_handler.rb +15 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring.rb +54 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_controller.rb +6 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_view.rb +6 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_action.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_result.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/version.rb +5 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +66 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_frontend.rb +18 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_service.rb +14 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_transaction.rb +133 -0
- metadata +88 -39
- data/lib/new_relic/metric_parser.rb +0 -136
- data/lib/new_relic/metric_parser/action_mailer.rb +0 -9
- data/lib/new_relic/metric_parser/active_merchant.rb +0 -26
- data/lib/new_relic/metric_parser/active_record.rb +0 -28
- data/lib/new_relic/metric_parser/controller.rb +0 -62
- data/lib/new_relic/metric_parser/controller_cpu.rb +0 -38
- data/lib/new_relic/metric_parser/errors.rb +0 -6
- data/lib/new_relic/metric_parser/external.rb +0 -50
- data/lib/new_relic/metric_parser/mem_cache.rb +0 -50
- data/lib/new_relic/metric_parser/other_transaction.rb +0 -36
- data/lib/new_relic/metric_parser/view.rb +0 -61
- data/lib/new_relic/metric_parser/web_frontend.rb +0 -14
- data/lib/new_relic/metric_parser/web_service.rb +0 -9
- 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
data/README.rdoc
CHANGED
@@ -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]
|
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
|
64
|
-
as a Rails plug-in, do the following:
|
63
|
+
You can install the agent as a Gem:
|
65
64
|
|
66
|
-
|
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
|
-
|
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
|
data/lib/new_relic/agent.rb
CHANGED
@@ -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)
|
@@ -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
|
-
|
3
|
-
# For now, we have to refer to all db metrics as "ActiveRecord"
|
4
|
-
if defined? DataMapper
|
40
|
+
if defined? ::DataMapper
|
5
41
|
|
6
|
-
|
7
|
-
|
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
|
-
|
15
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
29
|
-
name.gsub!(%r{^[/^]*(.*?)[
|
28
|
+
# strip off leading ^ and / chars and trailing $ and /
|
29
|
+
name.gsub!(%r{^[/^]*(.*?)[/\$\?]*$}, '\1')
|
30
30
|
name = 'root' if name.empty?
|
31
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
72
|
-
|
17
|
+
def [](*args)
|
18
|
+
@mutex.synchronize {
|
19
|
+
super
|
20
|
+
}
|
73
21
|
end
|
74
22
|
|
75
|
-
|
76
|
-
|
23
|
+
def clear(*args)
|
24
|
+
@mutex.synchronize {
|
25
|
+
super
|
26
|
+
}
|
27
|
+
end
|
77
28
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
48
|
+
def metrics
|
49
|
+
stats_hash.keys.map(&:to_s)
|
50
|
+
end
|
88
51
|
|
89
|
-
|
90
|
-
|
52
|
+
def get_stats_no_scope(metric_name)
|
53
|
+
stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
|
54
|
+
end
|
91
55
|
|
92
|
-
|
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
|
-
|
95
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
113
|
-
|
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
|