Class: NewRelic::Agent::NewRelicService
- Inherits:
-
Object
- Object
- NewRelic::Agent::NewRelicService
show all
- Defined in:
- lib/new_relic/agent/new_relic_service.rb,
lib/new_relic/agent/new_relic_service/encoders.rb,
lib/new_relic/agent/new_relic_service/marshaller.rb,
lib/new_relic/agent/new_relic_service/json_marshaller.rb,
lib/new_relic/agent/new_relic_service/security_policy_settings.rb
Defined Under Namespace
Modules: Encoders, SecurityPolicySettings
Classes: JsonMarshaller, Marshaller
Constant Summary
collapse
- PROTOCOL_VERSION =
Specifies the version of the agent’s communication protocol with the NewRelic hosted site.
17
- CONNECTION_ERRORS =
These include Errno connection errors, and all indicate that the underlying TCP connection may be in a bad state.
[Timeout::Error, EOFError, SystemCallError, SocketError].freeze
- MAX_ATTEMPTS =
The maximum number of times to attempt an HTTP request
2
- MIN_BYTE_SIZE_TO_COMPRESS =
Don’t perform compression on the payload unless its uncompressed size is greater than or equal to this number of bytes. In testing with Ruby 2.2 - 3.1, we determined an absolute minimum value for ASCII to be 535 bytes to obtain at least a 10% savings in size. It is recommended that this value be kept above that 535 number. It is also important to consider the CPU cost involved with performing compression and to find a balance between CPU cycles spent and bandwidth saved. A good reasonable default here is 2048 bytes, which is a tried and true Apache Tomcat default (as of v8.5.78)
2048
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(license_key = nil, collector = control.server) ⇒ NewRelicService
Returns a new instance of NewRelicService.
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 41
def initialize(license_key = nil, collector = control.server)
@license_key = license_key
@collector = collector
@configured_collector = collector
@request_timeout = Agent.config[:timeout]
@ssl_cert_store = nil
@in_session = nil
@agent_id = nil
@shared_tcp_connection = nil
@request_headers_map = nil
reset_remote_method_uris
prep_audit_logger
prep_marshaller
end
|
Instance Attribute Details
#agent_id ⇒ Object
Returns the value of attribute agent_id.
39
40
41
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 39
def agent_id
@agent_id
end
|
#collector ⇒ Object
Returns the value of attribute collector.
39
40
41
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 39
def collector
@collector
end
|
#marshaller ⇒ Object
Returns the value of attribute marshaller.
39
40
41
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 39
def marshaller
@marshaller
end
|
#request_timeout ⇒ Object
Returns the value of attribute request_timeout.
38
39
40
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 38
def request_timeout
@request_timeout
end
|
Instance Method Details
#agent_command_results(results) ⇒ Object
174
175
176
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 174
def agent_command_results(results)
invoke_remote(:agent_command_results, [@agent_id, results])
end
|
#analytic_event_data(data) ⇒ Object
178
179
180
181
182
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 178
def analytic_event_data(data)
_, items = data
invoke_remote(:analytic_event_data, [@agent_id, *data],
:item_count => items.size)
end
|
#build_metric_data_array(stats_hash) ⇒ Object
The collector wants to receive metric data in a format that’s different from how we store it internally, so this method handles the translation.
129
130
131
132
133
134
135
136
137
138
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 129
def build_metric_data_array(stats_hash)
metric_data_array = []
stats_hash.each do |metric_spec, stats|
unless stats.is_reset?
metric_data_array << NewRelic::MetricData.new(metric_spec, stats)
end
end
metric_data_array
end
|
#cert_file_path ⇒ Object
The path to the certificate file used to verify the SSL connection if verify_peer is enabled
372
373
374
375
376
377
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 372
def cert_file_path
if path_override = NewRelic::Agent.config[:ca_bundle_path]
NewRelic::Agent.logger.warn("Couldn't find CA bundle from configured ca_bundle_path: #{path_override}") unless File.exist?(path_override)
path_override
end
end
|
#close_shared_connection ⇒ Object
266
267
268
269
270
271
272
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 266
def close_shared_connection
if @shared_tcp_connection
::NewRelic::Agent.logger.debug("Closing shared TCP connection to #{@shared_tcp_connection.address}:#{@shared_tcp_connection.port}")
@shared_tcp_connection.finish if @shared_tcp_connection.started?
@shared_tcp_connection = nil
end
end
|
#compress_request_if_needed(data, endpoint) ⇒ Object
#connect(settings = {}) ⇒ Object
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 83
def connect(settings = {})
@request_headers_map = nil
security_policies = nil
if response = preconnect
if host = response['redirect_host']
@collector = NewRelic::Control.instance.server_from_host(host)
end
if policies = response['security_policies']
security_policies = SecurityPolicySettings.preliminary_settings(policies)
settings.merge!(security_policies)
end
end
response = invoke_remote(:connect, [settings])
@request_headers_map = response['request_headers_map']
self.agent_id = response['agent_run_id']
response.merge!(security_policies) if security_policies
response
end
|
#create_and_start_http_connection ⇒ Object
361
362
363
364
365
366
367
368
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 361
def create_and_start_http_connection
conn = create_http_connection
start_connection(conn)
conn
rescue Timeout::Error
::NewRelic::Agent.logger.info('Timeout while attempting to connect. You may need to install system-level CA Certificates, as the ruby agent no longer includes these.')
raise
end
|
#create_http_connection ⇒ Object
335
336
337
338
339
340
341
342
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 335
def create_http_connection
conn = prep_connection
setup_connection_for_ssl(conn)
setup_connection_timeouts(conn)
::NewRelic::Agent.logger.debug("Created net/http handle to #{conn.address}:#{conn.port}")
conn
end
|
#custom_event_data(data) ⇒ Object
184
185
186
187
188
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 184
def custom_event_data(data)
_, items = data
invoke_remote(:custom_event_data, [@agent_id, *data],
:item_count => items.size)
end
|
#error_data(unsent_errors) ⇒ Object
151
152
153
154
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 151
def error_data(unsent_errors)
invoke_remote(:error_data, [@agent_id, unsent_errors],
:item_count => unsent_errors.size)
end
|
#error_event_data(data) ⇒ Object
195
196
197
198
199
200
201
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 195
def error_event_data(data)
metadata, items = data
response = invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
NewRelic::Agent.record_metric('Supportability/Events/TransactionError/Sent', :count => items.size)
NewRelic::Agent.record_metric('Supportability/Events/TransactionError/Seen', :count => metadata[:events_seen])
response
end
|
#establish_shared_connection ⇒ Object
261
262
263
264
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 261
def establish_shared_connection
@shared_tcp_connection ||= create_and_start_http_connection
@shared_tcp_connection
end
|
#force_restart ⇒ Object
123
124
125
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 123
def force_restart
close_shared_connection
end
|
#get_agent_commands ⇒ Object
170
171
172
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 170
def get_agent_commands
invoke_remote(:get_agent_commands, [@agent_id])
end
|
#has_shared_connection? ⇒ Boolean
274
275
276
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 274
def has_shared_connection?
!@shared_tcp_connection.nil?
end
|
#http_connection ⇒ Object
Return a Net::HTTP connection object to make a call to the collector. We’ll reuse the same handle for cases where we’re using keep-alive, or otherwise create a new one.
292
293
294
295
296
297
298
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 292
def http_connection
if @in_session
establish_shared_connection
else
create_http_connection
end
end
|
#log_event_data(data) ⇒ Object
190
191
192
193
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 190
def log_event_data(data)
payload, size = LogEventAggregator.payload_to_melt_format(data)
invoke_remote(:log_event_data, payload, :item_count => size)
end
|
#metric_data(stats_hash) ⇒ Object
140
141
142
143
144
145
146
147
148
149
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 140
def metric_data(stats_hash)
timeslice_start = stats_hash.started_at
timeslice_end = stats_hash.harvested_at || Process.clock_gettime(Process::CLOCK_REALTIME)
metric_data_array = build_metric_data_array(stats_hash)
invoke_remote(
:metric_data,
[@agent_id, timeslice_start, timeslice_end, metric_data_array],
:item_count => metric_data_array.size
)
end
|
#preconnect ⇒ Object
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 102
def preconnect
token = Agent.config[:security_policies_token]
if token && !token.empty?
response = invoke_remote(:preconnect, [{'security_policies_token' => token, 'high_security' => false}])
validator = SecurityPolicySettings::Validator.new(response)
validator.validate_matching_agent_config!
response
elsif Agent.config[:high_security]
invoke_remote(:preconnect, [{'high_security' => true}])
else
invoke_remote(:preconnect, [{'high_security' => false}])
end
end
|
#prep_audit_logger ⇒ Object
57
58
59
60
61
62
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 57
def prep_audit_logger
@audit_logger = ::NewRelic::Agent::AuditLogger.new
Agent.config.register_callback(:'audit_log.enabled') do |enabled|
@audit_logger.enabled = enabled
end
end
|
#prep_connection ⇒ Object
344
345
346
347
348
349
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 344
def prep_connection
return Net::HTTP.new(@collector.name, @collector.port) unless Agent.config[:proxy_host]
::NewRelic::Agent.logger.debug("Using proxy server #{Agent.config[:proxy_host]}:#{Agent.config[:proxy_port]}")
prep_proxy_connection
end
|
#prep_marshaller ⇒ Object
64
65
66
67
68
69
70
71
72
73
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 64
def prep_marshaller
Agent.config.register_callback(:marshaller) do |marshaller|
if marshaller != 'json'
::NewRelic::Agent.logger.warn("Non-JSON marshaller '#{marshaller}' requested but not supported, using " \
'JSON marshaller instead. pruby marshalling has been removed as of version 3.14.0.')
end
@marshaller = JsonMarshaller.new
end
end
|
#prep_proxy_connection ⇒ Object
351
352
353
354
355
356
357
358
359
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 351
def prep_proxy_connection
proxy = Net::HTTP::Proxy(
Agent.config[:proxy_host],
Agent.config[:proxy_port],
Agent.config[:proxy_user],
Agent.config[:proxy_pass]
)
proxy.new(@collector.name, @collector.port)
end
|
#profile_data(profile) ⇒ Object
166
167
168
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 166
def profile_data(profile)
invoke_remote(:profile_data, [@agent_id, profile], :skip_normalization => true) || ''
end
|
#session(&block) ⇒ Object
One session with the service’s endpoint. In this case the session represents 1 tcp connection which may transmit multiple HTTP requests via keep-alive.
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 228
def session(&block)
raise ArgumentError, "#{self.class}#shared_connection must be passed a block" unless block
begin
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@in_session = true
if NewRelic::Agent.config[:aggressive_keepalive]
session_with_keepalive(&block)
else
session_without_keepalive(&block)
end
rescue *CONNECTION_ERRORS => e
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
raise NewRelic::Agent::ServerConnectionException, "Recoverable error connecting to #{@collector} after #{elapsed} seconds: #{e}"
ensure
@in_session = false
end
end
|
#session_with_keepalive(&block) ⇒ Object
247
248
249
250
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 247
def session_with_keepalive(&block)
establish_shared_connection
yield
end
|
#session_without_keepalive(&block) ⇒ Object
252
253
254
255
256
257
258
259
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 252
def session_without_keepalive(&block)
begin
establish_shared_connection
yield
ensure
close_shared_connection
end
end
|
#set_cert_store(conn) ⇒ Object
312
313
314
315
316
317
318
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 312
def set_cert_store(conn)
if NewRelic::Agent.config[:ca_bundle_path]
conn.cert_store = ssl_cert_store
else
::NewRelic::Agent.logger.debug('Using default security certificates')
end
end
|
#setup_connection_for_ssl(conn) ⇒ Object
300
301
302
303
304
305
306
307
308
309
310
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 300
def setup_connection_for_ssl(conn)
conn.use_ssl = true
conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
set_cert_store(conn)
rescue StandardError, LoadError
msg = 'SSL is not available in the environment; please install SSL support.'
raise UnrecoverableAgentException.new(msg)
end
|
#setup_connection_timeouts(conn) ⇒ Object
326
327
328
329
330
331
332
333
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 326
def setup_connection_timeouts(conn)
conn.read_timeout = nil
if conn.respond_to?(:keep_alive_timeout) && NewRelic::Agent.config[:aggressive_keepalive]
conn.keep_alive_timeout = NewRelic::Agent.config[:keep_alive_timeout]
end
end
|
#shutdown(time) ⇒ Object
119
120
121
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 119
def shutdown(time)
invoke_remote(:shutdown, [@agent_id, time.to_i]) if @agent_id
end
|
#span_event_data(data) ⇒ Object
203
204
205
206
207
208
209
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 203
def span_event_data(data)
metadata, items = data
response = invoke_remote(:span_event_data, [@agent_id, *data], :item_count => items.size)
NewRelic::Agent.record_metric('Supportability/Events/SpanEvents/Sent', :count => items.size)
NewRelic::Agent.record_metric('Supportability/Events/SpanEvents/Seen', :count => metadata[:events_seen])
response
end
|
#sql_trace_data(sql_traces) ⇒ Object
161
162
163
164
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 161
def sql_trace_data(sql_traces)
invoke_remote(:sql_trace_data, [sql_traces],
:item_count => sql_traces.size)
end
|
#ssl_cert_store ⇒ Object
278
279
280
281
282
283
284
285
286
287
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 278
def ssl_cert_store
path = cert_file_path
if !@ssl_cert_store || path != @cached_cert_store_path
::NewRelic::Agent.logger.debug("Creating SSL certificate store from file at #{path}")
@ssl_cert_store = OpenSSL::X509::Store.new
@ssl_cert_store.add_file(path)
@cached_cert_store_path = path
end
@ssl_cert_store
end
|
#start_connection(conn) ⇒ Object
320
321
322
323
324
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 320
def start_connection(conn)
NewRelic::Agent.logger.debug("Opening TCP connection to #{conn.address}:#{conn.port}")
Timeout.timeout(@request_timeout) { conn.start }
conn
end
|
#transaction_sample_data(traces) ⇒ Object
156
157
158
159
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 156
def transaction_sample_data(traces)
invoke_remote(:transaction_sample_data, [@agent_id, traces],
:item_count => traces.size)
end
|
#valid_to_marshal?(data) ⇒ Boolean
379
380
381
382
383
384
385
|
# File 'lib/new_relic/agent/new_relic_service.rb', line 379
def valid_to_marshal?(data)
@marshaller.dump(data)
true
rescue StandardError, SystemStackError => e
NewRelic::Agent.logger.warn('Unable to marshal environment report on connect.', e)
false
end
|