newrelic_rpm 3.6.0.74.beta → 3.6.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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