newrelic_rpm 2.8.11 → 2.9.2
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 +267 -0
- data/LICENSE +1 -1
- data/Manifest +142 -0
- data/README.md +138 -0
- data/Rakefile +10 -28
- data/bin/mongrel_rpm +33 -0
- data/cert/cacert.pem +34 -0
- data/init.rb +38 -0
- data/lib/new_relic/agent/agent.rb +160 -347
- data/lib/new_relic/agent/collection_helper.rb +13 -24
- data/lib/new_relic/agent/error_collector.rb +29 -15
- data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +63 -76
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +90 -48
- data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +72 -47
- data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +14 -0
- data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -1
- data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +5 -7
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +3 -1
- data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +7 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +34 -7
- data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +20 -12
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -4
- data/lib/new_relic/agent/method_tracer.rb +159 -135
- data/lib/new_relic/agent/patch_const_missing.rb +46 -26
- data/lib/new_relic/agent/sampler.rb +12 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +44 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +126 -0
- data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
- data/lib/new_relic/agent/shim_agent.rb +11 -0
- data/lib/new_relic/agent/stats_engine.rb +85 -46
- data/lib/new_relic/agent/transaction_sampler.rb +63 -38
- data/lib/new_relic/agent/worker_loop.rb +8 -18
- data/lib/new_relic/agent.rb +200 -25
- data/lib/new_relic/commands/deployments.rb +9 -9
- data/lib/new_relic/control/merb.rb +22 -0
- data/lib/new_relic/control/rails.rb +141 -0
- data/lib/new_relic/{config → control}/ruby.rb +13 -2
- data/lib/new_relic/control.rb +424 -0
- data/lib/new_relic/local_environment.rb +201 -79
- data/lib/new_relic/metric_data.rb +7 -0
- data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
- data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
- data/lib/new_relic/metric_parser/active_record.rb +11 -0
- data/lib/new_relic/metric_parser/controller.rb +51 -0
- data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
- data/lib/new_relic/metric_parser/database.rb +23 -0
- data/lib/new_relic/metric_parser/errors.rb +6 -0
- data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
- data/lib/new_relic/metric_parser/view.rb +61 -0
- data/lib/new_relic/metric_parser/web_service.rb +9 -0
- data/lib/new_relic/metric_parser.rb +107 -0
- data/lib/new_relic/metric_spec.rb +5 -0
- data/lib/new_relic/noticed_error.rb +5 -1
- data/lib/new_relic/rack/metric_app.rb +57 -0
- data/lib/new_relic/rack/newrelic.ru +25 -0
- data/lib/new_relic/rack/newrelic.yml +25 -0
- data/lib/new_relic/rack.rb +5 -0
- data/lib/new_relic/recipes.rb +10 -3
- data/lib/new_relic/stats.rb +130 -144
- data/lib/new_relic/transaction_analysis.rb +7 -8
- data/lib/new_relic/transaction_sample.rb +86 -10
- data/lib/new_relic/version.rb +41 -160
- data/lib/new_relic_api.rb +7 -6
- data/lib/newrelic_rpm.rb +30 -17
- data/lib/tasks/{agent_tests.rake → tests.rake} +1 -1
- data/newrelic.yml +115 -62
- data/newrelic_rpm.gemspec +36 -0
- data/test/active_record_fixtures.rb +55 -0
- data/test/config/newrelic.yml +21 -3
- data/test/config/{test_config.rb → test_control.rb} +14 -10
- data/test/new_relic/agent/active_record_instrumentation_test.rb +189 -0
- data/test/new_relic/agent/agent_test.rb +104 -0
- data/test/new_relic/agent/agent_test_controller.rb +18 -1
- data/test/new_relic/agent/classloader_patch_test.rb +56 -0
- data/test/new_relic/agent/{tc_collection_helper.rb → collection_helper_test.rb} +28 -23
- data/test/new_relic/agent/controller_test.rb +107 -0
- data/test/new_relic/agent/dispatcher_instrumentation_test.rb +70 -0
- data/test/new_relic/agent/error_collector_test.rb +155 -0
- data/test/new_relic/agent/{tc_method_tracer.rb → method_tracer_test.rb} +6 -12
- data/test/new_relic/agent/metric_data_test.rb +56 -0
- data/test/new_relic/agent/stats_engine_test.rb +266 -0
- data/test/new_relic/agent/{tc_transaction_sample_builder.rb → transaction_sample_builder_test.rb} +6 -5
- data/test/new_relic/agent/{tc_transaction_sample.rb → transaction_sample_test.rb} +9 -13
- data/test/new_relic/agent/transaction_sampler_test.rb +317 -0
- data/test/new_relic/agent/{tc_worker_loop.rb → worker_loop_test.rb} +1 -1
- data/test/new_relic/control_test.rb +97 -0
- data/test/new_relic/{tc_deployments_api.rb → deployments_api_test.rb} +8 -4
- data/test/new_relic/environment_test.rb +75 -0
- data/test/new_relic/metric_parser_test.rb +142 -0
- data/test/new_relic/{tc_metric_spec.rb → metric_spec_test.rb} +28 -1
- data/test/new_relic/samplers_test.rb +71 -0
- data/test/new_relic/{tc_shim_agent.rb → shim_agent_test.rb} +1 -1
- data/test/new_relic/stats_test.rb +291 -0
- data/test/new_relic/version_number_test.rb +46 -0
- data/test/test_helper.rb +7 -30
- data/test/ui/newrelic_controller_test.rb +14 -0
- data/test/ui/{tc_newrelic_helper.rb → newrelic_helper_test.rb} +16 -7
- data/ui/controllers/newrelic_controller.rb +17 -3
- data/ui/helpers/newrelic_helper.rb +44 -15
- data/ui/views/layouts/newrelic_default.rhtml +7 -8
- data/ui/views/newrelic/_sample.rhtml +5 -2
- data/ui/views/newrelic/_segment.rhtml +1 -1
- data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
- data/ui/views/newrelic/_segment_row.rhtml +4 -4
- data/ui/views/newrelic/_show_sample_detail.rhtml +3 -1
- data/ui/views/newrelic/_show_sample_sql.rhtml +2 -1
- data/ui/views/newrelic/explain_sql.rhtml +2 -5
- data/ui/views/newrelic/images/file_icon.png +0 -0
- data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
- data/ui/views/newrelic/index.rhtml +21 -13
- data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
- data/ui/views/newrelic/show_sample.rhtml +18 -3
- data/ui/views/newrelic/stylesheets/style.css +39 -0
- data/ui/views/newrelic/threads.rhtml +52 -0
- metadata +192 -70
- data/README +0 -136
- data/lib/new_relic/agent/instrumentation/rails/rails.rb +0 -6
- data/lib/new_relic/agent/samplers/cpu.rb +0 -29
- data/lib/new_relic/agent/samplers/memory.rb +0 -53
- data/lib/new_relic/agent/samplers/mongrel.rb +0 -26
- data/lib/new_relic/agent/synchronize.rb +0 -40
- data/lib/new_relic/config/merb.rb +0 -35
- data/lib/new_relic/config/rails.rb +0 -114
- data/lib/new_relic/config.rb +0 -279
- data/lib/new_relic/shim_agent.rb +0 -96
- data/test/new_relic/agent/model_fixture.rb +0 -15
- data/test/new_relic/agent/tc_active_record.rb +0 -90
- data/test/new_relic/agent/tc_agent.rb +0 -148
- data/test/new_relic/agent/tc_controller.rb +0 -77
- data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +0 -52
- data/test/new_relic/agent/tc_error_collector.rb +0 -127
- data/test/new_relic/agent/tc_stats_engine.rb +0 -218
- data/test/new_relic/agent/tc_synchronize.rb +0 -37
- data/test/new_relic/agent/tc_transaction_sampler.rb +0 -302
- data/test/new_relic/tc_config.rb +0 -36
- data/test/new_relic/tc_environment.rb +0 -94
- data/test/new_relic/tc_stats.rb +0 -141
@@ -0,0 +1,38 @@
|
|
1
|
+
class NewRelic::MetricParser::ControllerCPU < NewRelic::MetricParser
|
2
|
+
|
3
|
+
def is_controller_cpu?; true; end
|
4
|
+
|
5
|
+
def controller_name
|
6
|
+
segments[1..-2].join('/').camelize+"Controller"
|
7
|
+
end
|
8
|
+
|
9
|
+
def action_name
|
10
|
+
segments[-1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def developer_name
|
14
|
+
"#{controller_name}##{action_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def base_metric_name
|
18
|
+
"Controller/" + segments[1..-1].join('/')
|
19
|
+
end
|
20
|
+
|
21
|
+
def short_name
|
22
|
+
# standard controller actions
|
23
|
+
if segments.length > 1
|
24
|
+
url
|
25
|
+
else
|
26
|
+
'All Controller Actions'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def url
|
31
|
+
'/' + segments[1..-1].join('/')
|
32
|
+
end
|
33
|
+
|
34
|
+
def call_rate_suffix
|
35
|
+
'rpm'
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class NewRelic::MetricParser::Database < NewRelic::MetricParser
|
2
|
+
def is_database?; true; end
|
3
|
+
|
4
|
+
def database
|
5
|
+
segments[1]
|
6
|
+
end
|
7
|
+
|
8
|
+
def active_record_class
|
9
|
+
segments[2].split(' ').first
|
10
|
+
end
|
11
|
+
|
12
|
+
def operation
|
13
|
+
op = segments.last
|
14
|
+
op_split = op.split(' ')
|
15
|
+
|
16
|
+
return op if op == 'Join Table Columns'
|
17
|
+
op_split.last
|
18
|
+
end
|
19
|
+
|
20
|
+
def developer_name
|
21
|
+
(segments[2]) ? "#{segments[1]} - #{segments[2]}" : "#{segments[1]} - unknown"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class NewRelic::MetricParser::MemCache < NewRelic::MetricParser
|
2
|
+
def is_memcache?; true; end
|
3
|
+
|
4
|
+
# for MemCache metrics, the short name is actually
|
5
|
+
# the full name
|
6
|
+
def short_name
|
7
|
+
name
|
8
|
+
end
|
9
|
+
def developer_name
|
10
|
+
"MemCache #{segments[1..-1].join '/'}"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class NewRelic::MetricParser::View < NewRelic::MetricParser
|
2
|
+
def is_view?; true; end
|
3
|
+
|
4
|
+
def is_render?
|
5
|
+
segments.last == "Rendering"
|
6
|
+
end
|
7
|
+
def is_compiler?
|
8
|
+
segments.last == "Compile"
|
9
|
+
end
|
10
|
+
def pie_chart_label
|
11
|
+
case segments.last
|
12
|
+
when "Rendering"
|
13
|
+
"#{file_name(segments[-2])} Template"
|
14
|
+
when "Partial"
|
15
|
+
"#{file_name(segments[-2])} Partial"
|
16
|
+
when ".rhtml Processing"
|
17
|
+
"ERB compilation"
|
18
|
+
else
|
19
|
+
segments[1..-1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def template_label
|
23
|
+
case segments.last
|
24
|
+
when "Rendering"
|
25
|
+
"#{file_name(segments[1..-2].join(NewRelic::MetricParser::SEPARATOR))} Template"
|
26
|
+
when "Partial"
|
27
|
+
"#{file_name(segments[1..-2].join(NewRelic::MetricParser::SEPARATOR))} Partial"
|
28
|
+
when ".rhtml Processing"
|
29
|
+
"ERB compilation"
|
30
|
+
else
|
31
|
+
segments[1..-1].join("/")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def short_name
|
36
|
+
segments[1..-2].join(NewRelic::MetricParser::SEPARATOR)
|
37
|
+
end
|
38
|
+
|
39
|
+
def controller_name
|
40
|
+
template_label
|
41
|
+
end
|
42
|
+
|
43
|
+
def action_name
|
44
|
+
# Strip the extension
|
45
|
+
segments[-2].gsub(/\..*$/, "")
|
46
|
+
end
|
47
|
+
|
48
|
+
def developer_name
|
49
|
+
template_label
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
'/' + file_name(segments[1..-2].join('/'))
|
54
|
+
end
|
55
|
+
private
|
56
|
+
def file_name(path)
|
57
|
+
label = path.gsub /\.html\.rhtml/, '.rhtml'
|
58
|
+
label = segments[1] if label.empty?
|
59
|
+
label
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module NewRelic
|
2
|
+
# Metric parsing logic mixin. Given a metric name (attribute called "name"), provide a set of accessors
|
3
|
+
# that enable inspection of the metric. A metric has 2 or more segments, each separated
|
4
|
+
# by the '/' character. The metric's category is specified by its first segment. Following
|
5
|
+
# are the set of categories currently supported by NewRelic's default metric set:
|
6
|
+
#
|
7
|
+
# * Controller
|
8
|
+
# * ActiveRecord
|
9
|
+
# * Rails
|
10
|
+
# * WebService
|
11
|
+
# * View
|
12
|
+
# * Database
|
13
|
+
# * Custom
|
14
|
+
#
|
15
|
+
# Based on the category of the metric, specific parsing logic is defined in the source files
|
16
|
+
# countained in the "metric_parsers" sub directory local to this file.
|
17
|
+
#
|
18
|
+
|
19
|
+
class MetricParser
|
20
|
+
|
21
|
+
SEPARATOR = '/' unless defined? SEPARATOR
|
22
|
+
attr_reader :name
|
23
|
+
|
24
|
+
# Load in the parsers classes in the plugin:
|
25
|
+
Dir[File.join(File.dirname(__FILE__), "metric_parser", "*.rb")].each { | file | require file }
|
26
|
+
|
27
|
+
# return a string that is parsable via the Metric parser APIs
|
28
|
+
def self.for_metric_named(s)
|
29
|
+
category = (s =~ /^([^\/]*)/) && $1
|
30
|
+
parser_class = self
|
31
|
+
if category
|
32
|
+
parser_class = NewRelic::MetricParser.const_get(category) if (NewRelic::MetricParser.const_defined?(category) rescue nil)
|
33
|
+
end
|
34
|
+
parser_class.new s
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse(s)
|
38
|
+
for_metric_named(s)
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(method_name, *args)
|
42
|
+
return false if method_name.to_s =~ /^is_.*\?/
|
43
|
+
super
|
44
|
+
end
|
45
|
+
# The short name for the metric is defined as all of the segments
|
46
|
+
# of the metric name except for its first (its domain).
|
47
|
+
def short_name
|
48
|
+
if segments.empty?
|
49
|
+
''
|
50
|
+
elsif segments.length == 1
|
51
|
+
segments[0]
|
52
|
+
else
|
53
|
+
segments[1..-1].join(SEPARATOR)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def pie_chart_label
|
58
|
+
developer_name
|
59
|
+
end
|
60
|
+
|
61
|
+
def developer_name
|
62
|
+
short_name
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return the name of another metric if the current
|
66
|
+
# metric is really add-on data for another metric.
|
67
|
+
def base_metric_name
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def category
|
72
|
+
segments[0]
|
73
|
+
end
|
74
|
+
|
75
|
+
def segments
|
76
|
+
return [] if !name
|
77
|
+
@segments ||= name.split(SEPARATOR).freeze
|
78
|
+
end
|
79
|
+
|
80
|
+
# --
|
81
|
+
# These accessors are used to allow chart to use a specific segment in the metric
|
82
|
+
# name for label construction as a zero-arg accessor
|
83
|
+
# ++
|
84
|
+
def segment_0; segments[0]; end
|
85
|
+
def segment_1; segments[1]; end
|
86
|
+
def segment_2; segments[2]; end
|
87
|
+
def segment_3; segments[3]; end
|
88
|
+
def segment_4; segments[4]; end
|
89
|
+
def last_segment; segments.last; end
|
90
|
+
|
91
|
+
# This is the suffix used for call rate or throughput. By default, it's cpm
|
92
|
+
# but things like controller actions will override to use something like 'rpm'
|
93
|
+
# for requests per minute
|
94
|
+
def call_rate_suffix
|
95
|
+
'cpm'
|
96
|
+
end
|
97
|
+
|
98
|
+
def url
|
99
|
+
''
|
100
|
+
end
|
101
|
+
|
102
|
+
def initialize(name)
|
103
|
+
@name = name
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -15,7 +15,11 @@ class NewRelic::NoticedError
|
|
15
15
|
else
|
16
16
|
self.message = exception.message
|
17
17
|
end
|
18
|
+
|
19
|
+
# clamp long messages to 4k so that we don't send a lot of
|
20
|
+
# overhead across the wire
|
21
|
+
self.message = self.message[0..4096] if self.message.length > 4096
|
18
22
|
|
19
23
|
self.timestamp = Time.now
|
20
24
|
end
|
21
|
-
end
|
25
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module NewRelic::Rack
|
3
|
+
class MetricApp
|
4
|
+
def initialize(options)
|
5
|
+
if options[:install]
|
6
|
+
FileUtils.copy File.join(File.dirname(__FILE__), "newrelic.yml"), "."
|
7
|
+
NewRelic::Control.instance.log.info "==============================="
|
8
|
+
NewRelic::Control.instance.log.info "A newrelic.yml template was copied to #{File.expand_path('.')}."
|
9
|
+
NewRelic::Control.instance.log.info "Please add a license key to the file and restart #{$0}"
|
10
|
+
exit 0
|
11
|
+
end
|
12
|
+
options[:app_name] ||= 'EPM Monitor'
|
13
|
+
options[:disable_samplers] = true
|
14
|
+
NewRelic::Agent.manual_start options
|
15
|
+
@stats_engine = NewRelic::Agent.instance.stats_engine
|
16
|
+
unless NewRelic::Control.instance.license_key
|
17
|
+
NewRelic::Control.instance.log.error "Please add a valid license key to newrelic.yml."
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def call(env)
|
22
|
+
request = ::Rack::Request.new env
|
23
|
+
segments = request.url.gsub(/^.*?\/metrics\//, '').split("?")[0].split("/")
|
24
|
+
metric = "Custom/" + segments.join("/")
|
25
|
+
raise "Expected value parameter!" unless request['value']
|
26
|
+
data = request['value'].to_f
|
27
|
+
stats = @stats_engine.get_stats(metric, false)
|
28
|
+
stats.record_data_point data
|
29
|
+
response = ::Rack::Response.new "#{metric}=#{data}"
|
30
|
+
response.finish
|
31
|
+
end
|
32
|
+
end
|
33
|
+
class Status
|
34
|
+
def call(env)
|
35
|
+
request = ::Rack::Request.new env
|
36
|
+
data_url = "http://#{env['HTTP_HOST']}/metrics/path?value=nnn"
|
37
|
+
body = StringIO.new
|
38
|
+
body.puts "<html><body>"
|
39
|
+
body.puts "<h1>New Relic Actively Monitoring #{NewRelic::Control.instance.app_names.join(' and ')}</h1>"
|
40
|
+
body.puts "<p>To submit a metric value, use <a href='#{data_url}'>#{data_url}</a></p>"
|
41
|
+
body.puts "<h2>Request Details</h2>"
|
42
|
+
body.puts "<dl>"
|
43
|
+
body.puts "<dt>ip<dd>#{request.ip}"
|
44
|
+
body.puts "<dt>host<dd>#{request.host}"
|
45
|
+
body.puts "<dt>path<dd>#{request.url}"
|
46
|
+
body.puts "<dt>query<dd>#{request.query_string}"
|
47
|
+
body.puts "<dt>params<dd>#{request.params.inspect}"
|
48
|
+
body.puts "</dl>"
|
49
|
+
body.puts "<h2>Complete ENV</h2>"
|
50
|
+
body.puts "<ul>"
|
51
|
+
body.puts env.to_a.map{|k,v| "<li>#{k} = #{v}</li>" }.join("\n")
|
52
|
+
body.puts "</ul></body></html>"
|
53
|
+
response = ::Rack::Response.new body.string
|
54
|
+
response.finish
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This is so that we don't detect a dispatcher like mongrel and think we are
|
2
|
+
# monitoring it.
|
3
|
+
ENV['NEWRELIC_DISPATCHER'] = 'none'
|
4
|
+
|
5
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..','..'))
|
6
|
+
|
7
|
+
require 'new_relic/rack'
|
8
|
+
|
9
|
+
# Valid options which may be present in this binding:
|
10
|
+
# :license_key optional license key override
|
11
|
+
# :app_name optional name of app
|
12
|
+
# :logging optional, false to omit request logging to stdout
|
13
|
+
|
14
|
+
options ||= {}
|
15
|
+
|
16
|
+
use Rack::CommonLogger unless options[:logging] == false
|
17
|
+
use Rack::ShowExceptions
|
18
|
+
map "http://localhost/metrics" do
|
19
|
+
run NewRelic::Rack::MetricApp.new(options)
|
20
|
+
end
|
21
|
+
map "/" do
|
22
|
+
run NewRelic::Rack::Status.new
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# This file configures the Rack NewRelic Metric Application. Place a copy of this file
|
3
|
+
# in the directory where you start the mongrel_rpm process.
|
4
|
+
#
|
5
|
+
common: &default_settings
|
6
|
+
log_level: info
|
7
|
+
|
8
|
+
# This is the key generated by script/create_admin
|
9
|
+
license_key: 'your_license_key_here'
|
10
|
+
|
11
|
+
# Replace this with the name of the application feeding
|
12
|
+
# metrics into the Metric application;
|
13
|
+
app_name: Application Monitor
|
14
|
+
|
15
|
+
# If RAILS_ENV or RUBY_ENV is defined to be one of the following
|
16
|
+
# these values will be overrides
|
17
|
+
|
18
|
+
production:
|
19
|
+
<<: *default_settings
|
20
|
+
app_name: Production Monitor
|
21
|
+
|
22
|
+
staging:
|
23
|
+
<<: *default_settings
|
24
|
+
app_name: Staging Monitor
|
25
|
+
|
data/lib/new_relic/recipes.rb
CHANGED
@@ -16,8 +16,8 @@ make_notify_task = lambda do
|
|
16
16
|
desc "Record a deployment in New Relic RPM (rpm.newrelic.com)"
|
17
17
|
task :notice_deployment, :roles => :app, :except => {:no_release => true } do
|
18
18
|
rails_env = fetch(:rails_env, "production")
|
19
|
+
require File.join(File.dirname(__FILE__), 'commands', 'deployments.rb')
|
19
20
|
begin
|
20
|
-
require File.join(File.dirname(__FILE__), 'commands', 'deployments.rb')
|
21
21
|
# Try getting the changelog from the server. Then fall back to local changelog
|
22
22
|
# if it doesn't work. Problem is that I don't know what directory the .git is
|
23
23
|
# in when using git. I could possibly use the remote cache but i don't know
|
@@ -35,10 +35,17 @@ make_notify_task = lambda do
|
|
35
35
|
if !changelog
|
36
36
|
logger.debug "Getting log of changes for New Relic Deployment details"
|
37
37
|
from_revision = source.next_revision(current_revision)
|
38
|
-
|
38
|
+
if scm == :git
|
39
|
+
log_command = "git log --no-color --pretty=format:' * %an: %s' --abbrev-commit --no-merges #{previous_revision}..#{real_revision}"
|
40
|
+
else
|
41
|
+
log_command = "#{source.log(from_revision)}"
|
42
|
+
end
|
39
43
|
changelog = `#{log_command}`
|
40
44
|
end
|
41
|
-
new_revision = rev || source.query_revision(source.head())
|
45
|
+
new_revision = rev || source.query_revision(source.head()) do |cmd|
|
46
|
+
logger.debug "executing locally: '#{cmd}'"
|
47
|
+
`#{cmd}`
|
48
|
+
end
|
42
49
|
deploy_options = { :environment => rails_env,
|
43
50
|
:revision => new_revision,
|
44
51
|
:changelog => changelog,
|
data/lib/new_relic/stats.rb
CHANGED
@@ -7,6 +7,17 @@ module NewRelic
|
|
7
7
|
call_count == 0
|
8
8
|
end
|
9
9
|
|
10
|
+
def time_str(value_ms)
|
11
|
+
case
|
12
|
+
when value_ms >= 10000
|
13
|
+
"%.1f s" % (value_ms / 1000.0)
|
14
|
+
when value_ms >= 5000
|
15
|
+
"%.2f s" % (value_ms / 1000.0)
|
16
|
+
else
|
17
|
+
"%.0f ms" % value_ms
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
10
21
|
def average_call_time
|
11
22
|
return 0 if call_count == 0
|
12
23
|
total_call_time / call_count
|
@@ -54,9 +65,10 @@ module NewRelic
|
|
54
65
|
timeslices = []
|
55
66
|
while current_end_time < self.end_time do
|
56
67
|
ts = yield(current_begin_time, current_end_time)
|
57
|
-
|
58
|
-
|
59
|
-
|
68
|
+
if ts
|
69
|
+
ts.fraction_of(self)
|
70
|
+
timeslices << ts
|
71
|
+
end
|
60
72
|
current_begin_time = current_end_time
|
61
73
|
current_end_time = current_begin_time + rollup_period
|
62
74
|
end
|
@@ -64,13 +76,19 @@ module NewRelic
|
|
64
76
|
if self.end_time > current_begin_time
|
65
77
|
percentage = rollup_period / self.duration + (self.begin_time - rollup_begin_time) / rollup_period
|
66
78
|
ts = yield(current_begin_time, self.end_time)
|
67
|
-
ts
|
68
|
-
|
79
|
+
if ts
|
80
|
+
ts.fraction_of(self)
|
81
|
+
timeslices << ts
|
82
|
+
end
|
69
83
|
end
|
70
84
|
|
71
85
|
timeslices
|
72
86
|
end
|
73
87
|
|
88
|
+
def is_reset?
|
89
|
+
call_count == 0 && total_call_time == 0.0 && total_exclusive_time == 0.0
|
90
|
+
end
|
91
|
+
|
74
92
|
def reset
|
75
93
|
self.call_count = 0
|
76
94
|
self.total_call_time = 0.0
|
@@ -84,13 +102,13 @@ module NewRelic
|
|
84
102
|
|
85
103
|
def as_percentage_of(other_stats)
|
86
104
|
return 0 if other_stats.total_call_time == 0
|
87
|
-
return (total_call_time / other_stats.total_call_time)
|
105
|
+
return to_percentage(total_call_time / other_stats.total_call_time)
|
88
106
|
end
|
89
107
|
|
90
108
|
# the stat total_call_time is a percent
|
91
109
|
def as_percentage
|
92
110
|
return 0 if call_count == 0
|
93
|
-
(total_call_time / call_count)
|
111
|
+
to_percentage(total_call_time / call_count)
|
94
112
|
end
|
95
113
|
|
96
114
|
def duration
|
@@ -103,9 +121,11 @@ module NewRelic
|
|
103
121
|
end
|
104
122
|
|
105
123
|
def calls_per_second
|
106
|
-
|
124
|
+
round_to_2 calls_per_minute / 60
|
125
|
+
end
|
126
|
+
def total_call_time_per_minute
|
127
|
+
60.0 * time_percentage
|
107
128
|
end
|
108
|
-
|
109
129
|
def standard_deviation
|
110
130
|
return 0 if call_count < 2 || self.sum_of_squares.nil?
|
111
131
|
|
@@ -130,35 +150,36 @@ module NewRelic
|
|
130
150
|
end
|
131
151
|
|
132
152
|
alias average_value average_call_time
|
133
|
-
alias average_response_time average_call_time
|
153
|
+
alias average_response_time average_call_time
|
154
|
+
alias requests_per_minute calls_per_minute
|
134
155
|
|
135
156
|
def to_s
|
136
157
|
s = "Begin=#{begin_time}, "
|
137
158
|
s << "Duration=#{duration} s, "
|
138
159
|
s << "Count=#{call_count}, "
|
139
|
-
s << "Total=#{total_call_time
|
140
|
-
s << "Total Exclusive=#{total_exclusive_time
|
141
|
-
s << "Avg=#{average_call_time
|
142
|
-
s << "Min=#{min_call_time
|
143
|
-
s << "Max=#{max_call_time
|
144
|
-
s << "StdDev=#{standard_deviation
|
160
|
+
s << "Total=#{to_ms(total_call_time)}, "
|
161
|
+
s << "Total Exclusive=#{to_ms(total_exclusive_time)}, "
|
162
|
+
s << "Avg=#{to_ms(average_call_time)}, "
|
163
|
+
s << "Min=#{to_ms(min_call_time)}, "
|
164
|
+
s << "Max=#{to_ms(max_call_time)}, "
|
165
|
+
s << "StdDev=#{to_ms(standard_deviation)}"
|
145
166
|
end
|
146
167
|
|
147
168
|
# Summary string to facilitate testing
|
148
169
|
def summary
|
149
170
|
format = "%m/%d %I:%M%p"
|
150
|
-
"[#{Time.at(begin_time).strftime(format)}, #{duration}s. #{call_count} calls; #{average_call_time
|
171
|
+
"[#{Time.at(begin_time).strftime(format)}, #{duration}s. #{call_count} calls; #{to_ms(average_call_time)}ms]"
|
151
172
|
end
|
152
173
|
|
153
174
|
# round all of the values to n decimal points
|
154
|
-
def round!
|
155
|
-
self.total_call_time = total_call_time
|
156
|
-
self.total_exclusive_time = total_exclusive_time
|
157
|
-
self.min_call_time = min_call_time
|
158
|
-
self.max_call_time = max_call_time
|
159
|
-
self.sum_of_squares = sum_of_squares
|
160
|
-
self.begin_time = begin_time
|
161
|
-
self.end_time = end_time
|
175
|
+
def round!
|
176
|
+
self.total_call_time = round_to_3(total_call_time)
|
177
|
+
self.total_exclusive_time = round_to_3(total_exclusive_time)
|
178
|
+
self.min_call_time = round_to_3(min_call_time)
|
179
|
+
self.max_call_time = round_to_3(max_call_time)
|
180
|
+
self.sum_of_squares = round_to_3(sum_of_squares)
|
181
|
+
self.begin_time = begin_time
|
182
|
+
self.end_time = end_time
|
162
183
|
end
|
163
184
|
|
164
185
|
# calculate this set of stats to be a percentage fraction
|
@@ -185,16 +206,38 @@ module NewRelic
|
|
185
206
|
self
|
186
207
|
end
|
187
208
|
|
209
|
+
|
210
|
+
# returns s,t,f
|
188
211
|
def get_apdex
|
189
|
-
[@call_count, @total_call_time, @total_exclusive_time
|
190
|
-
end
|
212
|
+
[@call_count, @total_call_time.to_i, @total_exclusive_time.to_i]
|
213
|
+
end
|
214
|
+
|
215
|
+
def apdex_score
|
216
|
+
s, t, f = get_apdex
|
217
|
+
(s.to_f + (t.to_f / 2)) / (s+t+f).to_f
|
218
|
+
end
|
219
|
+
private
|
220
|
+
def to_ms(number)
|
221
|
+
(number*1000).round
|
222
|
+
end
|
223
|
+
# utility method that converts floating point percentage values
|
224
|
+
# to integers as a percentage, to improve readability in ui
|
225
|
+
def to_percentage(value)
|
226
|
+
round_to_2(value * 100)
|
227
|
+
end
|
191
228
|
|
229
|
+
def round_to_2(val)
|
230
|
+
(val * 100).round / 100.0
|
231
|
+
end
|
232
|
+
def round_to_3(val)
|
233
|
+
(val * 1000).round / 1000.0
|
234
|
+
end
|
192
235
|
end
|
193
236
|
|
194
|
-
|
195
|
-
class
|
237
|
+
|
238
|
+
class StatsBase
|
196
239
|
include Stats
|
197
|
-
|
240
|
+
|
198
241
|
attr_accessor :call_count
|
199
242
|
attr_accessor :min_call_time
|
200
243
|
attr_accessor :max_call_time
|
@@ -202,53 +245,25 @@ module NewRelic
|
|
202
245
|
attr_accessor :total_exclusive_time
|
203
246
|
attr_accessor :sum_of_squares
|
204
247
|
|
205
|
-
alias data_point_count call_count
|
206
|
-
|
207
248
|
def initialize
|
208
249
|
reset
|
209
250
|
end
|
210
251
|
|
211
|
-
# record a single data point into the statistical gatherer. The gatherer
|
212
|
-
# will aggregate all data points collected over a specified period and upload
|
213
|
-
# its data to the NewRelic server
|
214
|
-
def record_data_point(value, exclusive_time = value)
|
215
|
-
@call_count += 1
|
216
|
-
@total_call_time += value
|
217
|
-
@min_call_time = value if value < @min_call_time || @call_count == 1
|
218
|
-
@max_call_time = value if value > @max_call_time
|
219
|
-
@total_exclusive_time += exclusive_time
|
220
|
-
|
221
|
-
@sum_of_squares += (value * value)
|
222
|
-
self
|
223
|
-
end
|
224
|
-
|
225
|
-
alias trace_call record_data_point
|
226
|
-
|
227
|
-
def record_apdex_s(cpu)
|
228
|
-
@call_count += 1
|
229
|
-
@sum_of_squares += cpu
|
230
|
-
end
|
231
|
-
|
232
|
-
def record_apdex_t(cpu)
|
233
|
-
@total_call_time += 1
|
234
|
-
@sum_of_squares += cpu
|
235
|
-
end
|
236
|
-
|
237
|
-
def record_apdex_f(cpu)
|
238
|
-
@total_exclusive_time += 1
|
239
|
-
@sum_of_squares += cpu
|
240
|
-
end
|
241
|
-
|
242
|
-
def increment_count(value = 1)
|
243
|
-
@call_count += value
|
244
|
-
end
|
245
|
-
|
246
|
-
|
247
252
|
def freeze
|
248
253
|
@end_time = Time.now
|
249
254
|
super
|
250
255
|
end
|
251
256
|
|
257
|
+
def to_json(*a)
|
258
|
+
{'call_count' => call_count,
|
259
|
+
'min_call_time' => min_call_time,
|
260
|
+
'max_call_time' => max_call_time,
|
261
|
+
'total_call_time' => total_call_time,
|
262
|
+
'total_exclusive_time' => total_exclusive_time,
|
263
|
+
'sum_of_squares' => sum_of_squares}.to_json(*a)
|
264
|
+
end
|
265
|
+
|
266
|
+
|
252
267
|
# In this class, we explicitly don't track begin and end time here, to save space during
|
253
268
|
# cross process serialization via xml. Still the accessor methods must be provided for merge to work.
|
254
269
|
def begin_time=(t)
|
@@ -266,94 +281,65 @@ module NewRelic
|
|
266
281
|
end
|
267
282
|
end
|
268
283
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
284
|
+
|
285
|
+
class BasicStats < StatsBase
|
286
|
+
end
|
287
|
+
|
288
|
+
class ApdexStats < StatsBase
|
289
|
+
|
290
|
+
def record_apdex_s
|
291
|
+
@call_count += 1
|
274
292
|
end
|
275
293
|
|
276
|
-
def
|
277
|
-
@
|
278
|
-
super call_time, exclusive_time
|
294
|
+
def record_apdex_t
|
295
|
+
@total_call_time += 1
|
279
296
|
end
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
class Numeric
|
284
|
-
|
285
|
-
# copied from rails
|
286
|
-
def with_delimiter(delimiter=",", separator=".")
|
287
|
-
begin
|
288
|
-
parts = self.to_s.split('.')
|
289
|
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
|
290
|
-
parts.join separator
|
291
|
-
rescue
|
292
|
-
self
|
297
|
+
|
298
|
+
def record_apdex_f
|
299
|
+
@total_exclusive_time += 1
|
293
300
|
end
|
294
301
|
end
|
295
302
|
|
296
|
-
#
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
end
|
301
|
-
|
302
|
-
# return the number of decimal points that this number would best render in
|
303
|
-
#
|
304
|
-
def get_number_decimals_ms
|
305
|
-
base = 0.010
|
306
|
-
decimal = 0
|
303
|
+
# Statistics used to track the performance of traced methods
|
304
|
+
class MethodTraceStats < StatsBase
|
305
|
+
|
306
|
+
alias data_point_count call_count
|
307
307
|
|
308
|
-
|
309
|
-
|
310
|
-
|
308
|
+
# record a single data point into the statistical gatherer. The gatherer
|
309
|
+
# will aggregate all data points collected over a specified period and upload
|
310
|
+
# its data to the NewRelic server
|
311
|
+
def record_data_point(value, exclusive_time = value)
|
312
|
+
@call_count += 1
|
313
|
+
@total_call_time += value
|
314
|
+
@min_call_time = value if value < @min_call_time || @call_count == 1
|
315
|
+
@max_call_time = value if value > @max_call_time
|
316
|
+
@total_exclusive_time += exclusive_time
|
317
|
+
|
318
|
+
@sum_of_squares += (value * value)
|
319
|
+
self
|
311
320
|
end
|
312
321
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
|
322
|
-
def to_ns(decimal_places = 0)
|
323
|
-
(self * 1000000).round_to(decimal_places)
|
324
|
-
end
|
325
|
-
|
326
|
-
def to_minutes(decimal_places = 0)
|
327
|
-
(self / 60).round_to(decimal_places)
|
328
|
-
end
|
329
|
-
|
330
|
-
# utility method that converts floating point percentage values
|
331
|
-
# to integers as a percentage, to improve readability in ui
|
332
|
-
def to_percentage(decimal_places = 2)
|
333
|
-
(self * 100).round_to(decimal_places)
|
322
|
+
alias trace_call record_data_point
|
323
|
+
|
324
|
+
def increment_count(value = 1)
|
325
|
+
@call_count += value
|
326
|
+
end
|
327
|
+
|
334
328
|
end
|
335
329
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
330
|
+
class ScopedMethodTraceStats < MethodTraceStats
|
331
|
+
def initialize(unscoped_stats)
|
332
|
+
super()
|
333
|
+
@unscoped_stats = unscoped_stats
|
334
|
+
end
|
335
|
+
|
336
|
+
def trace_call(call_time, exclusive_time = call_time)
|
337
|
+
@unscoped_stats.trace_call call_time, exclusive_time
|
338
|
+
super call_time, exclusive_time
|
340
339
|
end
|
341
|
-
|
342
|
-
|
343
|
-
x = x.to_f / 10
|
340
|
+
def unscoped_stats
|
341
|
+
@unscoped_stats
|
344
342
|
end
|
345
|
-
x
|
346
|
-
end
|
347
|
-
|
348
|
-
def round_to_1
|
349
|
-
round_to(1)
|
350
|
-
end
|
351
|
-
|
352
|
-
def round_to_2
|
353
|
-
round_to(2)
|
354
343
|
end
|
344
|
+
end
|
355
345
|
|
356
|
-
def round_to_3
|
357
|
-
round_to(3)
|
358
|
-
end
|
359
|
-
end
|