wd_newrelic_rpm 3.5.6 → 3.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGELOG +43 -3
  2. data/Gemfile +6 -2
  3. data/LICENSE +23 -0
  4. data/lib/new_relic/agent.rb +50 -3
  5. data/lib/new_relic/agent/agent.rb +40 -60
  6. data/lib/new_relic/agent/configuration/defaults.rb +9 -3
  7. data/lib/new_relic/agent/configuration/server_source.rb +4 -0
  8. data/lib/new_relic/agent/cross_app_monitor.rb +239 -0
  9. data/lib/new_relic/agent/cross_app_tracing.rb +281 -0
  10. data/lib/new_relic/agent/database.rb +28 -10
  11. data/lib/new_relic/agent/error_collector.rb +5 -0
  12. data/lib/new_relic/agent/event_listener.rb +4 -0
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +58 -39
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +16 -3
  15. data/lib/new_relic/agent/instrumentation/net.rb +13 -11
  16. data/lib/new_relic/agent/instrumentation/queue_time.rb +50 -192
  17. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +145 -0
  18. data/lib/new_relic/agent/instrumentation/rails4/errors.rb +45 -0
  19. data/lib/new_relic/agent/instrumentation/resque.rb +10 -10
  20. data/lib/new_relic/agent/instrumentation/sinatra.rb +19 -9
  21. data/lib/new_relic/agent/new_relic_service.rb +63 -9
  22. data/lib/new_relic/agent/pipe_service.rb +8 -12
  23. data/lib/new_relic/agent/rules_engine.rb +72 -0
  24. data/lib/new_relic/agent/shim_agent.rb +0 -1
  25. data/lib/new_relic/agent/sql_sampler.rb +3 -2
  26. data/lib/new_relic/agent/stats.rb +149 -0
  27. data/lib/new_relic/agent/stats_engine.rb +9 -0
  28. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -24
  29. data/lib/new_relic/agent/stats_engine/metric_stats.rb +84 -185
  30. data/lib/new_relic/agent/stats_engine/stats_hash.rb +58 -0
  31. data/lib/new_relic/agent/stats_engine/transactions.rb +10 -2
  32. data/lib/new_relic/agent/transaction_info.rb +31 -6
  33. data/lib/new_relic/agent/transaction_sample_builder.rb +19 -8
  34. data/lib/new_relic/agent/transaction_sampler.rb +17 -10
  35. data/lib/new_relic/helper.rb +32 -0
  36. data/lib/new_relic/local_environment.rb +24 -32
  37. data/lib/new_relic/okjson.rb +599 -0
  38. data/lib/new_relic/transaction_sample.rb +2 -1
  39. data/lib/new_relic/transaction_sample/segment.rb +2 -1
  40. data/lib/new_relic/version.rb +1 -1
  41. data/newrelic.yml +27 -41
  42. data/test/multiverse/suites/agent_only/Envfile +5 -1
  43. data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -4
  44. data/test/multiverse/suites/agent_only/config/newrelic.yml +1 -2
  45. data/test/multiverse/suites/agent_only/{cross_process_test.rb → cross_application_tracing_test.rb} +3 -3
  46. data/test/multiverse/suites/agent_only/key_transactions_test.rb +66 -0
  47. data/test/multiverse/suites/agent_only/marshaling_test.rb +9 -22
  48. data/test/multiverse/suites/agent_only/rename_rule_test.rb +57 -0
  49. data/test/multiverse/suites/agent_only/start_up_test.rb +1 -1
  50. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +17 -6
  51. data/test/multiverse/suites/rails/error_tracing_test.rb +20 -8
  52. data/test/multiverse/suites/rails/queue_time_test.rb +2 -2
  53. data/test/multiverse/suites/resque/instrumentation_test.rb +4 -3
  54. data/test/multiverse/suites/sinatra/Envfile +2 -0
  55. data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -0
  56. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +5 -5
  57. data/test/multiverse/suites/sinatra/sinatra_test.rb +77 -10
  58. data/test/new_relic/agent/agent/connect_test.rb +45 -1
  59. data/test/new_relic/agent/agent/start_worker_thread_test.rb +0 -3
  60. data/test/new_relic/agent/agent_test.rb +20 -40
  61. data/test/new_relic/agent/agent_test_controller_test.rb +27 -60
  62. data/test/new_relic/agent/busy_calculator_test.rb +1 -1
  63. data/test/new_relic/agent/configuration/server_source_test.rb +8 -3
  64. data/test/new_relic/agent/cross_app_monitor_test.rb +237 -0
  65. data/test/new_relic/agent/database_test.rb +60 -16
  66. data/test/new_relic/agent/error_collector_test.rb +28 -4
  67. data/test/new_relic/agent/event_listener_test.rb +23 -2
  68. data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
  69. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +85 -12
  70. data/test/new_relic/agent/instrumentation/metric_frame_test.rb +95 -0
  71. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +436 -59
  72. data/test/new_relic/agent/instrumentation/queue_time_test.rb +58 -357
  73. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -5
  74. data/test/new_relic/agent/method_tracer_test.rb +4 -2
  75. data/test/new_relic/agent/new_relic_service_test.rb +108 -6
  76. data/test/new_relic/agent/pipe_channel_manager_test.rb +1 -1
  77. data/test/new_relic/agent/pipe_service_test.rb +9 -9
  78. data/test/new_relic/agent/rpm_agent_test.rb +0 -11
  79. data/test/new_relic/agent/rules_engine_test.rb +82 -0
  80. data/test/new_relic/agent/shim_agent_test.rb +0 -4
  81. data/test/new_relic/agent/sql_sampler_test.rb +7 -0
  82. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +85 -0
  83. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +110 -23
  84. data/test/new_relic/agent/stats_engine_test.rb +1 -46
  85. data/test/new_relic/agent/stats_hash_test.rb +93 -0
  86. data/test/new_relic/agent/stats_test.rb +197 -0
  87. data/test/new_relic/agent/transaction_info_test.rb +63 -11
  88. data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -3
  89. data/test/new_relic/agent/transaction_sampler_test.rb +92 -80
  90. data/test/new_relic/agent/worker_loop_test.rb +1 -1
  91. data/test/new_relic/agent_test.rb +35 -5
  92. data/test/new_relic/control_test.rb +1 -1
  93. data/test/new_relic/fake_collector.rb +87 -9
  94. data/test/new_relic/helper_test.rb +24 -0
  95. data/test/new_relic/metric_data_test.rb +11 -11
  96. data/test/new_relic/metric_spec_test.rb +1 -1
  97. data/test/script/ci.sh +1 -1
  98. data/test/test_contexts.rb +0 -1
  99. data/test/test_helper.rb +21 -3
  100. metadata +34 -41
  101. data/lib/new_relic/agent/cross_process_monitoring.rb +0 -187
  102. data/lib/new_relic/stats.rb +0 -337
  103. data/test/new_relic/agent/cross_process_monitoring_test.rb +0 -190
  104. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +0 -133
  105. data/test/new_relic/fakes_sending_data.rb +0 -30
  106. data/test/new_relic/stats_test.rb +0 -421
