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.

Files changed (137) hide show
  1. data/CHANGELOG +267 -0
  2. data/LICENSE +1 -1
  3. data/Manifest +142 -0
  4. data/README.md +138 -0
  5. data/Rakefile +10 -28
  6. data/bin/mongrel_rpm +33 -0
  7. data/cert/cacert.pem +34 -0
  8. data/init.rb +38 -0
  9. data/lib/new_relic/agent/agent.rb +160 -347
  10. data/lib/new_relic/agent/collection_helper.rb +13 -24
  11. data/lib/new_relic/agent/error_collector.rb +29 -15
  12. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +63 -76
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +90 -48
  14. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +72 -47
  15. data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +14 -0
  16. data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -1
  17. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +5 -7
  18. data/lib/new_relic/agent/instrumentation/merb/errors.rb +3 -1
  19. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +7 -0
  20. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +34 -7
  21. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +20 -12
  22. data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -4
  23. data/lib/new_relic/agent/method_tracer.rb +159 -135
  24. data/lib/new_relic/agent/patch_const_missing.rb +46 -26
  25. data/lib/new_relic/agent/sampler.rb +12 -0
  26. data/lib/new_relic/agent/samplers/cpu_sampler.rb +44 -0
  27. data/lib/new_relic/agent/samplers/memory_sampler.rb +126 -0
  28. data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
  29. data/lib/new_relic/agent/shim_agent.rb +11 -0
  30. data/lib/new_relic/agent/stats_engine.rb +85 -46
  31. data/lib/new_relic/agent/transaction_sampler.rb +63 -38
  32. data/lib/new_relic/agent/worker_loop.rb +8 -18
  33. data/lib/new_relic/agent.rb +200 -25
  34. data/lib/new_relic/commands/deployments.rb +9 -9
  35. data/lib/new_relic/control/merb.rb +22 -0
  36. data/lib/new_relic/control/rails.rb +141 -0
  37. data/lib/new_relic/{config → control}/ruby.rb +13 -2
  38. data/lib/new_relic/control.rb +424 -0
  39. data/lib/new_relic/local_environment.rb +201 -79
  40. data/lib/new_relic/metric_data.rb +7 -0
  41. data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
  42. data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
  43. data/lib/new_relic/metric_parser/active_record.rb +11 -0
  44. data/lib/new_relic/metric_parser/controller.rb +51 -0
  45. data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
  46. data/lib/new_relic/metric_parser/database.rb +23 -0
  47. data/lib/new_relic/metric_parser/errors.rb +6 -0
  48. data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
  49. data/lib/new_relic/metric_parser/view.rb +61 -0
  50. data/lib/new_relic/metric_parser/web_service.rb +9 -0
  51. data/lib/new_relic/metric_parser.rb +107 -0
  52. data/lib/new_relic/metric_spec.rb +5 -0
  53. data/lib/new_relic/noticed_error.rb +5 -1
  54. data/lib/new_relic/rack/metric_app.rb +57 -0
  55. data/lib/new_relic/rack/newrelic.ru +25 -0
  56. data/lib/new_relic/rack/newrelic.yml +25 -0
  57. data/lib/new_relic/rack.rb +5 -0
  58. data/lib/new_relic/recipes.rb +10 -3
  59. data/lib/new_relic/stats.rb +130 -144
  60. data/lib/new_relic/transaction_analysis.rb +7 -8
  61. data/lib/new_relic/transaction_sample.rb +86 -10
  62. data/lib/new_relic/version.rb +41 -160
  63. data/lib/new_relic_api.rb +7 -6
  64. data/lib/newrelic_rpm.rb +30 -17
  65. data/lib/tasks/{agent_tests.rake → tests.rake} +1 -1
  66. data/newrelic.yml +115 -62
  67. data/newrelic_rpm.gemspec +36 -0
  68. data/test/active_record_fixtures.rb +55 -0
  69. data/test/config/newrelic.yml +21 -3
  70. data/test/config/{test_config.rb → test_control.rb} +14 -10
  71. data/test/new_relic/agent/active_record_instrumentation_test.rb +189 -0
  72. data/test/new_relic/agent/agent_test.rb +104 -0
  73. data/test/new_relic/agent/agent_test_controller.rb +18 -1
  74. data/test/new_relic/agent/classloader_patch_test.rb +56 -0
  75. data/test/new_relic/agent/{tc_collection_helper.rb → collection_helper_test.rb} +28 -23
  76. data/test/new_relic/agent/controller_test.rb +107 -0
  77. data/test/new_relic/agent/dispatcher_instrumentation_test.rb +70 -0
  78. data/test/new_relic/agent/error_collector_test.rb +155 -0
  79. data/test/new_relic/agent/{tc_method_tracer.rb → method_tracer_test.rb} +6 -12
  80. data/test/new_relic/agent/metric_data_test.rb +56 -0
  81. data/test/new_relic/agent/stats_engine_test.rb +266 -0
  82. data/test/new_relic/agent/{tc_transaction_sample_builder.rb → transaction_sample_builder_test.rb} +6 -5
  83. data/test/new_relic/agent/{tc_transaction_sample.rb → transaction_sample_test.rb} +9 -13
  84. data/test/new_relic/agent/transaction_sampler_test.rb +317 -0
  85. data/test/new_relic/agent/{tc_worker_loop.rb → worker_loop_test.rb} +1 -1
  86. data/test/new_relic/control_test.rb +97 -0
  87. data/test/new_relic/{tc_deployments_api.rb → deployments_api_test.rb} +8 -4
  88. data/test/new_relic/environment_test.rb +75 -0
  89. data/test/new_relic/metric_parser_test.rb +142 -0
  90. data/test/new_relic/{tc_metric_spec.rb → metric_spec_test.rb} +28 -1
  91. data/test/new_relic/samplers_test.rb +71 -0
  92. data/test/new_relic/{tc_shim_agent.rb → shim_agent_test.rb} +1 -1
  93. data/test/new_relic/stats_test.rb +291 -0
  94. data/test/new_relic/version_number_test.rb +46 -0
  95. data/test/test_helper.rb +7 -30
  96. data/test/ui/newrelic_controller_test.rb +14 -0
  97. data/test/ui/{tc_newrelic_helper.rb → newrelic_helper_test.rb} +16 -7
  98. data/ui/controllers/newrelic_controller.rb +17 -3
  99. data/ui/helpers/newrelic_helper.rb +44 -15
  100. data/ui/views/layouts/newrelic_default.rhtml +7 -8
  101. data/ui/views/newrelic/_sample.rhtml +5 -2
  102. data/ui/views/newrelic/_segment.rhtml +1 -1
  103. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  104. data/ui/views/newrelic/_segment_row.rhtml +4 -4
  105. data/ui/views/newrelic/_show_sample_detail.rhtml +3 -1
  106. data/ui/views/newrelic/_show_sample_sql.rhtml +2 -1
  107. data/ui/views/newrelic/explain_sql.rhtml +2 -5
  108. data/ui/views/newrelic/images/file_icon.png +0 -0
  109. data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
  110. data/ui/views/newrelic/index.rhtml +21 -13
  111. data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
  112. data/ui/views/newrelic/show_sample.rhtml +18 -3
  113. data/ui/views/newrelic/stylesheets/style.css +39 -0
  114. data/ui/views/newrelic/threads.rhtml +52 -0
  115. metadata +192 -70
  116. data/README +0 -136
  117. data/lib/new_relic/agent/instrumentation/rails/rails.rb +0 -6
  118. data/lib/new_relic/agent/samplers/cpu.rb +0 -29
  119. data/lib/new_relic/agent/samplers/memory.rb +0 -53
  120. data/lib/new_relic/agent/samplers/mongrel.rb +0 -26
  121. data/lib/new_relic/agent/synchronize.rb +0 -40
  122. data/lib/new_relic/config/merb.rb +0 -35
  123. data/lib/new_relic/config/rails.rb +0 -114
  124. data/lib/new_relic/config.rb +0 -279
  125. data/lib/new_relic/shim_agent.rb +0 -96
  126. data/test/new_relic/agent/model_fixture.rb +0 -15
  127. data/test/new_relic/agent/tc_active_record.rb +0 -90
  128. data/test/new_relic/agent/tc_agent.rb +0 -148
  129. data/test/new_relic/agent/tc_controller.rb +0 -77
  130. data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +0 -52
  131. data/test/new_relic/agent/tc_error_collector.rb +0 -127
  132. data/test/new_relic/agent/tc_stats_engine.rb +0 -218
  133. data/test/new_relic/agent/tc_synchronize.rb +0 -37
  134. data/test/new_relic/agent/tc_transaction_sampler.rb +0 -302
  135. data/test/new_relic/tc_config.rb +0 -36
  136. data/test/new_relic/tc_environment.rb +0 -94
  137. 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,6 @@
