newrelic_rpm 3.6.0.74.beta → 3.6.0.78

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data.tar.gz.sig +1 -2
  2. data/CHANGELOG +18 -0
  3. data/Gemfile +3 -3
  4. data/Rakefile +8 -0
  5. data/lib/new_relic/agent/agent.rb +4 -9
  6. data/lib/new_relic/agent/agent_logger.rb +1 -2
  7. data/lib/new_relic/agent/audit_logger.rb +7 -3
  8. data/lib/new_relic/agent/busy_calculator.rb +3 -3
  9. data/lib/new_relic/agent/configuration/server_source.rb +23 -10
  10. data/lib/new_relic/agent/cross_app_monitor.rb +1 -0
  11. data/lib/new_relic/agent/database.rb +2 -0
  12. data/lib/new_relic/agent/error_collector.rb +1 -1
  13. data/lib/new_relic/agent/instrumentation/active_record.rb +14 -13
  14. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +72 -0
  15. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +0 -76
  16. data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +138 -0
  17. data/lib/new_relic/agent/instrumentation/rails4/active_record.rb +108 -0
  18. data/lib/new_relic/agent/null_logger.rb +15 -0
  19. data/lib/new_relic/agent/stats.rb +1 -0
  20. data/lib/new_relic/agent/stats_engine/transactions.rb +4 -4
  21. data/lib/new_relic/control/frameworks/rails.rb +0 -37
  22. data/lib/new_relic/control/frameworks/rails3.rb +0 -17
  23. data/lib/new_relic/control/instance_methods.rb +1 -2
  24. data/lib/new_relic/environment_report.rb +159 -0
  25. data/lib/new_relic/helper.rb +4 -0
  26. data/lib/new_relic/local_environment.rb +0 -161
  27. data/lib/newrelic_rpm.rb +1 -1
  28. data/test/multiverse/lib/multiverse/suite.rb +7 -0
  29. data/test/multiverse/suites/active_record/Envfile +0 -1
  30. data/test/multiverse/suites/agent_only/key_transactions_test.rb +22 -12
  31. data/test/multiverse/suites/rails/Envfile +8 -1
  32. data/test/multiverse/suites/rails/app.rb +7 -2
  33. data/test/multiverse/suites/rails/error_tracing_test.rb +28 -16
  34. data/test/multiverse/suites/rails/view_instrumentation_test.rb +8 -2
  35. data/test/new_relic/agent/agent/connect_test.rb +5 -8
  36. data/test/new_relic/agent/agent/start_test.rb +3 -1
  37. data/test/new_relic/agent/agent_logger_test.rb +8 -8
  38. data/test/new_relic/agent/agent_test.rb +0 -6
  39. data/test/new_relic/agent/agent_test_controller_test.rb +18 -14
  40. data/test/new_relic/agent/audit_logger_test.rb +12 -0
  41. data/test/new_relic/agent/configuration/server_source_test.rb +48 -0
  42. data/test/new_relic/agent/cross_app_monitor_test.rb +8 -0
  43. data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +254 -0
  44. data/test/new_relic/agent/instrumentation/active_record_helper_test.rb +52 -0
  45. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +32 -51
  46. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +132 -0
  47. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +20 -0
  48. data/test/new_relic/agent_test.rb +0 -8
  49. data/test/new_relic/control_test.rb +1 -2
  50. data/test/new_relic/dispatcher_test.rb +1 -5
  51. data/test/new_relic/environment_report_test.rb +89 -0
  52. data/test/new_relic/license_test.rb +1 -3
  53. data/test/new_relic/load_test.rb +5 -1
  54. data/test/new_relic/local_environment_test.rb +0 -27
  55. data/test/new_relic/rack/browser_monitoring_test.rb +1 -0
  56. data/test/script/ci.sh +19 -18
  57. data/test/test_helper.rb +15 -3
  58. metadata +42 -3
  59. metadata.gz.sig +0 -0
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ # Listen for ActiveSupport::Notifications events for ActionView render
6
+ # events. Write metric data and transaction trace segments for each event.
7
+ module NewRelic
8
+ module Agent
9
+ module Instrumentation
10
+ class ActionViewSubscriber
11
+ def initialize
12
+ @queue_key = ['NewRelic', self.class.name, object_id].join('-')
13
+ end
14
+
15
+ def self.subscribe
16
+ if !subscribed?
17
+ ActiveSupport::Notifications.subscribe(/render_.+\.action_view$/,
18
+ new)
19
+ end
20
+ end
21
+
22
+ def self.subscribed?
23
+ # TODO: need to talk to Rails core about an API for this,
24
+ # rather than digging through Listener ivars
25
+ ActiveSupport::Notifications.notifier.instance_variable_get(:@subscribers) \
26
+ .find{|s| s.instance_variable_get(:@delegate).class == self }
27
+ end
28
+
29
+ def start(name, id, payload)
30
+ event = RenderEvent.new(name, Time.now, nil, id, payload)
31
+ parent = event_stack[id].last
32
+ event.parent = parent
33
+ parent << event if parent
34
+ event_stack[id].push event
35
+
36
+ if NewRelic::Agent.is_execution_traced? && event.recordable?
37
+ event.scope = NewRelic::Agent.instance.stats_engine \
38
+ .push_scope(event.metric_name, event.time)
39
+ end
40
+ end
41
+
42
+ def finish(name, id, payload)
43
+ event = event_stack[id].pop
44
+ event.end = Time.now
45
+
46
+ if NewRelic::Agent.is_execution_traced? && event.recordable?
47
+ record_metrics(event)
48
+ NewRelic::Agent.instance.stats_engine \
49
+ .pop_scope(event.scope, event.duration, event.end)
50
+ end
51
+ end
52
+
53
+ def record_metrics(event)
54
+ NewRelic::Agent.instance.stats_engine \
55
+ .record_metrics(event.metric_name,
56
+ Helper.milliseconds_to_seconds(event.duration),
57
+ :scoped => true)
58
+ end
59
+
60
+ def event_stack
61
+ Thread.current[@queue_key] ||= Hash.new {|h,id| h[id] = [] }
62
+ end
63
+
64
+ if defined?(ActiveSupport::Notifications::Event)
65
+ class RenderEvent < ActiveSupport::Notifications::Event
66
+ attr_accessor :parent, :scope
67
+
68
+ # Nearly every "render_blah.action_view" event has a child
69
+ # in the form of "!render_blah.action_view". The children
70
+ # are the ones we want to record. There are a couple
71
+ # special cases of events without children.
72
+ def recordable?
73
+ name[0] == '!' ||
74
+ metric_name == 'View/text template/Rendering' ||
75
+ metric_name == 'View/(unknown)/Partial'
76
+ end
77
+
78
+ def metric_name
79
+ if parent && (payload[:virtual_path] ||
80
+ (parent.payload[:identifier] =~ /template$/))
81
+ return parent.metric_name
82
+ elsif payload[:virtual_path]
83
+ identifier = payload[:virtual_path]
84
+ else
85
+ identifier = payload[:identifier]
86
+ end
87
+
88
+ # memoize
89
+ @metric_name ||= "View/#{metric_path(identifier)}/#{metric_action(name)}"
90
+ @metric_name
91
+ end
92
+
93
+ def metric_path(identifier)
94
+ if identifier == nil
95
+ 'file'
96
+ elsif identifier =~ /template$/
97
+ identifier
98
+ elsif (parts = identifier.split('/')).size > 1
99
+ parts[-2..-1].join('/')
100
+ else
101
+ '(unknown)'
102
+ end
103
+ end
104
+
105
+ def metric_action(name)
106
+ case name
107
+ when /render_template.action_view$/ then 'Rendering'
108
+ when 'render_partial.action_view' then 'Partial'
109
+ when 'render_collection.action_view' then 'Partial'
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ DependencyDetection.defer do
120
+ @name = :rails4_view
121
+
122
+ depends_on do
123
+ defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 4
124
+ end
125
+
126
+ depends_on do
127
+ !NewRelic::Agent.config[:disable_view_instrumentation] &&
128
+ !NewRelic::Agent::Instrumentation::ActionViewSubscriber.subscribed?
129
+ end
130
+
131
+ executes do
132
+ ::NewRelic::Agent.logger.info 'Installing Rails 4 view instrumentation'
133
+ end
134
+
135
+ executes do
136
+ NewRelic::Agent::Instrumentation::ActionViewSubscriber.subscribe
137
+ end
138
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+ require 'new_relic/agent/instrumentation/active_record_helper'
5
+
6
+ # Listen for ActiveSupport::Notifications events for ActiveRecord query
7
+ # events. Write metric data, transaction trace segments and slow sql
8
+ # nodes for each event.
9
+ module NewRelic
10
+ module Agent
11
+ module Instrumentation
12
+ class ActiveRecordSubscriber
13
+ include NewRelic::Agent::Instrumentation
14
+
15
+ def self.subscribed?
16
+ # TODO: need to talk to Rails core about an API for this,
17
+ # rather than digging through Listener ivars
18
+ ActiveSupport::Notifications.notifier.listeners_for('sql.active_record') \
19
+ .find{|l| l.instance_variable_get(:@delegate).class == self }
20
+ end
21
+
22
+ def call(*args)
23
+ return unless NewRelic::Agent.is_execution_traced?
24
+
25
+ event = ActiveSupport::Notifications::Event.new(*args)
26
+ record_metrics(event)
27
+ notice_sql(event)
28
+ end
29
+
30
+ def notice_sql(event)
31
+ config = active_record_config_for_event(event)
32
+ metric = base_metric(event)
33
+
34
+ # enter transaction trace segment
35
+ scope = NewRelic::Agent.instance.stats_engine.push_scope(metric, event.time)
36
+
37
+ NewRelic::Agent.instance.transaction_sampler \
38
+ .notice_sql(event.payload[:sql], config,
39
+ Helper.milliseconds_to_seconds(event.duration))
40
+
41
+ NewRelic::Agent.instance.sql_sampler \
42
+ .notice_sql(event.payload[:sql], metric, config,
43
+ Helper.milliseconds_to_seconds(event.duration))
44
+
45
+ # exit transaction trace segment
46
+ NewRelic::Agent.instance.stats_engine.pop_scope(scope, event.duration, event.end)
47
+ end
48
+
49
+ def record_metrics(event)
50
+ base = base_metric(event)
51
+ NewRelic::Agent.instance.stats_engine.record_metrics(base,
52
+ Helper.milliseconds_to_seconds(event.duration),
53
+ :scoped => true)
54
+
55
+ other_metrics = ActiveRecordHelper.rollup_metrics_for(base)
56
+ if config = active_record_config_for_event(event)
57
+ other_metrics << ActiveRecordHelper.remote_service_metric(config[:adapter], config[:host])
58
+ end
59
+
60
+ other_metrics.compact.each do |metric_name|
61
+ NewRelic::Agent.instance.stats_engine.record_metrics(metric_name,
62
+ Helper.milliseconds_to_seconds(event.duration),
63
+ :scoped => false)
64
+ end
65
+ end
66
+
67
+ def base_metric(event)
68
+ ActiveRecordHelper.metric_for_name(event.payload[:name]) ||
69
+ ActiveRecordHelper.metric_for_sql(NewRelic::Helper.correctly_encoded(event.payload[:sql]))
70
+ end
71
+
72
+ def active_record_config_for_event(event)
73
+ return unless event.payload[:connection_id]
74
+
75
+ # TODO: This will not work for JRuby and in any case we want
76
+ # this to be part of the event meta data so it doesn't have
77
+ # to be dug out of an ivar.
78
+ connection = ObjectSpace._id2ref(event.payload[:connection_id])
79
+ connection.instance_variable_get(:@config) if connection
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ DependencyDetection.defer do
87
+ @name = :active_record
88
+
89
+ depends_on do
90
+ defined?(::ActiveRecord) && defined?(::ActiveRecord::Base) &&
91
+ defined?(::ActiveRecord::VERSION) &&
92
+ ::ActiveRecord::VERSION::MAJOR.to_i >= 4
93
+ end
94
+
95
+ depends_on do
96
+ !NewRelic::Agent.config[:disable_activerecord_instrumentation] &&
97
+ !NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.subscribed?
98
+ end
99
+
100
+ executes do
101
+ ::NewRelic::Agent.logger.info 'Installing ActiveRecord instrumentation'
102
+ end
103
+
104
+ executes do
105
+ ActiveSupport::Notifications.subscribe('sql.active_record',
106
+ NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.new)
107
+ end
108
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ # A stub object that we can use in place of a real Logger instance when
6
+ # the agent is disabled.
7
+ module NewRelic
8
+ module Agent
9
+ class NullLogger
10
+ def method_missing(method, *args, &blk)
11
+ nil
12
+ end
13
+ end
14
+ end
15
+ end
@@ -101,6 +101,7 @@ module NewRelic
101
101
  end