data/CHANGELOG CHANGED
@@ -1,14 +1,54 @@
1
1
 
2
2
  # New Relic Ruby Agent Release Notes #
3
3
 
4
+ ## v3.5.8 ##
5
+
6
+ * Key Transactions
7
+
8
+ The Ruby agent now supports Key Transactions! Check out more details on the
9
+ feature at https://newrelic.com/docs/site/key-transactions
10
+
11
+ * Ruby 2.0
12
+
13
+ The Ruby agent is compatible with Ruby 2.0.0 which was just released.
14
+
15
+ * Improved Sinatra instrumentation
16
+
17
+ Several cases around the use of conditions and pass in Sinatra are now
18
+ better supported by the Ruby agent. Thanks Konstantin for the help!
19
+
20
+ * Outbound HTTP headers
21
+
22
+ Adds a 'X-NewRelic-ID' header to outbound Net::HTTP requests. This change
23
+ helps improve the correlation of performance between services in a service-
24
+ oriented architecture for a forthcoming feature. In the meantime, to disable
25
+ the header, set this in your newrelic.yml:
26
+
27
+ cross_application_tracer:
28
+ enabled: false
29
+
30
+ * Automatically detect Resque dispatcher
31
+
32
+ The agent does better auto-detection for the Resque worker process.
33
+ This should reduce the need to set NEW_RELIC_DISPATCHER=resque directly.
34
+
35
+ ## v3.5.7 ##
36
+
37
+ * Resolved some issues with tracking of frontend queue time, particularly
38
+ when the agent is running on an app hosted on Heroku. The agent will now
39
+ more reliably parse the headers described in
40
+ https://newrelic.com/docs/features/tracking-front-end-time and will
41
+ automatically detect whether the times provided are in seconds,
42
+ milliseconds or microseconds.
43
+
4
44
  ## v3.5.6 ##
5
45
 
6
46
  * Use HTTPS by default
7
47
 
