wd_newrelic_rpm 3.5.6 → 3.5.8
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.
- data/CHANGELOG +43 -3
- data/Gemfile +6 -2
- data/LICENSE +23 -0
- data/lib/new_relic/agent.rb +50 -3
- data/lib/new_relic/agent/agent.rb +40 -60
- data/lib/new_relic/agent/configuration/defaults.rb +9 -3
- data/lib/new_relic/agent/configuration/server_source.rb +4 -0
- data/lib/new_relic/agent/cross_app_monitor.rb +239 -0
- data/lib/new_relic/agent/cross_app_tracing.rb +281 -0
- data/lib/new_relic/agent/database.rb +28 -10
- data/lib/new_relic/agent/error_collector.rb +5 -0
- data/lib/new_relic/agent/event_listener.rb +4 -0
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +58 -39
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +16 -3
- data/lib/new_relic/agent/instrumentation/net.rb +13 -11
- data/lib/new_relic/agent/instrumentation/queue_time.rb +50 -192
- data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +145 -0
- data/lib/new_relic/agent/instrumentation/rails4/errors.rb +45 -0
- data/lib/new_relic/agent/instrumentation/resque.rb +10 -10
- data/lib/new_relic/agent/instrumentation/sinatra.rb +19 -9
- data/lib/new_relic/agent/new_relic_service.rb +63 -9
- data/lib/new_relic/agent/pipe_service.rb +8 -12
- data/lib/new_relic/agent/rules_engine.rb +72 -0
- data/lib/new_relic/agent/shim_agent.rb +0 -1
- data/lib/new_relic/agent/sql_sampler.rb +3 -2
- data/lib/new_relic/agent/stats.rb +149 -0
- data/lib/new_relic/agent/stats_engine.rb +9 -0
- data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -24
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +84 -185
- data/lib/new_relic/agent/stats_engine/stats_hash.rb +58 -0
- data/lib/new_relic/agent/stats_engine/transactions.rb +10 -2
- data/lib/new_relic/agent/transaction_info.rb +31 -6
- data/lib/new_relic/agent/transaction_sample_builder.rb +19 -8
- data/lib/new_relic/agent/transaction_sampler.rb +17 -10
- data/lib/new_relic/helper.rb +32 -0
- data/lib/new_relic/local_environment.rb +24 -32
- data/lib/new_relic/okjson.rb +599 -0
- data/lib/new_relic/transaction_sample.rb +2 -1
- data/lib/new_relic/transaction_sample/segment.rb +2 -1
- data/lib/new_relic/version.rb +1 -1
- data/newrelic.yml +27 -41
- data/test/multiverse/suites/agent_only/Envfile +5 -1
- data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -4
- data/test/multiverse/suites/agent_only/config/newrelic.yml +1 -2
- data/test/multiverse/suites/agent_only/{cross_process_test.rb → cross_application_tracing_test.rb} +3 -3
- data/test/multiverse/suites/agent_only/key_transactions_test.rb +66 -0
- data/test/multiverse/suites/agent_only/marshaling_test.rb +9 -22
- data/test/multiverse/suites/agent_only/rename_rule_test.rb +57 -0
- data/test/multiverse/suites/agent_only/start_up_test.rb +1 -1
- data/test/multiverse/suites/agent_only/thread_profiling_test.rb +17 -6
- data/test/multiverse/suites/rails/error_tracing_test.rb +20 -8
- data/test/multiverse/suites/rails/queue_time_test.rb +2 -2
- data/test/multiverse/suites/resque/instrumentation_test.rb +4 -3
- data/test/multiverse/suites/sinatra/Envfile +2 -0
- data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -0
- data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +5 -5
- data/test/multiverse/suites/sinatra/sinatra_test.rb +77 -10
- data/test/new_relic/agent/agent/connect_test.rb +45 -1
- data/test/new_relic/agent/agent/start_worker_thread_test.rb +0 -3
- data/test/new_relic/agent/agent_test.rb +20 -40
- data/test/new_relic/agent/agent_test_controller_test.rb +27 -60
- data/test/new_relic/agent/busy_calculator_test.rb +1 -1
- data/test/new_relic/agent/configuration/server_source_test.rb +8 -3
- data/test/new_relic/agent/cross_app_monitor_test.rb +237 -0
- data/test/new_relic/agent/database_test.rb +60 -16
- data/test/new_relic/agent/error_collector_test.rb +28 -4
- data/test/new_relic/agent/event_listener_test.rb +23 -2
- data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
- data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +85 -12
- data/test/new_relic/agent/instrumentation/metric_frame_test.rb +95 -0
- data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +436 -59
- data/test/new_relic/agent/instrumentation/queue_time_test.rb +58 -357
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -5
- data/test/new_relic/agent/method_tracer_test.rb +4 -2
- data/test/new_relic/agent/new_relic_service_test.rb +108 -6
- data/test/new_relic/agent/pipe_channel_manager_test.rb +1 -1
- data/test/new_relic/agent/pipe_service_test.rb +9 -9
- data/test/new_relic/agent/rpm_agent_test.rb +0 -11
- data/test/new_relic/agent/rules_engine_test.rb +82 -0
- data/test/new_relic/agent/shim_agent_test.rb +0 -4
- data/test/new_relic/agent/sql_sampler_test.rb +7 -0
- data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +85 -0
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +110 -23
- data/test/new_relic/agent/stats_engine_test.rb +1 -46
- data/test/new_relic/agent/stats_hash_test.rb +93 -0
- data/test/new_relic/agent/stats_test.rb +197 -0
- data/test/new_relic/agent/transaction_info_test.rb +63 -11
- data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -3
- data/test/new_relic/agent/transaction_sampler_test.rb +92 -80
- data/test/new_relic/agent/worker_loop_test.rb +1 -1
- data/test/new_relic/agent_test.rb +35 -5
- data/test/new_relic/control_test.rb +1 -1
- data/test/new_relic/fake_collector.rb +87 -9
- data/test/new_relic/helper_test.rb +24 -0
- data/test/new_relic/metric_data_test.rb +11 -11
- data/test/new_relic/metric_spec_test.rb +1 -1
- data/test/script/ci.sh +1 -1
- data/test/test_contexts.rb +0 -1
- data/test/test_helper.rb +21 -3
- metadata +34 -41
- data/lib/new_relic/agent/cross_process_monitoring.rb +0 -187
- data/lib/new_relic/stats.rb +0 -337
- data/test/new_relic/agent/cross_process_monitoring_test.rb +0 -190
- data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +0 -133
- data/test/new_relic/fakes_sending_data.rb +0 -30
- 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
|
|
10
|
-
sensitive information (e.g. SQL parameters are masked), but SSL adds
|
|
11
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
data/lib/new_relic/agent.rb
CHANGED
|
@@ -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/
|
|
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
|
-
@
|
|
27
|
-
@
|
|
28
|
-
@
|
|
29
|
-
@
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
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
|
|
92
|
+
# cross application tracing ids and encoding
|
|
97
93
|
attr_reader :cross_process_id
|
|
98
|
-
attr_reader :
|
|
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
|
-
@
|
|
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.
|
|
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 =>
|
|
835
|
-
:force_reconnect =>
|
|
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,
|
|
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
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
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
|
-
|
|
129
|
+
].freeze
|
|
124
130
|
end
|
|
125
131
|
end
|
|
126
132
|
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
|
+
|