102
102
 
103
103
  def ==(other)
104
+ other.class == self.class &&
104
105
  (
105
106
  @min_call_time == other.min_call_time &&
106
107
  @max_call_time == other.max_call_time &&
@@ -75,17 +75,17 @@ module Agent
75
75
  @transaction_sampler.notice_pop_scope(scope.name, time) if sampler_enabled?
76
76
  scope
77
77
  end
78
-
78
+
79
79
  def sampler_enabled?
80
80
  @transaction_sampler && Agent.config[:'transaction_tracer.enabled']
81
81
  end
82
-
82
+
83
83
  # Rename the segment associated with the last pushed scope to +new_name+.
84
84
  def rename_scope_segment( new_name )
85
85
  self.peek_scope.name = new_name
86
- @transaction_sampler.rename_scope_segment( new_name )
86
+ @transaction_sampler.rename_scope_segment( new_name ) if sampler_enabled?
87
87
  end
88
-
88
+
89
89
  # Returns the latest ScopeStackElement
90
90
  def peek_scope
91
91
  scope_stack.last
@@ -125,43 +125,6 @@ module NewRelic
125
125
  File.join(root,'vendor','rails')
126
126
  end
127
127
 
128
- def rails_gem_list
129
- ::Rails.configuration.gems.map do |gem|
130
- version = (gem.respond_to?(:version) && gem.version) ||
131
- (gem.specification.respond_to?(:version) && gem.specification.version)
132
- gem.name + (version ? "(#{version})" : "")
133
- end
134
- end
135
-
136
- # Collect the Rails::Info into an associative array as well as the list of plugins
137
- def append_environment_info
138
- local_env.append_environment_value('Rails version'){ ::Rails::VERSION::STRING }
139
- if rails_version >= NewRelic::VersionNumber.new('2.2.0')
140
- local_env.append_environment_value('Rails threadsafe') do
141
- ::Rails.configuration.action_controller.allow_concurrency == true
142
- end
143
- end
144
- local_env.append_environment_value('Rails Env') { ENV['RAILS_ENV'] }
145
- if rails_version >= NewRelic::VersionNumber.new('2.1.0')
146
- local_env.append_gem_list do
147
- (bundler_gem_list + rails_gem_list).uniq
148
- end
149
- # The plugins is configured manually. If it's nil, it loads everything non-deterministically
150
- if ::Rails.configuration.plugins
151
- local_env.append_plugin_list { ::Rails.configuration.plugins }
152
- else
153
- ::Rails.configuration.plugin_paths.each do |path|
154
- local_env.append_plugin_list { Dir[File.join(path, '*')].collect{ |p| File.basename p if File.directory? p }.compact }
155
- end
156
- end
157
- else
158
- # Rails prior to 2.1, can't get the gems. Find plugins in the default location
159
- local_env.append_plugin_list do
160
- Dir[File.join(root, 'vendor', 'plugins', '*')].collect{ |p| File.basename p if File.directory? p }.compact
161
- end
162
- end
163
- end
164
-
165
128
  def install_shim
166
129
  super
167
130
  require 'new_relic/agent/instrumentation/controller_instrumentation'
@@ -46,23 +46,6 @@ module NewRelic
46
46
 
47
47
  protected
48
48
 
49
- # Collect the Rails::Info into an associative array as well as the list of plugins
50
- def append_environment_info
51
- local_env.append_environment_value('Rails version'){ ::Rails::VERSION::STRING }
52
- local_env.append_environment_value('Rails threadsafe') do
53
- true == ::Rails.configuration.action_controller.allow_concurrency
54
- end
55
- local_env.append_environment_value('Rails Env') { env }
56
- local_env.append_gem_list do
57
- bundler_gem_list
58
- end
59
- append_plugin_list
60
- end
61
-
62
- def append_plugin_list
63
- local_env.append_plugin_list { ::Rails.configuration.plugins.to_a }
64
- end
65
-
66
49
  def install_shim
67
50
  super
68
51
  ActiveSupport.on_load(:action_controller) do
@@ -3,6 +3,7 @@
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
5
  require 'new_relic/language_support'
6
+ require 'new_relic/agent/null_logger'
6
7
  require 'new_relic/agent/agent_logger'
7
8
 
8
9
  module NewRelic
@@ -74,8 +75,6 @@ module NewRelic
74
75
  start_agent
75
76
  install_instrumentation
76
77
  load_samplers unless Agent.config[:disable_samplers]
77
- local_env.gather_environment_info
78
- append_environment_info
79
78
  elsif !Agent.config[:agent_enabled]
80
79
  install_shim
81
80
  end
@@ -0,0 +1,159 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ # The EnvironmentReport is responsible for analyzing the application's
7
+ # environment and generating the data for the Environment Report in New
8
+ # Relic's interface.
9
+ #
10
+ # It contains useful system information like Ruby version, OS, loaded gems,
11
+ # etc.
12
+ #
13
+ # Additional logic can be registered by using the EnvironmentReport.report_on
14
+ # hook.
15
+ class EnvironmentReport
16
+
17
+ # This is the main interface for registering logic that should be included
18
+ # in the Environment Report. For example:
19
+ #
20
+ # EnvironmentReport.report_on "Day of week" do
21
+ # Time.now.strftime("%A")
22
+ # end
23
+ #
24
+ # The passed blocks will be run in EnvironmentReport instances on #initialize.
25
+ #
26
+ # Errors raised in passed blocks will be handled and logged at debug, so it
27
+ # is safe to report on things that may not work in certain environments.
28
+ #
29
+ # The blocks should only return strings or arrays full of strings. Falsey
30
+ # values will be ignored.
31
+ def self.report_on(key, &block)
32
+ registered_reporters[key] = block
33
+ end
34
+
35
+ def self.registered_reporters
36
+ @registered_reporters ||= Hash.new
37
+ end
38
+
39
+ # allow the logic to be swapped out in tests
40
+ def self.registered_reporters=(logic)
41
+ @registered_reporters = logic
42
+ end
43
+
44
+ # register reporting logic
45
+ ####################################
46
+ report_on 'Gems' do
47
+ begin
48
+ Bundler.rubygems.all_specs.map { |gem| "#{gem.name}(#{gem.version})" }
49
+ rescue
50
+ # There are certain rubygem, bundler, rails combinations (e.g. gem
51
+ # 1.6.2, rails 2.3, bundler 1.2.3) where the code above throws an error
52
+ # in bundler because of rails monkey patching gem. The below does work
53
+ # though so try it if the above fails.
54
+ Bundler.load.specs.map do | spec |
55
+ version = (spec.respond_to?(:version) && spec.version)
56
+ spec.name + (version ? "(#{version})" : "")
57
+ end
58
+ end
59
+ end
60
+ report_on('Plugin List'){ ::Rails.configuration.plugins.to_a }
61
+ report_on('Ruby version'){ RUBY_VERSION }
62
+ report_on('Ruby description'){ RUBY_DESCRIPTION }
63
+ report_on('Ruby platform'){ RUBY_PLATFORM }
64
+ report_on('Ruby patchlevel'){ RUBY_PATCHLEVEL.to_s }
65
+ report_on('JRuby version') { JRUBY_VERSION }
66
+ report_on('Java VM version') { ENV_JAVA['java.vm.version']}
67
+ report_on 'Processors' do
68
+ cpuinfo = ''
69
+ proc_file = '/proc/cpuinfo'
70
+ File.open(proc_file) do |f|
71
+ loop do
72
+ begin
73
+ cpuinfo << f.read_nonblock(4096).strip
74
+ rescue EOFError
75
+ break
76
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
77
+ cpuinfo = ''
78
+ break # don't select file handle, just give up
79
+ end
80
+ end
81
+ end
82
+ processors = cpuinfo.split("\n").select {|line| line =~ /^processor\s*:/ }.size
83
+
84
+ if processors == 0
85
+ processors = nil # assume there is at least one processor
86
+ ::NewRelic::Agent.logger.warn("Cannot determine the number of processors in #{proc_file}")
87
+ end
88
+ processors
89
+ end
90
+ report_on 'Arch' do
91
+ arch = `uname -p`
92
+ arch = ENV['PROCESSOR_ARCHITECTURE'] if arch == ''
93
+ arch
94
+ end
95
+ report_on('OS version'){ `uname -v` }
96
+ report_on('OS') do
97
+ os = `uname -s`
98
+ os = ENV['OS'] if os == ''
99
+ os
100
+ end
101
+ report_on 'Database adapter' do
102
+ ActiveRecord::Base.configurations[NewRelic::Control.instance.env]['adapter']
103
+ end
104
+ report_on('Framework') { Agent.config[:framework].to_s }
105
+ report_on('Dispatcher') { Agent.config[:dispatcher].to_s }
106
+ report_on('Environment') { NewRelic::Control.instance.env }
107
+ report_on('Rails version') { ::Rails::VERSION::STRING }
108
+ report_on 'Rails threadsafe' do
109
+ ::Rails.configuration.action_controller.allow_concurrency
110
+ end
111
+ report_on 'Rails Env' do
112
+ if defined? ::Rails and ::Rails.respond_to?(:env)
113
+ ::Rails.env
114
+ else
115
+ ENV['RAILS_ENV']
116
+ end
117
+ end
118
+ # end reporting logic
119
+ ####################################
120
+
121
+
122
+ attr_reader :data
123
+ # Generate the report based on the class level logic.
124
+ def initialize
125
+ @data = self.class.registered_reporters.inject(Hash.new) do |data, (key, logic)|
126
+ begin
127
+ value = logic.call
128
+ if value
129
+ data[key] = value
130
+
131
+ Agent.record_metric("Supportability/EnvironmentReport/success", 0.0)
132
+ Agent.record_metric("Supportability/EnvironmentReport/success/#{key}", 0.0)
133
+ else
134
+ Agent.logger.debug("EnvironmentReport ignoring value for #{key.inspect} which came back falsey: #{value.inspect}")
135
+ Agent.record_metric("Supportability/EnvironmentReport/empty", 0.0)
136
+ Agent.record_metric("Supportability/EnvironmentReport/empty/#{key}", 0.0)
137
+ end
138
+ rescue => e
139
+ Agent.logger.debug("EnvironmentReport failed to retrieve value for #{key.inspect}: #{e}")
140
+ Agent.record_metric("Supportability/EnvironmentReport/error", 0.0)
141
+ Agent.record_metric("Supportability/EnvironmentReport/error/#{key}", 0.0)
142
+ end
143
+ data
144
+ end
145
+ end
146
+
147
+ def [](key)
148
+ @data[key]
149
+ end
150
+
151
+ def []=(key, value)
152
+ @data[key] = value
153
+ end
154
+
155
+ def to_a
156
+ @data.to_a
157
+ end
158
+ end
159
+ end