8
48
  The agent now defaults to using SSL when it communicates with New Relic's
9
- servers. By defaults already configured, New Relic does not transmit any
10
- sensitive information (e.g. SQL parameters are masked), but SSL adds
11
- another layer of security. Upgrading customers may need to remove the
49
+ servers. By default is already configured New Relic does not transmit any
50
+ sensitive information (e.g. SQL parameters are masked), but SSL adds an
51
+ additional layer of security. Upgrading customers may need to remove the
12
52
  "ssl: false" directive from their newrelic.yml to enable ssl. Customers on
13
53
  Jruby may need to install the jruby-openssl gem to take advantage of this
14
54
  feature.
data/Gemfile CHANGED
@@ -1,11 +1,15 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  group :development do
4
4
  # require 0.9.2.2.
5
5
  # There's problems with the test task in rake 10
6
6
  # https://github.com/jimweirich/rake/issues/144
7
7
  gem 'rake', '0.9.2.2'
8
- gem 'mocha', '~>0.12.0'
8
+ if RUBY_VERSION > '1.9.0'
9
+ gem 'mocha', '~>0.13.0'
10
+ else
11
+ gem 'mocha', '~>0.12.0'
12
+ end
9
13
  gem 'shoulda', '~>3.0.1'
10
14
  gem 'sdoc-helpers'
11
15
  gem 'rdoc', '>= 2.4.2'
data/LICENSE CHANGED
@@ -23,6 +23,29 @@ See https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt
23
23
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
24
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
25
 
26
+ It also includes source derived from 'okjson' by Keith Rarick, distributed
27
+ under the MIT license.
28
+ See https://github.com/kr/okjson/blob/bdd1113/okjson.rb#L3-21
29
+
30
+ Copyright 2011, 2012 Keith Rarick
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ of this software and associated documentation files (the "Software"), to deal
34
+ in the Software without restriction, including without limitation the rights
35
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ copies of the Software, and to permit persons to whom the Software is
37
+ furnished to do so, subject to the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be included in
40
+ all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48
+ THE SOFTWARE.
26
49
 
27
50
  All other components of this product are
28
51
  Copyright (c) 2008-2012 New Relic, Inc. All rights reserved.
@@ -63,7 +63,6 @@ module NewRelic
63
63
 
64
64
  require 'new_relic/version'
65
65
  require 'new_relic/local_environment'
66
- require 'new_relic/stats'
67
66
  require 'new_relic/metrics'
68
67
  require 'new_relic/metric_spec'
69
68
  require 'new_relic/metric_data'
@@ -75,9 +74,10 @@ module NewRelic
75
74
  require 'new_relic/timer_lib'
76
75
 
77
76
  require 'new_relic/agent'
77
+ require 'new_relic/agent/stats'
78
78
  require 'new_relic/agent/chained_call'
79
79
  require 'new_relic/agent/browser_monitoring'
80
- require 'new_relic/agent/cross_process_monitoring'
80
+ require 'new_relic/agent/cross_app_monitor'
81
81
  require 'new_relic/agent/agent'
82
82
  require 'new_relic/agent/shim_agent'
83
83
  require 'new_relic/agent/method_tracer'
@@ -93,6 +93,7 @@ module NewRelic
93
93
  require 'new_relic/agent/pipe_channel_manager'
94
94
  require 'new_relic/agent/transaction_info'
95
95
  require 'new_relic/agent/configuration'
96
+ require 'new_relic/agent/rules_engine'
96
97
 
97
98
  require 'new_relic/agent/instrumentation/controller_instrumentation'
98
99
 
@@ -154,14 +155,60 @@ module NewRelic
154
155
  @logger = log
155
156
  end
156
157
 