1
+ class NewRelic::MetricParser::Errors < NewRelic::MetricParser
2
+ def is_error?; true; end
3
+ def short_name
4
+ segments[2..-1].join(NewRelic::MetricParser::SEPARATOR)
5
+ end
6
+ 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,9 @@
1
+ class NewRelic::MetricParser::WebService < NewRelic::MetricParser
2
+ def is_web_service?
3
+ segments[1] != 'Soap' && segments[1] != 'Xml Rpc'
4
+ end
5
+
6
+ def webservice_call_rate_suffix
7
+ 'rpm'
8
+ end
9
+ 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
@@ -20,6 +20,11 @@ class NewRelic::MetricSpec
20
20
  h
21
21
  end
22
22
 
23
+ def to_json(*a)
24
+ {'name' => name,
25
+ 'scope' => scope}.to_json(*a)
26
+ end
27
+
23
28
  def <=>(o)
24
29
  namecmp = name <=> o.name
25
30
  return namecmp if namecmp != 0
@@ -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
+
@@ -0,0 +1,5 @@
1
+ require 'rack/response'
2
+ require 'newrelic_rpm'
3
+
4
+ # Rack middlewares and applications used by New Relic
5
+ require 'new_relic/rack/metric_app'
@@ -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
- log_command = "#{source.log(from_revision)}"
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()) { |cmd| `#{cmd}` }
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,
@@ -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
- ts.fraction_of(self)
59
- timeslices << ts
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.fraction_of(self)
68
- timeslices << ts
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).to_percentage
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).to_percentage
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
- (calls_per_minute / 60).round_to(2)
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.to_ms}, "
140
- s << "Total Exclusive=#{total_exclusive_time.to_ms}, "
141
- s << "Avg=#{average_call_time.to_ms}, "
142
- s << "Min=#{min_call_time.to_ms}, "
143
- s << "Max=#{max_call_time.to_ms}, "
144
- s << "StdDev=#{standard_deviation.to_ms}"
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.to_ms}ms]"
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!(decimal_places = 3)
155
- self.total_call_time = total_call_time.round_to(decimal_places)
156
- self.total_exclusive_time = total_exclusive_time.round_to(decimal_places)
157
- self.min_call_time = min_call_time.round_to(decimal_places)
158
- self.max_call_time = max_call_time.round_to(decimal_places)
159
- self.sum_of_squares = sum_of_squares.round_to(decimal_places)
160
- self.begin_time = begin_time.round
161
- self.end_time = end_time.round
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, @sum_of_squares]
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
- # Statistics used to track the performance of traced methods
195
- class MethodTraceStats
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
- class ScopedMethodTraceStats < MethodTraceStats
270
- def initialize(unscoped_stats)
271
- super()
272
-
273
- @unscoped_stats = unscoped_stats
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 trace_call(call_time, exclusive_time = call_time)
277
- @unscoped_stats.trace_call call_time, exclusive_time
278
- super call_time, exclusive_time
294
+ def record_apdex_t
295
+ @total_call_time += 1
279
296
  end
280
- end
281
- end
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
- # utlity method that converts floating point time values in seconds
297
- # to integers in milliseconds, to improve readability in ui
298
- def to_ms(decimal_places = 0)
299
- (self * 1000).round_to(decimal_places)
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
- while decimal <= 6 && self < base do
309
- base /= 10.0
310
- decimal += 1
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
- decimal
314
- end
315
-
316
- # auto-adjust the precision based on the value
317
- def to_smart_ms
318
- to_ms get_number_decimals_ms
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
- def round_to(decimal_places)
337
- x = self
338
- decimal_places.times do
339
- x = x * 10
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
- x = x.round
342
- decimal_places.times do
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