newrelic_rpm 8.8.0 → 8.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +31 -3
- data/.simplecov +5 -0
- data/CHANGELOG.md +18 -0
- data/lib/new_relic/agent/agent/shutdown.rb +34 -0
- data/lib/new_relic/agent/agent/special_startup.rb +70 -0
- data/lib/new_relic/agent/agent/start_worker_thread.rb +163 -0
- data/lib/new_relic/agent/agent/startup.rb +196 -0
- data/lib/new_relic/agent/agent.rb +10 -439
- data/lib/new_relic/agent/agent_logger.rb +3 -5
- data/lib/new_relic/agent/autostart.rb +1 -1
- data/lib/new_relic/agent/commands/agent_command_router.rb +1 -1
- data/lib/new_relic/agent/configuration/default_source.rb +10 -10
- data/lib/new_relic/agent/configuration/manager.rb +2 -2
- data/lib/new_relic/agent/connect/request_builder.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +11 -3
- data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +9 -2
- data/lib/new_relic/agent/instrumentation/sunspot.rb +2 -2
- data/lib/new_relic/agent/monitors/cross_app_monitor.rb +1 -1
- data/lib/new_relic/agent/pipe_channel_manager.rb +6 -1
- data/lib/new_relic/agent/pipe_service.rb +1 -1
- data/lib/new_relic/agent/samplers/delayed_job_sampler.rb +1 -1
- data/lib/new_relic/agent/sql_sampler.rb +1 -1
- data/lib/new_relic/agent/system_info.rb +59 -44
- data/lib/new_relic/agent/threading/thread_profile.rb +2 -2
- data/lib/new_relic/agent/transaction/transaction_sample_buffer.rb +1 -1
- data/lib/new_relic/agent/transaction_sampler.rb +1 -1
- data/lib/new_relic/agent.rb +1 -1
- data/lib/new_relic/coerce.rb +1 -1
- data/lib/new_relic/collection_helper.rb +1 -1
- data/lib/new_relic/dependency_detection.rb +2 -2
- data/lib/new_relic/local_environment.rb +1 -1
- data/lib/new_relic/metric_data.rb +1 -1
- data/lib/new_relic/metric_spec.rb +1 -1
- data/lib/new_relic/rack/browser_monitoring.rb +1 -1
- data/lib/new_relic/version.rb +1 -1
- data/lib/tasks/config.rake +4 -0
- data/lib/tasks/coverage_report.rake +22 -0
- data/lib/tasks/install.rake +4 -0
- data/lib/tasks/multiverse.rake +4 -0
- data/lib/tasks/tests.rake +4 -1
- data/newrelic.yml +1 -1
- data/newrelic_rpm.gemspec +4 -1
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e16c313766a7d9c51bc1a1c3031f666747b4a3aefa773cf226d28510d90d6658
|
4
|
+
data.tar.gz: cb464a40400ce3b4a41178f737fa804952bfe22de66dc88f73deccaa256520a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93915783c6110bbc027d03d614b5ad582ba0bf73b93933d4a93f11a7e698fd82436c74b3b941c27846e6e1ae408b7d8929b9c1d7dcae0e6103ed3ded2836c9c2
|
7
|
+
data.tar.gz: f7a5196e10ecd5dd88ed16b218cdcb760511d3747f1c821ccc7f4b6c5e22c28a1e08d581022513d04e6540d6d5cd62419cc587c05c8154d4a7abe6a574944a97
|
data/.gitignore
CHANGED
@@ -15,6 +15,7 @@ tags
|
|
15
15
|
*.swp
|
16
16
|
*.swo
|
17
17
|
|
18
|
+
coverage
|
18
19
|
/doc/
|
19
20
|
/log/
|
20
21
|
/lerg/
|
@@ -33,7 +34,9 @@ infinite_tracing/test/new_relic/infinite_tracing/log
|
|
33
34
|
test/fixtures/cross_agent_tests/*/README.md
|
34
35
|
node_modules/
|
35
36
|
yarn.lock
|
37
|
+
package-lock.json
|
36
38
|
errors.txt
|
37
39
|
.history/
|
38
40
|
vendor/
|
39
41
|
Brewfile.lock.json
|
42
|
+
.github/actions/simplecov-report/lib/
|
data/.rubocop.yml
CHANGED
@@ -2,9 +2,9 @@ require:
|
|
2
2
|
- rubocop-performance
|
3
3
|
|
4
4
|
AllCops:
|
5
|
-
# Prevent RuboCop from exploding when it finds an older-than-2.4 .ruby-version
|
6
5
|
TargetRubyVersion: 2.7
|
7
6
|
Exclude: []
|
7
|
+
NewCops: enable
|
8
8
|
|
9
9
|
Bundler/DuplicatedGem:
|
10
10
|
Enabled: true
|
@@ -33,7 +33,7 @@ Bundler/InsecureProtocolSource:
|
|
33
33
|
Bundler/OrderedGems:
|
34
34
|
Enabled: false
|
35
35
|
|
36
|
-
Gemspec/
|
36
|
+
Gemspec/DeprecatedAttributeAssignment:
|
37
37
|
Enabled: true
|
38
38
|
|
39
39
|
Gemspec/DuplicatedAssignment:
|
@@ -44,6 +44,9 @@ Gemspec/DuplicatedAssignment:
|
|
44
44
|
Gemspec/OrderedDependencies:
|
45
45
|
Enabled: false
|
46
46
|
|
47
|
+
Gemspec/RequireMFA:
|
48
|
+
Enabled: false
|
49
|
+
|
47
50
|
Gemspec/RequiredRubyVersion:
|
48
51
|
Enabled: false
|
49
52
|
|
@@ -238,7 +241,7 @@ Layout/IndentationStyle:
|
|
238
241
|
Layout/IndentationWidth:
|
239
242
|
Enabled: true
|
240
243
|
Width: 2
|
241
|
-
|
244
|
+
AllowedPatterns: []
|
242
245
|
|
243
246
|
Layout/InitialIndentation:
|
244
247
|
Enabled: true
|
@@ -1317,6 +1320,12 @@ Style/EndBlock:
|
|
1317
1320
|
Style/EndlessMethod:
|
1318
1321
|
Enabled: false
|
1319
1322
|
|
1323
|
+
# The use of Dir.home should be preferred over ENV['HOME'], but as of
|
1324
|
+
# JRuby 9.3.3.0 the use of ENV['HOME'] is required to deliver the desired
|
1325
|
+
# functionality
|
1326
|
+
Style/EnvHome:
|
1327
|
+
Enabled: false
|
1328
|
+
|
1320
1329
|
# Disabling for now
|
1321
1330
|
Style/EvalWithLocation:
|
1322
1331
|
Enabled: false
|
@@ -1333,6 +1342,11 @@ Style/ExplicitBlockArgument:
|
|
1333
1342
|
Style/ExponentialNotation:
|
1334
1343
|
Enabled: false
|
1335
1344
|
|
1345
|
+
# TODO: MAJOR VERSION - Re-enable FetchEnvVar after dropping support for Ruby 2.2
|
1346
|
+
# Ruby 2.3+ allows for ENV.fetch('KEY') { default }
|
1347
|
+
Style/FetchEnvVar:
|
1348
|
+
Enabled: false
|
1349
|
+
|
1336
1350
|
Style/FloatDivision:
|
1337
1351
|
Enabled: false
|
1338
1352
|
|
@@ -1446,6 +1460,13 @@ Style/LineEndConcatenation:
|
|
1446
1460
|
Enabled: false
|
1447
1461
|
# SafeAutoCorrect: false
|
1448
1462
|
|
1463
|
+
# TODO: OLD RUBIES - enable this cop after support for Ruby <= 2.5 has been
|
1464
|
+
# dropped. NewRelic::Agent::InfiniteTracing::Transformer.hash_to_attributes
|
1465
|
+
# currently does `values.map {}.to_h`. Newer Rubies should
|
1466
|
+
# use `values.to_h {}` instead.
|
1467
|
+
Style/MapToHash:
|
1468
|
+
Enabled: false
|
1469
|
+
|
1449
1470
|
Style/MethodCallWithArgsParentheses:
|
1450
1471
|
Enabled: false
|
1451
1472
|
|
@@ -1589,6 +1610,13 @@ Style/NumericPredicate:
|
|
1589
1610
|
Style/OneLineConditional:
|
1590
1611
|
Enabled: true
|
1591
1612
|
|
1613
|
+
# TODO: UNIT TESTS - tests relying on OpenStruct should be refactored to not
|
1614
|
+
# do so, given that the use of OpenStruct instances can
|
1615
|
+
# give a false sense of security with their extreme
|
1616
|
+
# flexibility that may not match realistic code behavior.
|
1617
|
+
Style/OpenStructUse:
|
1618
|
+
Enabled: false
|
1619
|
+
|
1592
1620
|
Style/OptionHash:
|
1593
1621
|
Enabled: false
|
1594
1622
|
|
data/.simplecov
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# New Relic Ruby Agent Release Notes #
|
2
2
|
|
3
|
+
|
4
|
+
## v8.9.0
|
5
|
+
|
6
|
+
|
7
|
+
* **Add support for Dalli 3.1.0 to Dalli 3.2.2**
|
8
|
+
|
9
|
+
Dalli versions 3.1.0 and above include breaking changes where the agent previously hooked into the gem. We have updated our instrumentation to correctly hook into Dalli 3.1.0 and above. At this time, 3.2.2 is the latest Dalli version and is confirmed to be supported.
|
10
|
+
|
11
|
+
|
12
|
+
* **Bugfix: Infinite Tracing hung on connection restart**
|
13
|
+
|
14
|
+
Previously, when using infinite tracing, the agent would intermittently encounter a deadlock when attempting to restart the infinite tracing connection. This bug would prevent the agent from sending all data types, including non-infinite-tracing-related data. This change reworks how we restart infinite tracing to prevent potential deadlocks.
|
15
|
+
|
16
|
+
* **Bugfix: Use read_nonblock instead of read on pipe**
|
17
|
+
|
18
|
+
Previously, our PipeChannelManager was using read which could cause Resque jobs to get stuck in some versions. This change updates the PipeChannelManager to use read_nonblock instead. This method can leverage error handling to allow the instrumentation to gracefully log a message and exit the stuck Resque job.
|
19
|
+
|
20
|
+
|
3
21
|
## v8.8.0
|
4
22
|
|
5
23
|
* **Support Makara database adapters with ActiveRecord**
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module Shutdown
|
8
|
+
# Attempt a graceful shutdown of the agent, flushing any remaining
|
9
|
+
# data.
|
10
|
+
def shutdown
|
11
|
+
return unless started?
|
12
|
+
::NewRelic::Agent.logger.info "Starting Agent shutdown"
|
13
|
+
|
14
|
+
stop_event_loop
|
15
|
+
trap_signals_for_litespeed
|
16
|
+
untraced_graceful_disconnect
|
17
|
+
revert_to_default_configuration
|
18
|
+
|
19
|
+
@started = nil
|
20
|
+
Control.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
def untraced_graceful_disconnect
|
24
|
+
begin
|
25
|
+
NewRelic::Agent.disable_all_tracing do
|
26
|
+
graceful_disconnect
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
::NewRelic::Agent.logger.error e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module SpecialStartup
|
8
|
+
# If we're using a dispatcher that forks before serving
|
9
|
+
# requests, we need to wait until the children are forked
|
10
|
+
# before connecting, otherwise the parent process sends useless data
|
11
|
+
def using_forking_dispatcher?
|
12
|
+
# TODO: MAJOR VERSION - remove :rainbows
|
13
|
+
if [:puma, :passenger, :rainbows, :unicorn].include? Agent.config[:dispatcher]
|
14
|
+
::NewRelic::Agent.logger.info "Deferring startup of agent reporting thread because #{Agent.config[:dispatcher]} may fork."
|
15
|
+
true
|
16
|
+
else
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return true if we're using resque and it hasn't had a chance to (potentially)
|
22
|
+
# daemonize itself. This avoids hanging when there's a Thread started
|
23
|
+
# before Resque calls Process.daemon (Jira RUBY-857)
|
24
|
+
def defer_for_resque?
|
25
|
+
NewRelic::Agent.config[:dispatcher] == :resque &&
|
26
|
+
NewRelic::Agent::Instrumentation::Resque::Helper.resque_fork_per_job? &&
|
27
|
+
!PipeChannelManager.listener.started?
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_resque_child_process?
|
31
|
+
defined?(@service) && @service.is_a?(PipeService)
|
32
|
+
end
|
33
|
+
|
34
|
+
def defer_for_delayed_job?
|
35
|
+
NewRelic::Agent.config[:dispatcher] == :delayed_job &&
|
36
|
+
!NewRelic::DelayedJobInjection.worker_name
|
37
|
+
end
|
38
|
+
|
39
|
+
# This matters when the following three criteria are met:
|
40
|
+
#
|
41
|
+
# 1. A Sinatra 'classic' application is being run
|
42
|
+
# 2. The app is being run by executing the main file directly, rather
|
43
|
+
# than via a config.ru file.
|
44
|
+
# 3. newrelic_rpm is required *after* sinatra
|
45
|
+
#
|
46
|
+
# In this case, the entire application runs from an at_exit handler in
|
47
|
+
# Sinatra, and if we were to install ours, it would be executed before
|
48
|
+
# the one in Sinatra, meaning that we'd shutdown the agent too early
|
49
|
+
# and never collect any data.
|
50
|
+
def sinatra_classic_app?
|
51
|
+
(
|
52
|
+
defined?(Sinatra::Application) &&
|
53
|
+
Sinatra::Application.respond_to?(:run) &&
|
54
|
+
Sinatra::Application.run?
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def should_install_exit_handler?
|
59
|
+
return false unless Agent.config[:send_data_on_exit]
|
60
|
+
!sinatra_classic_app? || Agent.config[:force_install_exit_handler]
|
61
|
+
end
|
62
|
+
|
63
|
+
def install_exit_handler
|
64
|
+
return unless should_install_exit_handler?
|
65
|
+
NewRelic::Agent.logger.debug("Installing at_exit handler")
|
66
|
+
at_exit { shutdown }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module StartWorkerThread
|
8
|
+
LOG_ONCE_KEYS_RESET_PERIOD = 60.0
|
9
|
+
|
10
|
+
TRANSACTION_EVENT_DATA = "transaction_event_data".freeze
|
11
|
+
CUSTOM_EVENT_DATA = "custom_event_data".freeze
|
12
|
+
ERROR_EVENT_DATA = "error_event_data".freeze
|
13
|
+
SPAN_EVENT_DATA = "span_event_data".freeze
|
14
|
+
LOG_EVENT_DATA = "log_event_data".freeze
|
15
|
+
|
16
|
+
# Try to launch the worker thread and connect to the server.
|
17
|
+
#
|
18
|
+
# See #connect for a description of connection_options.
|
19
|
+
def start_worker_thread(connection_options = {})
|
20
|
+
if disable = NewRelic::Agent.config[:disable_harvest_thread]
|
21
|
+
NewRelic::Agent.logger.info "Not starting Ruby Agent worker thread because :disable_harvest_thread is #{disable}"
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
::NewRelic::Agent.logger.debug "Creating Ruby Agent worker thread."
|
26
|
+
@worker_thread = Threading::AgentThread.create('Worker Loop') do
|
27
|
+
deferred_work!(connection_options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_event_loop
|
32
|
+
EventLoop.new
|
33
|
+
end
|
34
|
+
|
35
|
+
# If the @worker_thread encounters an error during the attempt to connect to the collector
|
36
|
+
# then the connect attempts enter an exponential backoff retry loop. To avoid potential
|
37
|
+
# race conditions with shutting down while also attempting to reconnect, we join the
|
38
|
+
# @worker_thread with a timeout threshold. This allows potentially connecting and flushing
|
39
|
+
# pending data to the server, but without waiting indefinitely for a reconnect to succeed.
|
40
|
+
# The use-case where this typically arises is in cronjob scheduled rake tasks where there's
|
41
|
+
# also some network stability/latency issues happening.
|
42
|
+
def stop_event_loop
|
43
|
+
@event_loop.stop if @event_loop
|
44
|
+
# Wait the end of the event loop thread.
|
45
|
+
if @worker_thread
|
46
|
+
unless @worker_thread.join(3)
|
47
|
+
::NewRelic::Agent.logger.debug "Event loop thread did not stop within 3 seconds"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Certain event types may sometimes need to be on the same interval as metrics,
|
53
|
+
# so we will check config assigned in EventHarvestConfig to determine the interval
|
54
|
+
# on which to report them
|
55
|
+
def interval_for event_type
|
56
|
+
interval = Agent.config[:"event_report_period.#{event_type}"]
|
57
|
+
:"#{interval}_second_harvest"
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_and_run_event_loop
|
61
|
+
data_harvest = :"#{Agent.config[:data_report_period]}_second_harvest"
|
62
|
+
event_harvest = :"#{Agent.config[:event_report_period]}_second_harvest"
|
63
|
+
|
64
|
+
@event_loop = create_event_loop
|
65
|
+
@event_loop.on(data_harvest) do
|
66
|
+
transmit_data
|
67
|
+
end
|
68
|
+
|
69
|
+
@event_loop.on(interval_for TRANSACTION_EVENT_DATA) do
|
70
|
+
transmit_analytic_event_data
|
71
|
+
end
|
72
|
+
@event_loop.on(interval_for CUSTOM_EVENT_DATA) do
|
73
|
+
transmit_custom_event_data
|
74
|
+
end
|
75
|
+
@event_loop.on(interval_for ERROR_EVENT_DATA) do
|
76
|
+
transmit_error_event_data
|
77
|
+
end
|
78
|
+
@event_loop.on(interval_for SPAN_EVENT_DATA) do
|
79
|
+
transmit_span_event_data
|
80
|
+
end
|
81
|
+
@event_loop.on(interval_for LOG_EVENT_DATA) do
|
82
|
+
transmit_log_event_data
|
83
|
+
end
|
84
|
+
|
85
|
+
@event_loop.on(:reset_log_once_keys) do
|
86
|
+
::NewRelic::Agent.logger.clear_already_logged
|
87
|
+
end
|
88
|
+
@event_loop.fire_every(Agent.config[:data_report_period], data_harvest)
|
89
|
+
@event_loop.fire_every(Agent.config[:event_report_period], event_harvest)
|
90
|
+
@event_loop.fire_every(LOG_ONCE_KEYS_RESET_PERIOD, :reset_log_once_keys)
|
91
|
+
|
92
|
+
@event_loop.run
|
93
|
+
end
|
94
|
+
|
95
|
+
# Handles the case where the server tells us to restart -
|
96
|
+
# this clears the data, clears connection attempts, and
|
97
|
+
# waits a while to reconnect.
|
98
|
+
def handle_force_restart(error)
|
99
|
+
::NewRelic::Agent.logger.debug error.message
|
100
|
+
drop_buffered_data
|
101
|
+
@service.force_restart if @service
|
102
|
+
@connect_state = :pending
|
103
|
+
sleep 30
|
104
|
+
end
|
105
|
+
|
106
|
+
# when a disconnect is requested, stop the current thread, which
|
107
|
+
# is the worker thread that gathers data and talks to the
|
108
|
+
# server.
|
109
|
+
def handle_force_disconnect(error)
|
110
|
+
::NewRelic::Agent.logger.warn "Agent received a ForceDisconnectException from the server, disconnecting. (#{error.message})"
|
111
|
+
disconnect
|
112
|
+
end
|
113
|
+
|
114
|
+
# Handles an unknown error in the worker thread by logging
|
115
|
+
# it and disconnecting the agent, since we are now in an
|
116
|
+
# unknown state.
|
117
|
+
def handle_other_error(error)
|
118
|
+
::NewRelic::Agent.logger.error "Unhandled error in worker thread, disconnecting."
|
119
|
+
# These errors are fatal (that is, they will prevent the agent from
|
120
|
+
# reporting entirely), so we really want backtraces when they happen
|
121
|
+
::NewRelic::Agent.logger.log_exception(:error, error)
|
122
|
+
disconnect
|
123
|
+
end
|
124
|
+
|
125
|
+
# a wrapper method to handle all the errors that can happen
|
126
|
+
# in the connection and worker thread system. This
|
127
|
+
# guarantees a no-throw from the background thread.
|
128
|
+
def catch_errors
|
129
|
+
yield
|
130
|
+
rescue NewRelic::Agent::ForceRestartException => e
|
131
|
+
handle_force_restart(e)
|
132
|
+
retry
|
133
|
+
rescue NewRelic::Agent::ForceDisconnectException => e
|
134
|
+
handle_force_disconnect(e)
|
135
|
+
rescue => e
|
136
|
+
handle_other_error(e)
|
137
|
+
end
|
138
|
+
|
139
|
+
# This is the method that is run in a new thread in order to
|
140
|
+
# background the harvesting and sending of data during the
|
141
|
+
# normal operation of the agent.
|
142
|
+
#
|
143
|
+
# Takes connection options that determine how we should
|
144
|
+
# connect to the server, and loops endlessly - typically we
|
145
|
+
# never return from this method unless we're shutting down
|
146
|
+
# the agent
|
147
|
+
def deferred_work!(connection_options)
|
148
|
+
catch_errors do
|
149
|
+
NewRelic::Agent.disable_all_tracing do
|
150
|
+
connect(connection_options)
|
151
|
+
if connected?
|
152
|
+
create_and_run_event_loop
|
153
|
+
# never reaches here unless there is a problem or
|
154
|
+
# the agent is exiting
|
155
|
+
else
|
156
|
+
::NewRelic::Agent.logger.debug "No connection. Worker thread ending."
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module Startup
|
8
|
+
# True if we have initialized and completed 'start'
|
9
|
+
def started?
|
10
|
+
@started
|
11
|
+
end
|
12
|
+
|
13
|
+
# Check whether we have already started, which is an error condition
|
14
|
+
def already_started?
|
15
|
+
if started?
|
16
|
+
::NewRelic::Agent.logger.error("Agent Started Already!")
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Logs a bunch of data and starts the agent, if needed
|
22
|
+
def start
|
23
|
+
return unless agent_should_start?
|
24
|
+
|
25
|
+
log_startup
|
26
|
+
check_config_and_start_agent
|
27
|
+
log_version_and_pid
|
28
|
+
|
29
|
+
events.subscribe(:initial_configuration_complete) do
|
30
|
+
log_ignore_url_regexes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sanity-check the agent configuration and start the agent,
|
35
|
+
# setting up the worker thread and the exit handler to shut
|
36
|
+
# down the agent
|
37
|
+
def check_config_and_start_agent
|
38
|
+
return unless monitoring? && has_correct_license_key?
|
39
|
+
return if using_forking_dispatcher?
|
40
|
+
setup_and_start_agent
|
41
|
+
end
|
42
|
+
|
43
|
+
# This is the shared method between the main agent startup and the
|
44
|
+
# after_fork call restarting the thread in deferred dispatchers.
|
45
|
+
#
|
46
|
+
# Treatment of @started and env report is important to get right.
|
47
|
+
def setup_and_start_agent(options = {})
|
48
|
+
@started = true
|
49
|
+
@harvester.mark_started
|
50
|
+
|
51
|
+
unless in_resque_child_process?
|
52
|
+
install_exit_handler
|
53
|
+
environment_for_connect
|
54
|
+
@harvest_samplers.load_samplers unless Agent.config[:disable_samplers]
|
55
|
+
end
|
56
|
+
|
57
|
+
connect_in_foreground if Agent.config[:sync_startup]
|
58
|
+
start_worker_thread(options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Log startup information that we almost always want to know
|
62
|
+
def log_startup
|
63
|
+
log_environment
|
64
|
+
log_dispatcher
|
65
|
+
log_app_name
|
66
|
+
end
|
67
|
+
|
68
|
+
# Log the environment the app thinks it's running in.
|
69
|
+
# Useful in debugging, as this is the key for config YAML lookups.
|
70
|
+
def log_environment
|
71
|
+
::NewRelic::Agent.logger.info "Environment: #{NewRelic::Control.instance.env}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Logs the dispatcher to the log file to assist with
|
75
|
+
# debugging. When no debugger is present, logs this fact to
|
76
|
+
# assist with proper dispatcher detection
|
77
|
+
def log_dispatcher
|
78
|
+
dispatcher_name = Agent.config[:dispatcher].to_s
|
79
|
+
|
80
|
+
if dispatcher_name.empty?
|
81
|
+
::NewRelic::Agent.logger.info 'No known dispatcher detected.'
|
82
|
+
else
|
83
|
+
::NewRelic::Agent.logger.info "Dispatcher: #{dispatcher_name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_app_name
|
88
|
+
::NewRelic::Agent.logger.info "Application: #{Agent.config[:app_name].join(", ")}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def log_ignore_url_regexes
|
92
|
+
regexes = NewRelic::Agent.config[:'rules.ignore_url_regexes']
|
93
|
+
|
94
|
+
unless regexes.empty?
|
95
|
+
::NewRelic::Agent.logger.info "Ignoring URLs that match the following regexes: #{regexes.map(&:inspect).join(", ")}."
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Classy logging of the agent version and the current pid,
|
100
|
+
# so we can disambiguate processes in the log file and make
|
101
|
+
# sure they're running a reasonable version
|
102
|
+
def log_version_and_pid
|
103
|
+
::NewRelic::Agent.logger.debug "New Relic Ruby Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Logs the configured application names
|
107
|
+
def app_name_configured?
|
108
|
+
names = Agent.config[:app_name]
|
109
|
+
return names.respond_to?(:any?) && names.any?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Connecting in the foreground blocks further startup of the
|
113
|
+
# agent until we have a connection - useful in cases where
|
114
|
+
# you're trying to log a very-short-running process and want
|
115
|
+
# to get statistics from before a server connection
|
116
|
+
# (typically 20 seconds) exists
|
117
|
+
def connect_in_foreground
|
118
|
+
NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Warn the user if they have configured their agent not to
|
122
|
+
# send data, that way we can see this clearly in the log file
|
123
|
+
def monitoring?
|
124
|
+
if Agent.config[:monitor_mode]
|
125
|
+
true
|
126
|
+
else
|
127
|
+
::NewRelic::Agent.logger.warn('Agent configured not to send data in this environment.')
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Tell the user when the license key is missing so they can
|
133
|
+
# fix it by adding it to the file
|
134
|
+
def has_license_key?
|
135
|
+
if Agent.config[:license_key] && Agent.config[:license_key].length > 0
|
136
|
+
true
|
137
|
+
else
|
138
|
+
::NewRelic::Agent.logger.warn("No license key found. " +
|
139
|
+
"This often means your newrelic.yml file was not found, or it lacks a section for the running environment, '#{NewRelic::Control.instance.env}'. You may also want to try linting your newrelic.yml to ensure it is valid YML.")
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# A correct license key exists and is of the proper length
|
145
|
+
def has_correct_license_key?
|
146
|
+
has_license_key? && correct_license_length
|
147
|
+
end
|
148
|
+
|
149
|
+
# A license key is an arbitrary 40 character string,
|
150
|
+
# usually looks something like a SHA1 hash
|
151
|
+
def correct_license_length
|
152
|
+
key = Agent.config[:license_key]
|
153
|
+
|
154
|
+
if key.length == 40
|
155
|
+
true
|
156
|
+
else
|
157
|
+
::NewRelic::Agent.logger.error("Invalid license key: #{key}")
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Check to see if the agent should start, returning +true+ if it should.
|
163
|
+
def agent_should_start?
|
164
|
+
return false if already_started? || disabled?
|
165
|
+
|
166
|
+
if defer_for_delayed_job?
|
167
|
+
::NewRelic::Agent.logger.debug "Deferring startup for DelayedJob"
|
168
|
+
return false
|
169
|
+
end
|
170
|
+
|
171
|
+
if defer_for_resque?
|
172
|
+
::NewRelic::Agent.logger.debug "Deferring startup for Resque in case it daemonizes"
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
|
176
|
+
unless app_name_configured?
|
177
|
+
NewRelic::Agent.logger.error "No application name configured.",
|
178
|
+
"The Agent cannot start without at least one. Please check your ",
|
179
|
+
"newrelic.yml and ensure that it is valid and has at least one ",
|
180
|
+
"value set for app_name in the #{NewRelic::Control.instance.env} ",
|
181
|
+
"environment."
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
|
185
|
+
return true
|
186
|
+
end
|
187
|
+
|
188
|
+
# The agent is disabled when it is not force enabled by the
|
189
|
+
# 'agent_enabled' option (e.g. in a manual start), or
|
190
|
+
# enabled normally through the configuration file
|
191
|
+
def disabled?
|
192
|
+
!Agent.config[:agent_enabled]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|