158
+ # Record a value for the given metric name.
159
+ #
160
+ # This method should be used to record event-based metrics such as method
161
+ # calls that are associated with a specific duration or magnitude.
162
+ #
163
+ # +metric_name+ should follow a slash separated path convention. Application
164
+ # specific metrics should begin with "Custom/".
165
+ #
166
+ # +value+ should be either a single Numeric value representing the duration/
167
+ # magnitude of the event being recorded, or a Hash containing :count,
168
+ # :total, :min, :max, and :sum_of_squares keys. The latter form is useful
169
+ # for recording pre-aggregated metrics collected externally.
170
+ #
171
+ # This method is safe to use from any thread.
172
+ #
173
+ # @api public
174
+ def record_metric(metric_name, value)
175
+ if value.is_a?(Hash)
176
+ stats = NewRelic::Agent::Stats.new
177
+ stats.call_count = value[:count]
178
+ stats.total_call_time = value[:total]
179
+ stats.total_exclusive_time = value[:total]
180
+ stats.min_call_time = value[:min]
181
+ stats.max_call_time = value[:max]
182
+ stats.sum_of_squares = value[:sum_of_squares]
183
+ value = stats
184
+ end
185
+ agent.stats_engine.record_metric(metric_name, value)
186
+ end
187
+
188
+ # Increment a simple counter metric.
189
+ #
190
+ # +metric_name+ should follow a slash separated path convention. Application
191
+ # specific metrics should begin with "Custom/".
192
+ #
193
+ # This method is safe to use from any thread.
194
+ #
195
+ # @api public
196
+ def increment_metric(metric_name, amount=1)
197
+ agent.stats_engine.record_metric(metric_name) do |stats|
198
+ stats.increment_count(amount)
199
+ end
200
+ end
201
+
157
202
  # Get or create a statistics gatherer that will aggregate numerical data
158
203
  # under a metric name.
159
204
  #
160
205
  # +metric_name+ should follow a slash separated path convention. Application
161
206
  # specific metrics should begin with "Custom/".
162
207
  #
163
- # Return a NewRelic::Stats that accepts data
208
+ # Return a NewRelic::Agent::Stats that accepts data
164
209
  # via calls to add_data_point(value).
210
+ #
211
+ # @deprecated
165
212
  def get_stats(metric_name, use_scope=false)
166
213
  agent.stats_engine.get_stats(metric_name, use_scope)
167
214
  end
@@ -9,6 +9,8 @@ require 'new_relic/agent/pipe_service'
9
9
  require 'new_relic/agent/configuration/manager'
10
10
  require 'new_relic/agent/database'
11
11
  require 'new_relic/agent/thread_profiler'
12
+ require 'new_relic/agent/event_listener'
13
+ require 'new_relic/agent/cross_app_monitor'
12
14
 
13
15
  module NewRelic
14
16
  module Agent
@@ -23,14 +25,15 @@ module NewRelic
23
25
  def initialize
24
26
  @launch_time = Time.now
25
27
 
26
- @metric_ids = {}
27
- @events = NewRelic::Agent::EventListener.new
28
- @stats_engine = NewRelic::Agent::StatsEngine.new
29
- @transaction_sampler = NewRelic::Agent::TransactionSampler.new
30
- @sql_sampler = NewRelic::Agent::SqlSampler.new
31
- @thread_profiler = NewRelic::Agent::ThreadProfiler.new
32
- @cross_process_monitor = NewRelic::Agent::CrossProcessMonitor.new(@events)
33
- @error_collector = NewRelic::Agent::ErrorCollector.new
28
+ @events = NewRelic::Agent::EventListener.new
29
+ @stats_engine = NewRelic::Agent::StatsEngine.new
30
+ @transaction_sampler = NewRelic::Agent::TransactionSampler.new
31
+ @sql_sampler = NewRelic::Agent::SqlSampler.new
32
+ @thread_profiler = NewRelic::Agent::ThreadProfiler.new
33
+ @cross_app_monitor = NewRelic::Agent::CrossAppMonitor.new(@events)
34
+ @error_collector = NewRelic::Agent::ErrorCollector.new
35
+ @transaction_rules = NewRelic::Agent::RulesEngine.new
36
+ @metric_rules = NewRelic::Agent::RulesEngine.new
34
37
 
35
38
  @connect_state = :pending
36
39
  @connect_attempts = 0
@@ -82,27 +85,24 @@ module NewRelic
82
85
  attr_reader :error_collector
83
86
  # whether we should record raw, obfuscated, or no sql
84
87
  attr_reader :record_sql
85
- # a cached set of metric_ids to save the collector some time -
86
- # it returns a metric id for every metric name we send it, and
87
- # in the future we transmit using the metric id only
88
- attr_reader :metric_ids
89
- # in theory a set of rules applied by the agent to the output
90
- # of its metrics. Currently unimplemented
91
- attr_reader :url_rules
92
88
  # a configuration for the Real User Monitoring system -
93
89
  # handles things like static setup of the header for inclusion
94
90
  # into pages
95
91
  attr_reader :beacon_configuration
96
- # cross process id's and encoding
92
+ # cross application tracing ids and encoding
97
93
  attr_reader :cross_process_id
98
- attr_reader :cross_process_encoding_bytes
94
+ attr_reader :cross_app_encoding_bytes
99
95
  # service for communicating with collector
100
96
  attr_accessor :service
101
97
  # Global events dispatcher. This will provides our primary mechanism
