wd_newrelic_rpm 3.5.5 → 3.5.6
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/.gitignore +2 -0
- data/CHANGELOG +60 -0
- data/Rakefile +14 -18
- data/gem-public_cert.pem +20 -0
- data/lib/new_relic/agent.rb +3 -0
- data/lib/new_relic/agent/agent.rb +86 -97
- data/lib/new_relic/agent/agent_logger.rb +9 -1
- data/lib/new_relic/agent/busy_calculator.rb +5 -0
- data/lib/new_relic/agent/configuration/defaults.rb +3 -3
- data/lib/new_relic/agent/configuration/manager.rb +12 -0
- data/lib/new_relic/agent/configuration/mask_defaults.rb +1 -0
- data/lib/new_relic/agent/configuration/yaml_source.rb +5 -1
- data/lib/new_relic/agent/cross_process_monitoring.rb +164 -20
- data/lib/new_relic/agent/error_collector.rb +13 -2
- data/lib/new_relic/agent/event_listener.rb +39 -0
- data/lib/new_relic/agent/instrumentation/browser_monitoring_timings.rb +18 -8
- data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sinatra.rb +8 -1
- data/lib/new_relic/agent/new_relic_service.rb +90 -10
- data/lib/new_relic/agent/pipe_service.rb +9 -0
- data/lib/new_relic/agent/sql_sampler.rb +10 -3
- data/lib/new_relic/agent/stats_engine/transactions.rb +1 -0
- data/lib/new_relic/agent/thread_profiler.rb +20 -7
- data/lib/new_relic/agent/worker_loop.rb +2 -1
- data/lib/new_relic/coerce.rb +37 -0
- data/lib/new_relic/commands/deployments.rb +1 -1
- data/lib/new_relic/control/frameworks/rails.rb +29 -5
- data/lib/new_relic/control/frameworks/rails3.rb +2 -11
- data/lib/new_relic/control/instance_methods.rb +11 -7
- data/lib/new_relic/control/server_methods.rb +5 -37
- data/lib/new_relic/latest_changes.rb +31 -0
- data/lib/new_relic/local_environment.rb +1 -1
- data/lib/new_relic/metric_data.rb +13 -2
- data/lib/new_relic/noticed_error.rb +8 -1
- data/lib/new_relic/rack/agent_hooks.rb +20 -0
- data/lib/new_relic/rack/error_collector.rb +11 -1
- data/lib/new_relic/recipes.rb +32 -10
- data/lib/new_relic/transaction_sample.rb +12 -3
- data/lib/new_relic/transaction_sample/segment.rb +6 -3
- data/lib/new_relic/version.rb +10 -15
- data/newrelic.yml +12 -19
- data/newrelic_rpm.gemspec +22 -464
- data/test/multiverse/.gitignore +1 -0
- data/test/multiverse/lib/multiverse/environment.rb +1 -1
- data/test/multiverse/lib/multiverse/suite.rb +2 -0
- data/test/multiverse/suites/active_record/Envfile +3 -3
- data/test/multiverse/suites/active_record/ar_method_aliasing.rb +1 -1
- data/test/multiverse/suites/active_record/config/newrelic.yml +2 -2
- data/test/multiverse/suites/agent_only/Envfile +2 -1
- data/test/multiverse/suites/agent_only/config/newrelic.yml +3 -1
- data/test/multiverse/suites/agent_only/cross_process_test.rb +56 -0
- data/test/multiverse/suites/{logging → agent_only}/logging_test.rb +42 -22
- data/test/multiverse/suites/agent_only/no_dns_resolv.rb +17 -0
- data/test/multiverse/suites/{rum_auto_instrumentation/sanity_test.rb → agent_only/rum_instrumentation_test.rb} +25 -46
- data/test/multiverse/suites/agent_only/service_timeout_test.rb +6 -3
- data/test/multiverse/suites/agent_only/ssl_test.rb +22 -0
- data/test/multiverse/suites/{no_load → agent_only}/start_up_test.rb +9 -2
- data/test/multiverse/suites/agent_only/testing_app.rb +17 -0
- data/test/multiverse/suites/agent_only/thread_profiling_test.rb +6 -5
- data/test/multiverse/suites/datamapper/config/newrelic.yml +1 -1
- data/test/multiverse/suites/{rails_3_queue_time → rails}/Envfile +3 -0
- data/test/multiverse/suites/rails/app.rb +49 -0
- data/test/multiverse/suites/{rails_3_views → rails}/app/views/foos/_foo.html.haml +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/_a_partial.html.erb +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/_mid_partial.html.erb +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/_top_partial.html.erb +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/deep_partial.html.erb +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/haml_view.html.haml +0 -0
- data/test/multiverse/suites/{rails_3_views/app/views/test → rails/app/views/views}/index.html.erb +0 -0
- data/test/multiverse/suites/rails/config/newrelic.yml +32 -0
- data/test/multiverse/suites/{rails_3_error_tracing → rails}/error_tracing_test.rb +51 -88
- data/test/multiverse/suites/rails/gc_instrumentation_test.rb +79 -0
- data/test/multiverse/suites/{rails_3_queue_time → rails}/queue_time_test.rb +3 -23
- data/test/multiverse/suites/{rails_3_views → rails}/view_instrumentation_test.rb +21 -61
- data/test/multiverse/suites/resque/Envfile +7 -4
- data/test/multiverse/suites/resque/Rakefile +8 -0
- data/test/multiverse/suites/resque/config/newrelic.yml +1 -1
- data/test/multiverse/suites/resque/instrumentation_test.rb +118 -41
- data/test/multiverse/suites/resque/resque_setup.rb +15 -0
- data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -2
- data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +38 -0
- data/test/multiverse/suites/sinatra/sinatra_test.rb +17 -0
- data/test/multiverse/test/suite_examples/one/a/config/newrelic.yml +1 -1
- data/test/multiverse/test/suite_examples/one/b/config/newrelic.yml +1 -1
- data/test/new_relic/agent/agent/connect_test.rb +24 -100
- data/test/new_relic/agent/agent/start_worker_thread_test.rb +3 -3
- data/test/new_relic/agent/agent_test.rb +126 -31
- data/test/new_relic/agent/browser_monitoring_test.rb +1 -1
- data/test/new_relic/agent/busy_calculator_test.rb +8 -0
- data/test/new_relic/agent/configuration/manager_test.rb +28 -0
- data/test/new_relic/agent/configuration/yaml_source_test.rb +12 -2
- data/test/new_relic/agent/cross_process_monitoring_test.rb +144 -31
- data/test/new_relic/agent/error_collector_test.rb +16 -0
- data/test/new_relic/agent/event_listener_test.rb +46 -0
- data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +57 -30
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +1 -0
- data/test/new_relic/agent/new_relic_service_test.rb +95 -2
- data/test/new_relic/agent/pipe_channel_manager_test.rb +3 -3
- data/test/new_relic/agent/pipe_service_test.rb +21 -1
- data/test/new_relic/agent/rpm_agent_test.rb +1 -1
- data/test/new_relic/agent/sql_sampler_test.rb +20 -0
- data/test/new_relic/agent/thread_profiler_test.rb +53 -8
- data/test/new_relic/agent/worker_loop_test.rb +19 -16
- data/test/new_relic/agent_test.rb +1 -2
- data/test/new_relic/coerce_test.rb +65 -0
- data/test/new_relic/command/deployments_test.rb +1 -1
- data/test/new_relic/control_test.rb +23 -44
- data/test/new_relic/fake_collector.rb +34 -6
- data/test/new_relic/local_environment_test.rb +1 -1
- data/test/new_relic/metric_data_test.rb +29 -0
- data/test/new_relic/noticed_error_test.rb +8 -0
- data/test/new_relic/rack/agent_hooks_test.rb +30 -0
- data/test/new_relic/rack/error_collector_test.rb +16 -0
- data/test/new_relic/transaction_sample/segment_test.rb +7 -0
- data/test/new_relic/transaction_sample_test.rb +36 -8
- data/test/new_relic/version_number_test.rb +6 -30
- data/test/script/ci.sh +6 -5
- data/test/test_contexts.rb +2 -1
- data/test/test_helper.rb +23 -6
- data/ui/helpers/google_pie_chart.rb +1 -0
- metadata +68 -67
- data/newrelic_rpm.gemspec.erb +0 -54
- data/test/fixtures/gemspec_no_build.rb +0 -442
- data/test/fixtures/gemspec_with_build.rb +0 -442
- data/test/fixtures/gemspec_with_build_and_stage.rb +0 -442
- data/test/multiverse/suites/logging/Envfile +0 -4
- data/test/multiverse/suites/logging/config/newrelic.yml +0 -22
- data/test/multiverse/suites/monitor_mode_false/Envfile +0 -2
- data/test/multiverse/suites/monitor_mode_false/config/newrelic.yml +0 -25
- data/test/multiverse/suites/monitor_mode_false/no_dns_resolv.rb +0 -29
- data/test/multiverse/suites/no_load/Envfile +0 -2
- data/test/multiverse/suites/no_load/config/newrelic.yml +0 -22
- data/test/multiverse/suites/rails_3_error_tracing/Envfile +0 -15
- data/test/multiverse/suites/rails_3_error_tracing/config/newrelic.yml +0 -165
- data/test/multiverse/suites/rails_3_gc/Envfile +0 -8
- data/test/multiverse/suites/rails_3_gc/config/newrelic.yml +0 -167
- data/test/multiverse/suites/rails_3_gc/instrumentation_test.rb +0 -92
- data/test/multiverse/suites/rails_3_queue_time/config/newrelic.yml +0 -165
- data/test/multiverse/suites/rails_3_views/.gitignore +0 -3
- data/test/multiverse/suites/rails_3_views/Envfile +0 -16
- data/test/multiverse/suites/rails_3_views/config/newrelic.yml +0 -164
- data/test/multiverse/suites/resque/dump.rdb +0 -0
- data/test/multiverse/suites/rum_auto_instrumentation/Envfile +0 -4
- data/test/multiverse/suites/rum_auto_instrumentation/config/newrelic.yml +0 -24
- data/test/multiverse/suites/rum_auto_instrumentation/responses/worst_case_small.html +0 -5000
- data/test/new_relic/fake_service.rb +0 -53
@@ -32,6 +32,10 @@ module NewRelic
|
|
32
32
|
@log.debug(format_messages(msgs))
|
33
33
|
end
|
34
34
|
|
35
|
+
def is_startup_logger?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
35
39
|
# Allows for passing exceptions in explicitly, which format with backtrace
|
36
40
|
def format_messages(msgs)
|
37
41
|
msgs.map do |msg|
|
@@ -120,12 +124,16 @@ module NewRelic
|
|
120
124
|
end
|
121
125
|
end
|
122
126
|
|
123
|
-
#
|
127
|
+
# Base class for startup logging and testing in multiverse
|
124
128
|
class MemoryLogger
|
125
129
|
def initialize
|
126
130
|
@messages = []
|
127
131
|
end
|
128
132
|
|
133
|
+
def is_startup_logger?
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
129
137
|
attr_accessor :messages, :level
|
130
138
|
|
131
139
|
def fatal(*msgs)
|
@@ -31,10 +31,15 @@ module NewRelic
|
|
31
31
|
# instance variable accumulator. this is harvested when we send
|
32
32
|
# data to the server
|
33
33
|
def dispatcher_finish(end_time = nil)
|
34
|
+
# If #dispatcher_start hasn't been called at least once, abort early
|
35
|
+
return unless Thread.current[:busy_entries]
|
36
|
+
|
34
37
|
end_time ||= time_now
|
35
38
|
callers = Thread.current[:busy_entries] -= 1
|
39
|
+
|
36
40
|
# Ignore nested calls
|
37
41
|
return if callers > 0
|
42
|
+
|
38
43
|
@lock.synchronize do
|
39
44
|
if @entrypoint_stack.empty?
|
40
45
|
::NewRelic::Agent.logger.warn("Stack underflow tracking dispatcher entry and exit!\n #{caller.join(" \n")}")
|
@@ -30,7 +30,7 @@ module NewRelic
|
|
30
30
|
when 4
|
31
31
|
:rails4
|
32
32
|
else
|
33
|
-
Agent.logger.error "Detected unsupported Rails version #{Rails::VERSION::STRING}"
|
33
|
+
::NewRelic::Agent::Agent.logger.error "Detected unsupported Rails version #{Rails::VERSION::STRING}"
|
34
34
|
end
|
35
35
|
when defined?(::Sinatra) && defined?(::Sinatra::Base) then :sinatra
|
36
36
|
when defined?(::NewRelic::IA) then :external
|
@@ -55,8 +55,8 @@ module NewRelic
|
|
55
55
|
:api_host => 'rpm.newrelic.com',
|
56
56
|
:port => Proc.new { self[:ssl] ? 443 : 80 },
|
57
57
|
:api_port => Proc.new { self[:port] },
|
58
|
-
:ssl =>
|
59
|
-
:verify_certificate =>
|
58
|
+
:ssl => true,
|
59
|
+
:verify_certificate => true,
|
60
60
|
:sync_startup => false,
|
61
61
|
:send_data_on_exit => true,
|
62
62
|
:post_size_limit => 2 * 1024 * 1024, # 2 megs
|
@@ -20,10 +20,14 @@ module NewRelic
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def apply_config(source, level=0)
|
23
|
+
was_finished = finished_configuring?
|
24
|
+
|
23
25
|
invoke_callbacks(:add, source)
|
24
26
|
@config_stack.insert(level, source.freeze)
|
25
27
|
reset_cache
|
26
28
|
log_config(:add, source)
|
29
|
+
|
30
|
+
notify_finished_configuring if !was_finished && finished_configuring?
|
27
31
|
end
|
28
32
|
|
29
33
|
def remove_config(source=nil)
|
@@ -86,6 +90,14 @@ module NewRelic
|
|
86
90
|
end
|
87
91
|
end
|
88
92
|
|
93
|
+
def notify_finished_configuring
|
94
|
+
NewRelic::Agent.instance.events.notify(:finished_configuring)
|
95
|
+
end
|
96
|
+
|
97
|
+
def finished_configuring?
|
98
|
+
@config_stack.any? {|s| s.is_a?(ServerSource)}
|
99
|
+
end
|
100
|
+
|
89
101
|
def flattened
|
90
102
|
@config_stack.reverse.inject({}) do |flat,layer|
|
91
103
|
thawed_layer = layer.dup
|
@@ -4,6 +4,7 @@ module NewRelic
|
|
4
4
|
MASK_DEFAULTS = {
|
5
5
|
:'thread_profiler' => Proc.new { !NewRelic::Agent::ThreadProfiler.is_supported? },
|
6
6
|
:'thread_profiler.enabled' => Proc.new { !NewRelic::Agent::ThreadProfiler.is_supported? },
|
7
|
+
:verify_certificate => Proc.new{ true }
|
7
8
|
}
|
8
9
|
end
|
9
10
|
end
|
@@ -25,7 +25,11 @@ module NewRelic
|
|
25
25
|
license_key = ''
|
26
26
|
|
27
27
|
erb = ERB.new(file).result(binding)
|
28
|
-
|
28
|
+
confighash = YAML.load(erb)
|
29
|
+
::NewRelic::Agent.logger.error("Config (#{path}) doesn't include a '#{env}' environment!") unless
|
30
|
+
confighash.key?(env)
|
31
|
+
|
32
|
+
config = merge!(confighash[env] || {})
|
29
33
|
rescue ScriptError, StandardError => e
|
30
34
|
::NewRelic::Agent.logger.error("Unable to read configuration file #{path}: #{e}")
|
31
35
|
end
|
@@ -1,43 +1,187 @@
|
|
1
|
+
require 'new_relic/rack/agent_hooks'
|
2
|
+
require 'new_relic/agent/thread'
|
3
|
+
|
1
4
|
module NewRelic
|
2
5
|
module Agent
|
3
|
-
|
6
|
+
class CrossProcessMonitor
|
7
|
+
|
8
|
+
def initialize(events = nil)
|
9
|
+
# When we're starting up for real in the agent, we get passed the events
|
10
|
+
# Other spots can pull from the agent, during startup the agent doesn't exist yet!
|
11
|
+
events ||= Agent.instance.events
|
12
|
+
@trusted_ids = []
|
13
|
+
|
14
|
+
events.subscribe(:finished_configuring) do
|
15
|
+
finish_setup(Agent.config)
|
16
|
+
register_event_listeners
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def finish_setup(config)
|
21
|
+
@cross_process_id = config[:cross_process_id]
|
22
|
+
@encoding_key = config[:encoding_key]
|
23
|
+
@encoding_bytes = get_bytes(@encoding_key) unless @encoding_key.nil?
|
24
|
+
@trusted_ids = config[:trusted_account_ids] || []
|
25
|
+
end
|
26
|
+
|
27
|
+
# Expected sequence of events:
|
28
|
+
# :before_call will save our cross process request id to the thread
|
29
|
+
# :start_transaction will get called when a transaction starts up
|
30
|
+
# :after_call will write our response headers/metrics and clean up the thread
|
31
|
+
def register_event_listeners
|
32
|
+
NewRelic::Agent.logger.debug("Wiring up Cross Process monitoring to events after finished configuring")
|
33
|
+
|
34
|
+
events = Agent.instance.events
|
35
|
+
events.subscribe(:before_call) do |env|
|
36
|
+
save_client_cross_process_id(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
events.subscribe(:start_transaction) do |name|
|
40
|
+
set_transaction_custom_parameters
|
41
|
+
end
|
42
|
+
|
43
|
+
events.subscribe(:after_call) do |env, (status_code, headers, body)|
|
44
|
+
insert_response_header(env, headers)
|
45
|
+
end
|
46
|
+
|
47
|
+
events.subscribe(:notice_error) do |_, options|
|
48
|
+
set_error_custom_parameters(options)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Because we aren't in the right spot when our transaction actually
|
53
|
+
# starts, hold client_cross_process_id we get thread local until then.
|
54
|
+
THREAD_ID_KEY = :newrelic_client_cross_process_id
|
55
|
+
|
56
|
+
def save_client_cross_process_id(request_headers)
|
57
|
+
if should_process_request(request_headers)
|
58
|
+
NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = decoded_id(request_headers)
|
59
|
+
end
|
60
|
+
end
|
4
61
|
|
5
|
-
|
62
|
+
def clear_client_cross_process_id
|
63
|
+
NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = nil
|
64
|
+
end
|
6
65
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
66
|
+
def client_cross_process_id
|
67
|
+
NewRelic::Agent::AgentThread.current[THREAD_ID_KEY]
|
68
|
+
end
|
10
69
|
|
11
|
-
|
70
|
+
def insert_response_header(request_headers, response_headers)
|
71
|
+
unless client_cross_process_id.nil?
|
12
72
|
timings = NewRelic::Agent::BrowserMonitoring.timings
|
73
|
+
content_length = content_length_from_request(request_headers)
|
74
|
+
|
75
|
+
set_response_headers(response_headers, timings, content_length)
|
76
|
+
set_metrics(client_cross_process_id, timings)
|
77
|
+
|
78
|
+
clear_client_cross_process_id
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def should_process_request(request_headers)
|
83
|
+
return Agent.config[:'cross_process.enabled'] &&
|
84
|
+
@cross_process_id &&
|
85
|
+
trusts?(request_headers)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Expects an ID of format "12#345", and will only accept that!
|
89
|
+
def trusts?(request)
|
90
|
+
id = decoded_id(request)
|
91
|
+
split_id = id.match(/(\d+)#\d+/)
|
92
|
+
return false if split_id.nil?
|
13
93
|
|
14
|
-
|
15
|
-
|
16
|
-
|
94
|
+
@trusted_ids.include?(split_id.captures.first.to_i)
|
95
|
+
end
|
96
|
+
|
97
|
+
def set_response_headers(response_headers, timings, content_length)
|
98
|
+
response_headers['X-NewRelic-App-Data'] = build_payload(timings, content_length)
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_payload(timings, content_length)
|
102
|
+
|
103
|
+
# FIXME The transaction name might not be properly encoded. use a json generator
|
104
|
+
# For now we just handle quote characters by dropping them
|
105
|
+
transaction_name = timings.transaction_name.gsub(/["']/, "")
|
106
|
+
|
107
|
+
payload = %[["#{@cross_process_id}","#{transaction_name}",#{timings.queue_time_in_seconds},#{timings.app_time_in_seconds},#{content_length}] ]
|
108
|
+
payload = obfuscate_with_key(payload)
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_transaction_custom_parameters
|
112
|
+
# We expect to get the before call to set the id (if we have it) before
|
113
|
+
# this, and then write our custom parameter when the transaction starts
|
114
|
+
NewRelic::Agent.add_custom_parameters(:client_cross_process_id => client_cross_process_id) unless client_cross_process_id.nil?
|
115
|
+
end
|
116
|
+
|
117
|
+
def set_error_custom_parameters(options)
|
118
|
+
options[:client_cross_process_id] = client_cross_process_id unless client_cross_process_id.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def set_metrics(id, timings)
|
122
|
+
metric = NewRelic::Agent.instance.stats_engine.get_stats_no_scope("ClientApplication/#{id}/all")
|
123
|
+
metric.record_data_point(timings.app_time_in_seconds)
|
124
|
+
end
|
125
|
+
|
126
|
+
def obfuscate_with_key(text)
|
127
|
+
Base64.encode64(encode_with_key(text)).chomp
|
128
|
+
end
|
17
129
|
|
18
|
-
|
19
|
-
|
20
|
-
|
130
|
+
def decode_with_key(text)
|
131
|
+
encode_with_key(Base64.decode64(text))
|
132
|
+
end
|
133
|
+
|
134
|
+
NEWRELIC_ID_HEADER_KEYS = %w{X-NewRelic-ID HTTP_X_NEWRELIC_ID X_NEWRELIC_ID}
|
135
|
+
CONTENT_LENGTH_HEADER_KEYS = %w{Content-Length HTTP_CONTENT_LENGTH CONTENT_LENGTH}
|
136
|
+
|
137
|
+
def decoded_id(request)
|
138
|
+
encoded_id = from_headers(request, NEWRELIC_ID_HEADER_KEYS)
|
139
|
+
return "" if encoded_id.nil?
|
140
|
+
|
141
|
+
decode_with_key(encoded_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
def content_length_from_request(request)
|
145
|
+
from_headers(request, CONTENT_LENGTH_HEADER_KEYS) || -1
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
# Ruby 1.8.6 doesn't support the bytes method on strings.
|
153
|
+
def get_bytes(value)
|
154
|
+
return [] if value.nil?
|
155
|
+
|
156
|
+
bytes = []
|
157
|
+
value.each_byte do |b|
|
158
|
+
bytes << b
|
21
159
|
end
|
160
|
+
bytes
|
22
161
|
end
|
23
162
|
|
24
|
-
def
|
25
|
-
|
163
|
+
def encode_with_key(text)
|
164
|
+
key_bytes = @encoding_bytes
|
165
|
+
|
166
|
+
encoded = ""
|
26
167
|
index = 0
|
27
168
|
text.each_byte{|byte|
|
28
|
-
|
169
|
+
encoded.concat((byte ^ key_bytes[index % key_bytes.length].to_i))
|
29
170
|
index+=1
|
30
171
|
}
|
31
|
-
|
32
|
-
[obfuscated].pack("m0").gsub("\n", '')
|
172
|
+
encoded
|
33
173
|
end
|
34
174
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
175
|
+
def from_headers(request, try_keys)
|
176
|
+
# For lookups, upcase all our keys on both sides just to be safe
|
177
|
+
upcased_keys = try_keys.map{|k| k.upcase}
|
178
|
+
upcased_keys.each do |header|
|
179
|
+
found_key = request.keys.find { |k| k.upcase == header }
|
180
|
+
return request[found_key] unless found_key.nil?
|
38
181
|
end
|
39
182
|
nil
|
40
183
|
end
|
184
|
+
|
41
185
|
end
|
42
186
|
end
|
43
187
|
end
|
@@ -21,6 +21,8 @@ module NewRelic
|
|
21
21
|
# Returns a new error collector
|
22
22
|
def initialize
|
23
23
|
@errors = []
|
24
|
+
@seen_error_ids = []
|
25
|
+
|
24
26
|
# lookup of exception class names to ignore. Hash for fast access
|
25
27
|
@ignore = {}
|
26
28
|
@capture_source = Agent.config[:'error_collector.capture_source']
|
@@ -92,7 +94,11 @@ module NewRelic
|
|
92
94
|
end
|
93
95
|
|
94
96
|
# Increments a statistic that tracks total error rate
|
95
|
-
|
97
|
+
# Be sure not to double-count same exception. This clears per harvest.
|
98
|
+
def increment_error_count!(exception)
|
99
|
+
return if @seen_error_ids.include?(exception.object_id)
|
100
|
+
@seen_error_ids << exception.object_id
|
101
|
+
|
96
102
|
NewRelic::Agent.get_stats("Errors/all").increment_count
|
97
103
|
end
|
98
104
|
|
@@ -102,7 +108,7 @@ module NewRelic
|
|
102
108
|
def should_exit_notice_error?(exception)
|
103
109
|
if enabled?
|
104
110
|
if !error_is_ignored?(exception)
|
105
|
-
increment_error_count!
|
111
|
+
increment_error_count!(exception)
|
106
112
|
return exception.nil? # exit early if the exception is nil
|
107
113
|
end
|
108
114
|
end
|
@@ -211,6 +217,7 @@ module NewRelic
|
|
211
217
|
|
212
218
|
include NoticeError
|
213
219
|
|
220
|
+
|
214
221
|
# Notice the error with the given available options:
|
215
222
|
#
|
216
223
|
# * <tt>:uri</tt> => The request path, minus any request params or query string.
|
@@ -223,6 +230,7 @@ module NewRelic
|
|
223
230
|
# If exception is nil, the error count is bumped and no traced error is recorded
|
224
231
|
def notice_error(exception, options={})
|
225
232
|
return if should_exit_notice_error?(exception)
|
233
|
+
NewRelic::Agent.instance.events.notify(:notice_error, exception, options)
|
226
234
|
action_path = fetch_from_options(options, :metric, (NewRelic::Agent.instance.stats_engine.scope_name || ''))
|
227
235
|
exception_options = error_params_from_options(options).merge(exception_info(exception))
|
228
236
|
add_to_error_queue(NewRelic::NoticedError.new(action_path, exception_options, exception))
|
@@ -238,6 +246,9 @@ module NewRelic
|
|
238
246
|
errors = @errors
|
239
247
|
@errors = []
|
240
248
|
|
249
|
+
# Only expect to re-see errors on same request, so clear on harvest
|
250
|
+
@seen_error_ids = []
|
251
|
+
|
241
252
|
if unsent_errors && !unsent_errors.empty?
|
242
253
|
errors = unsent_errors + errors
|
243
254
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NewRelic::Agent
|
2
|
+
# Basic mechanism for the agent instance to provide agent-wide eventing.
|
3
|
+
# It is intended to keep different pieces of the app decoupled from each other.
|
4
|
+
#
|
5
|
+
# While an EventListener could be used elsewhere, it's strongly expected
|
6
|
+
# your eventing needs should be met by the agent's instance.
|
7
|
+
class EventListener
|
8
|
+
|
9
|
+
attr_accessor :runaway_threshold
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@events = {}
|
13
|
+
@runaway_threshold = 100
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscribe(event, &handler)
|
17
|
+
@events[event] ||= []
|
18
|
+
@events[event] << handler
|
19
|
+
check_for_runaway_subscriptions(event)
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_for_runaway_subscriptions(event)
|
23
|
+
count = @events[event].size
|
24
|
+
NewRelic::Agent.logger.debug("Run-away event subscription on #{event}? Subscribed #{count}") if count > @runaway_threshold
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify(event, *args)
|
28
|
+
return unless @events.has_key?(event)
|
29
|
+
|
30
|
+
@events[event].each do |handler|
|
31
|
+
begin
|
32
|
+
handler.call(*args)
|
33
|
+
rescue => err
|
34
|
+
NewRelic::Agent.logger.debug("Failure during notify for #{@event}", err)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -4,37 +4,47 @@ module NewRelic
|
|
4
4
|
class BrowserMonitoringTimings
|
5
5
|
|
6
6
|
def initialize(queue_time_in_seconds, transaction)
|
7
|
+
@now = Time.now.to_i
|
7
8
|
if transaction.nil?
|
8
9
|
@start_time_in_seconds = 0.0
|
9
10
|
else
|
10
11
|
@transaction_name = transaction.transaction_name
|
11
|
-
@start_time_in_seconds = transaction.start_time
|
12
|
+
@start_time_in_seconds = transaction.start_time.to_i
|
12
13
|
end
|
13
14
|
|
14
|
-
@queue_time_in_seconds = queue_time_in_seconds
|
15
|
+
@queue_time_in_seconds = clamp_to_positive(queue_time_in_seconds)
|
15
16
|
end
|
16
17
|
|
17
|
-
attr_reader :transaction_name
|
18
|
+
attr_reader :transaction_name,
|
19
|
+
:start_time_in_seconds, :queue_time_in_seconds
|
18
20
|
|
19
21
|
def start_time_in_millis
|
20
22
|
convert_to_milliseconds(@start_time_in_seconds)
|
21
23
|
end
|
22
24
|
|
23
25
|
def queue_time_in_millis
|
24
|
-
convert_to_milliseconds(
|
26
|
+
convert_to_milliseconds(queue_time_in_seconds)
|
25
27
|
end
|
26
28
|
|
27
29
|
def app_time_in_millis
|
28
|
-
convert_to_milliseconds(
|
30
|
+
convert_to_milliseconds(app_time_in_seconds)
|
31
|
+
end
|
32
|
+
|
33
|
+
def app_time_in_seconds
|
34
|
+
@now - @start_time_in_seconds
|
29
35
|
end
|
30
36
|
|
31
37
|
private
|
32
38
|
|
33
|
-
def convert_to_milliseconds(
|
34
|
-
|
35
|
-
|
39
|
+
def convert_to_milliseconds(value_in_seconds)
|
40
|
+
clamp_to_positive((value_in_seconds.to_f * 1000.0).round)
|
41
|
+
end
|
42
|
+
|
43
|
+
def clamp_to_positive(value)
|
44
|
+
return 0.0 if value.nil? || value < 0
|
36
45
|
value
|
37
46
|
end
|
47
|
+
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|