102
98
  # for agent-wide events, such as finishing configuration, error notification
103
99
  # and request before/after from Rack.
104
100
  attr_reader :events
105
-
101
+ # Transaction and metric renaming rules as provided by the
102
+ # collector on connect. The former are applied during txns,
103
+ # the latter during harvest.
104
+ attr_reader :transaction_rules
105
+ attr_reader :metric_rules
106
106
 
107
107
  # Returns the length of the unsent errors array, if it exists,
108
108
  # otherwise nil
@@ -187,7 +187,6 @@ module NewRelic
187
187
  @service = NewRelic::Agent::PipeService.new(channel_id)
188
188
  if connected?
189
189
  @connected_pid = $$
190
- @metric_ids = {}
191
190
  else
192
191
  ::NewRelic::Agent.logger.debug("Child process #{$$} not reporting to non-connected parent.")
193
192
  @service.shutdown(Time.now)
@@ -533,7 +532,7 @@ module NewRelic
533
532
  def handle_force_restart(error)
534
533
  ::NewRelic::Agent.logger.debug error.message
535
534
  reset_stats
536
- @metric_ids = {}
535
+ @service.reset_metric_id_cache if @service
537
536
  @connect_state = :pending
538
537
  sleep 30
539
538
  end
@@ -747,12 +746,24 @@ module NewRelic
747
746
  Agent.config.apply_config(server_config, 1)
748
747
  log_connection!(config_data) if @service
749
748
 
749
+ add_rules_to_engine(config_data['transaction_name_rules'],
750
+ NewRelic::Agent.instance.transaction_rules)
751
+ add_rules_to_engine(config_data['metric_name_rules'],
752
+ NewRelic::Agent.instance.metric_rules)
753
+
750
754
  # If you're adding something else here to respond to the server-side config,
751
755
  # use Agent.instance.events.subscribe(:finished_configuring) callback instead!
752
756
 
753
757
  @beacon_configuration = BeaconConfiguration.new
754
758
  end
755
759
 
760
+ def add_rules_to_engine(rule_specifications, rules_engine)
761
+ return unless rule_specifications && rule_specifications.any?
762
+ rule_specifications.each do |rule_spec|
763
+ rules_engine << NewRelic::Agent::RulesEngine::Rule.new(rule_spec)
764
+ end
765
+ end
766
+
756
767
  # Logs when we connect to the server, for debugging purposes
757
768
  # - makes sure we know if an agent has not connected
758
769
  def log_connection!(config_data)
@@ -772,30 +783,13 @@ module NewRelic
772
783
  end
773
784
  include Connect
774
785
 
775
-
776
- # Serialize all the important data that the agent might want
777
- # to send to the server. We could be sending this to file (
778
- # common in short-running background transactions ) or
779
- # alternately we could serialize via a pipe or socket to a
780
- # local aggregation device
781
- def serialize
782
- accumulator = []
783
- accumulator[1] = harvest_transaction_traces if @transaction_sampler
784
- accumulator[2] = harvest_errors if @error_collector
785
- accumulator[0] = harvest_timeslice_data
786
- reset_stats
787
- @metric_ids = {}
788
- accumulator
789
- end
790
- public :serialize
791
-
792
- # Accepts data as provided by the serialize method and merges
786
+ # Accepts an array of (metrics, transaction_traces, errors) and merges
793
787
  # it into our current collection of data to send. Can be
794
788
  # dangerous if we re-merge the same data more than once - it
795
789
  # will be sent multiple times.
796
790
  def merge_data_from(data)
797
791
  metrics, transaction_traces, errors = data
798
- @stats_engine.merge_data(metrics) if metrics
792
+ @stats_engine.merge!(metrics) if metrics
799
793
  if transaction_traces && transaction_traces.respond_to?(:any?) &&
800
794
  transaction_traces.any?
801
795
  if @traces
@@ -831,8 +825,8 @@ module NewRelic
831
825
  # agent run and New Relic sees it as a separate instance (default is false).
832
826
  def connect(options={})
833
827
  defaults = {
834
- :keep_retrying => true,
835
- :force_reconnect => false
828
+ :keep_retrying => Agent.config[:keep_retrying],
829
+ :force_reconnect => Agent.config[:force_reconnect]
836
830
  }
837
831
  opts = defaults.merge(options)
838
832
 
@@ -876,21 +870,11 @@ module NewRelic
876
870
  NewRelic::Agent::BusyCalculator.harvest_busy
877
871
 
878
872
  @unsent_timeslice_data ||= {}
879
- @unsent_timeslice_data = @stats_engine.harvest_timeslice_data(@unsent_timeslice_data, @metric_ids)
873
+ @unsent_timeslice_data = @stats_engine.harvest_timeslice_data(@unsent_timeslice_data,
874
+ @metric_rules)
880
875
  @unsent_timeslice_data
881
876
  end
882
877
 
883
- # takes an array of arrays of spec and id, adds it into the
884
- # metric cache so we can save the collector some work by
885
- # sending integers instead of strings
886
- def fill_metric_id_cache(pairs_of_specs_and_ids)
887
- Array(pairs_of_specs_and_ids).each do |metric_spec_hash, metric_id|
888
- metric_spec = MetricSpec.new(metric_spec_hash['name'],
889
- metric_spec_hash['scope'])
890
- @metric_ids[metric_spec] = metric_id
891
- end
892
- end
893
-
894
878
  # note - exceptions are logged in invoke_remote. If an exception is encountered here,
895
879
  # then the metric data is downsampled for another
896
880
  # transmission later
@@ -899,17 +883,13 @@ module NewRelic
899
883
  NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote').record_data_point(0.0)
900
884
  NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote/metric_data').record_data_point(0.0)
901
885
  harvest_timeslice_data(now)
902
- # In this version of the protocol
903
- # we get back an assoc array of spec to id.
904
- metric_specs_and_ids = []
905
886
  begin
906
- metric_specs_and_ids = @service.metric_data(@last_harvest_time.to_f,
907
- now.to_f,
908
- @unsent_timeslice_data.values)
887
+ @service.metric_data(@last_harvest_time.to_f,
888
+ now.to_f,
889
+ @unsent_timeslice_data)
909
890
  rescue UnrecoverableServerException => e
910
891
  ::NewRelic::Agent.logger.debug e.message
911
892
  end
912
- fill_metric_id_cache(metric_specs_and_ids)
913
893
 
914
894
  ::NewRelic::Agent.logger.debug "#{now}: sent #{@unsent_timeslice_data.length} timeslices (#{@service.agent_id}) in #{Time.now - now} seconds"
915
895
 
@@ -1,7 +1,12 @@
1
1
  module NewRelic
2
2
  module Agent
3
3
  module Configuration
4
- DEFAULTS = {
4
+ # This is so we can easily differentiate between the actual
5
+ # default source and a Hash that was simply pushed onto the
6
+ # config stack.
7
+ class DefaultSource < Hash; end
8
+
9
+ DEFAULTS = DefaultSource[
5
10
  :config_path => Proc.new {
6
11
  # Check a sequence of file locations for newrelic.yml
7
12
  files = []
@@ -115,12 +120,13 @@ module NewRelic
115
120
  :'rum.load_episodes_file' => true,
116
121
  :'browser_monitoring.auto_instrument' => Proc.new { self[:'rum.enabled'] },
117
122
 
118
- :'cross_process.enabled' => true,
123
+ :trusted_account_ids => [],
124
+ :"cross_application_tracer.enabled" => true,
119
125
 
120
126
  :'thread_profiler.enabled' => Proc.new { NewRelic::Agent::ThreadProfiler.is_supported? },
121
127
 
122
128
  :marshaller => Proc.new { NewRelic::Agent::NewRelicService::JsonMarshaller.is_supported? ? 'json' : 'pruby' }
123
- }.freeze
129
+ ].freeze
124
130
  end
125
131
  end
126
132
  end
@@ -19,6 +19,10 @@ module NewRelic
19
19
  hash[pair[1]] = hash[pair[0]] if hash[pair[0]] != nil
20
20
  end
21
21
 
22
+ if hash['web_transactions_apdex']
23
+ self[:web_transactions_apdex] = hash.delete('web_transactions_apdex')
24
+ end
25
+
22
26
  super
23
27
  end
24
28
  end
@@ -0,0 +1,239 @@
1
+ require 'new_relic/rack/agent_hooks'
2
+ require 'new_relic/agent/thread'
3
+
4
+ module NewRelic
5
+ module Agent
6
+
7
+ class CrossAppMonitor
8
+
9
+ NEWRELIC_ID_HEADER = 'X-NewRelic-ID'
10
+ NEWRELIC_APPDATA_HEADER = 'X-NewRelic-App-Data'
11
+ NEWRELIC_TXN_HEADER = 'X-NewRelic-Transaction'
12
+ NEWRELIC_TXN_HEADER_KEYS = %W{
13
+ #{NEWRELIC_TXN_HEADER} HTTP_X_NEWRELIC_TRANSACTION X_NEWRELIC_TRANSACTION
14
+ }
15
+ NEWRELIC_ID_HEADER_KEYS = %W{
16
+ #{NEWRELIC_ID_HEADER} HTTP_X_NEWRELIC_ID X_NEWRELIC_ID
17
+ }
18
+ CONTENT_LENGTH_HEADER_KEYS = %w{Content-Length HTTP_CONTENT_LENGTH CONTENT_LENGTH}
19
+
20
+ # Because we aren't in the right spot when our transaction actually
21
+ # starts, hold client_cross_app_id we get thread local until then.
22
+ THREAD_ID_KEY = :newrelic_client_cross_app_id
23
+
24
+ # Same for the referring transaction guid
25
+ THREAD_TXN_KEY = :newrelic_cross_app_referring_txn_info
26
+
27
+
28
+ # Functions for obfuscating and unobfuscating header values
29
+ module EncodingFunctions
30
+
31
+ module_function
32
+
33
+ def obfuscate_with_key(key, text)
34
+ [ encode_with_key(key, text) ].pack('m').chomp.gsub(/\n/, '')
35
+ end
36
+
37
+ def decode_with_key(key, text)
38
+ encode_with_key( key, text.unpack('m').first )
39
+ end
40
+
41
+ def encode_with_key(key, text)
42
+ return text unless key
43
+ key = key.bytes.to_a if key.respond_to?( :bytes )
44
+
45
+ encoded = ""
46
+ index = 0
47
+ text.each_byte do |byte|
48
+ encoded.concat((byte ^ key[index % key.length].to_i))
49
+ index+=1
50
+ end
51
+ encoded
52
+ end
53
+
54
+ end
55
+ include EncodingFunctions
56
+
57
+
58
+ def initialize(events = nil)
59
+ # When we're starting up for real in the agent, we get passed the events
60
+ # Other spots can pull from the agent, during startup the agent doesn't exist yet!
61
+ events ||= Agent.instance.events
62
+
63
+ events.subscribe(:finished_configuring) do
64
+ register_event_listeners
65
+ end
66
+ end
67
+
68
+
69
+ # Expected sequence of events:
70
+ # :before_call will save our cross application request id to the thread
71
+ # :start_transaction will get called when a transaction starts up
72
+ # :after_call will write our response headers/metrics and clean up the thread
73
+ def register_event_listeners
74
+ NewRelic::Agent.logger.
75
+ debug("Wiring up Cross Application Tracing to events after finished configuring")
76
+
77
+ events = Agent.instance.events
78
+ events.subscribe(:before_call) do |env|
79
+ if should_process_request(env)
80
+ save_client_cross_app_id(env)
81
+ save_referring_transaction_info(env)
82
+ end
83
+ end
84
+
85
+ events.subscribe(:start_transaction) do |name|
86
+ set_transaction_custom_parameters
87
+ end
88
+
89
+ events.subscribe(:after_call) do |env, (status_code, headers, body)|
90
+ insert_response_header(env, headers)
91
+ end
92
+
93
+ events.subscribe(:notice_error) do |_, options|
94
+ set_error_custom_parameters(options)
95
+ end
96
+ end
97
+
98
+ def save_client_cross_app_id(request_headers)
99
+ NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = decoded_id(request_headers)
100
+ end
101
+
102
+ def clear_client_cross_app_id
103
+ NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = nil
104
+ end
105
+
106
+ def client_cross_app_id
107
+ NewRelic::Agent::AgentThread.current[THREAD_ID_KEY]
108
+ end
109
+
110
+ def save_referring_transaction_info(request_headers)
111
+ key = NewRelic::Agent.config[:encoding_key]
112
+ txn_header = from_headers( request_headers, NEWRELIC_TXN_HEADER_KEYS ) or return
113
+ txn_header = decode_with_key( key, txn_header )
114
+ txn_info = NewRelic.json_load( txn_header )
115
+ NewRelic::Agent.logger.debug "Referring txn_info: %p" % [ txn_info ]
116
+
117
+ NewRelic::Agent::AgentThread.current[THREAD_TXN_KEY] = txn_info
118
+ end
119
+
120
+ def clear_referring_transaction_info
121
+ NewRelic::Agent::AgentThread.current[THREAD_TXN_KEY] = nil
122
+ end
123
+
124
+ def client_referring_transaction_guid
125
+ info = NewRelic::Agent::AgentThread.current[THREAD_TXN_KEY] or return nil
126
+ return info[0]
127
+ end
128
+
129
+ def client_referring_transaction_record_flag
130
+ info = NewRelic::Agent::AgentThread.current[THREAD_TXN_KEY] or return nil
131
+ return info[1]
132
+ end
133
+
134
+ def insert_response_header(request_headers, response_headers)
135
+ unless client_cross_app_id.nil?
136
+ timings = NewRelic::Agent::BrowserMonitoring.timings
137
+ content_length = content_length_from_request(request_headers)
138
+
139
+ set_response_headers(response_headers, timings, content_length)
140
+ set_metrics(client_cross_app_id, timings)
141
+
142
+ clear_client_cross_app_id
143
+ end
144
+ end
145
+
146
+ def should_process_request(request_headers)
147
+ return cross_app_enabled? && trusts?(request_headers)
148
+ end
149
+
150
+ def cross_app_enabled?
151
+ NewRelic::Agent.config[:cross_process_id] &&
152
+ (NewRelic::Agent.config[:"cross_application_tracer.enabled"] ||
153
+ NewRelic::Agent.config[:cross_application_tracing])
154
+ end
155
+
156
+ # Expects an ID of format "12#345", and will only accept that!
157
+ def trusts?(request)
158
+ id = decoded_id(request)
159
+ split_id = id.match(/(\d+)#\d+/)
160
+ return false if split_id.nil?
161
+
162
+ NewRelic::Agent.config[:trusted_account_ids].include?(split_id.captures.first.to_i)
163
+ end
164
+
165
+ def set_response_headers(response_headers, timings, content_length)
166
+ response_headers[NEWRELIC_APPDATA_HEADER] = build_payload(timings, content_length)
167
+ end
168
+
169
+ def build_payload(timings, content_length)
170
+
171
+ # FIXME The transaction name might not be properly encoded. use a json generator
172
+ # For now we just handle quote characters by dropping them
173
+ transaction_name = timings.transaction_name.gsub(/["']/, "")
174
+
175
+ payload = [
176
+ NewRelic::Agent.config[:cross_process_id],
177
+ transaction_name,
178
+ timings.queue_time_in_seconds.to_f,
179
+ timings.app_time_in_seconds.to_f,
180
+ content_length,
181
+ transaction_guid()
182
+ ]
183
+ key = NewRelic::Agent.config[:encoding_key]
184
+ payload = obfuscate_with_key( key, NewRelic.json_dump(payload) )
185
+ end
186
+
187
+ def set_transaction_custom_parameters
188
+ # We expect to get the before call to set the id (if we have it) before
189
+ # this, and then write our custom parameter when the transaction starts
190
+ NewRelic::Agent.add_custom_parameters(:client_cross_process_id => client_cross_app_id()) if client_cross_app_id()
191
+ NewRelic::Agent.add_custom_parameters(:referring_transaction_guid => client_referring_transaction_guid()) if
192
+ client_referring_transaction_guid()
193
+
194
+ NewRelic::Agent.logger.debug "Referring transaction guid: %p" % [client_referring_transaction_guid()]
195
+ end
196
+
197
+ def set_error_custom_parameters(options)
198
+ options[:client_cross_process_id] = client_cross_app_id() if client_cross_app_id()
199
+ # [MG] TODO: Should the CAT metrics be set here too?
200
+ end
201
+
202
+ def set_metrics(id, timings)
203
+ metric = NewRelic::Agent.instance.stats_engine.get_stats_no_scope("ClientApplication/#{id}/all")
204
+ metric.record_data_point(timings.app_time_in_seconds)
205
+ end
206
+
207
+ def decoded_id(request)
208
+ encoded_id = from_headers(request, NEWRELIC_ID_HEADER_KEYS)
209
+ return "" if encoded_id.nil?
210
+
211
+ key = NewRelic::Agent.config[:encoding_key]
212
+ decode_with_key( key, encoded_id )
213
+ end
214
+
215
+ def content_length_from_request(request)
216
+ from_headers(request, CONTENT_LENGTH_HEADER_KEYS) || -1
217
+ end
218
+
219
+ def transaction_guid
220
+ NewRelic::Agent::TransactionInfo.get.guid
221
+ end
222
+
223
+ private
224
+
225
+ def from_headers(request, try_keys)
226
+ # For lookups, upcase all our keys on both sides just to be safe
227
+ upcased_keys = try_keys.map{|k| k.upcase}
228
+ upcased_keys.each do |header|
229
+ found_key = request.keys.find { |k| k.upcase == header }
230
+ return request[found_key] unless found_key.nil?
231
+ end
232
+ nil
233
+ end
234
+
235
+ end
236
+
237
+ end
238
+ end
239
+