newrelic_rpm 9.5.0 → 9.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +154 -7
- data/CONTRIBUTING.md +0 -7
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/bin/newrelic +2 -9
- data/bin/newrelic_rpm +15 -0
- data/init.rb +2 -2
- data/lib/new_relic/agent/agent.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/shutdown.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/special_startup.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +2 -2
- data/lib/new_relic/agent/agent_helpers/startup.rb +2 -2
- data/lib/new_relic/agent/attribute_filter.rb +3 -3
- data/lib/new_relic/agent/configuration/default_source.rb +131 -36
- data/lib/new_relic/agent/configuration/high_security_source.rb +1 -0
- data/lib/new_relic/agent/configuration/manager.rb +13 -9
- data/lib/new_relic/agent/configuration/security_policy_source.rb +11 -0
- data/lib/new_relic/agent/custom_event_aggregator.rb +27 -1
- data/lib/new_relic/agent/datastores/mongo/metric_translator.rb +1 -1
- data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +3 -3
- data/lib/new_relic/agent/error_collector.rb +2 -0
- data/lib/new_relic/agent/event_loop.rb +1 -1
- data/lib/new_relic/agent/http_clients/abstract.rb +4 -0
- data/lib/new_relic/agent/http_clients/async_http_wrappers.rb +80 -0
- data/lib/new_relic/agent/http_clients/curb_wrappers.rb +1 -3
- data/lib/new_relic/agent/http_clients/ethon_wrappers.rb +109 -0
- data/lib/new_relic/agent/http_clients/excon_wrappers.rb +0 -3
- data/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +1 -3
- data/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +0 -3
- data/lib/new_relic/agent/http_clients/httpx_wrappers.rb +91 -0
- data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +1 -4
- data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +0 -3
- data/lib/new_relic/agent/instrumentation/active_merchant.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -2
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/chain.rb +69 -0
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +17 -0
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/prepend.rb +37 -0
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +23 -0
- data/lib/new_relic/agent/instrumentation/active_support_logger.rb +3 -1
- data/lib/new_relic/agent/instrumentation/async_http/chain.rb +23 -0
- data/lib/new_relic/agent/instrumentation/async_http/instrumentation.rb +37 -0
- data/lib/new_relic/agent/instrumentation/async_http/prepend.rb +15 -0
- data/lib/new_relic/agent/instrumentation/async_http.rb +28 -0
- data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -0
- data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/ethon/chain.rb +39 -0
- data/lib/new_relic/agent/instrumentation/ethon/instrumentation.rb +105 -0
- data/lib/new_relic/agent/instrumentation/ethon/prepend.rb +35 -0
- data/lib/new_relic/agent/instrumentation/ethon.rb +39 -0
- data/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb +1 -4
- data/lib/new_relic/agent/instrumentation/grpc_server.rb +1 -1
- data/lib/new_relic/agent/instrumentation/httpx/chain.rb +20 -0
- data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +51 -0
- data/lib/new_relic/agent/instrumentation/httpx/prepend.rb +15 -0
- data/lib/new_relic/agent/instrumentation/httpx.rb +27 -0
- data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -3
- data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +7 -1
- data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +1 -0
- data/lib/new_relic/agent/instrumentation/roda/ignorer.rb +45 -0
- data/lib/new_relic/agent/instrumentation/roda/instrumentation.rb +12 -0
- data/lib/new_relic/agent/instrumentation/roda/roda_transaction_namer.rb +1 -2
- data/lib/new_relic/agent/instrumentation/roda.rb +2 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/chain.rb +36 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/instrumentation.rb +197 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/prepend.rb +20 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai.rb +35 -0
- data/lib/new_relic/agent/instrumentation/sidekiq.rb +3 -1
- data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sinatra/transaction_namer.rb +1 -3
- data/lib/new_relic/agent/instrumentation/sinatra.rb +1 -1
- data/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +1 -4
- data/lib/new_relic/agent/instrumentation/view_component/chain.rb +21 -0
- data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +39 -0
- data/lib/new_relic/agent/instrumentation/view_component/prepend.rb +13 -0
- data/lib/new_relic/agent/instrumentation/view_component.rb +26 -0
- data/lib/new_relic/agent/javascript_instrumentor.rb +0 -1
- data/lib/new_relic/agent/llm/chat_completion_message.rb +25 -0
- data/lib/new_relic/agent/llm/chat_completion_summary.rb +66 -0
- data/lib/new_relic/agent/llm/embedding.rb +60 -0
- data/lib/new_relic/agent/llm/llm_event.rb +95 -0
- data/lib/new_relic/agent/llm/response_headers.rb +80 -0
- data/lib/new_relic/agent/llm.rb +49 -0
- data/lib/new_relic/agent/messaging.rb +2 -2
- data/lib/new_relic/agent/monitors/synthetics_monitor.rb +12 -1
- data/lib/new_relic/agent/new_relic_service/encoders.rb +2 -2
- data/lib/new_relic/agent/new_relic_service.rb +8 -6
- data/lib/new_relic/agent/obfuscator.rb +0 -2
- data/lib/new_relic/agent/pipe_channel_manager.rb +2 -2
- data/lib/new_relic/agent/rules_engine/segment_terms_rule.rb +1 -2
- data/lib/new_relic/agent/rules_engine.rb +1 -1
- data/lib/new_relic/agent/span_event_primitive.rb +16 -4
- data/lib/new_relic/agent/sql_sampler.rb +0 -1
- data/lib/new_relic/agent/system_info.rb +26 -0
- data/lib/new_relic/agent/threading/agent_thread.rb +1 -2
- data/lib/new_relic/agent/tracer.rb +9 -10
- data/lib/new_relic/agent/transaction/abstract_segment.rb +4 -1
- data/lib/new_relic/agent/transaction/external_request_segment.rb +5 -2
- data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -2
- data/lib/new_relic/agent/transaction/request_attributes.rb +1 -3
- data/lib/new_relic/agent/transaction/tracing.rb +11 -1
- data/lib/new_relic/agent/transaction.rb +25 -2
- data/lib/new_relic/agent/transaction_error_primitive.rb +16 -0
- data/lib/new_relic/agent/transaction_event_primitive.rb +19 -0
- data/lib/new_relic/agent/utilization/gcp.rb +1 -3
- data/lib/new_relic/agent/vm/{mri_vm.rb → c_ruby_vm.rb} +7 -15
- data/lib/new_relic/agent/vm.rb +2 -2
- data/lib/new_relic/agent/worker_loop.rb +1 -1
- data/lib/new_relic/agent.rb +102 -7
- data/lib/new_relic/base64.rb +25 -0
- data/lib/new_relic/cli/command.rb +6 -4
- data/lib/new_relic/constants.rb +5 -0
- data/lib/new_relic/control/frameworks/rails.rb +17 -5
- data/lib/new_relic/control/instrumentation.rb +1 -1
- data/lib/new_relic/language_support.rb +4 -0
- data/lib/new_relic/local_environment.rb +22 -13
- data/lib/new_relic/rack/browser_monitoring.rb +8 -4
- data/lib/new_relic/supportability_helper.rb +3 -1
- data/lib/new_relic/thread_local_storage.rb +31 -0
- data/lib/new_relic/version.rb +1 -1
- data/lib/tasks/config.rake +1 -1
- data/lib/tasks/helpers/config.html.erb +6 -6
- data/lib/tasks/helpers/newrelicyml.rb +1 -1
- data/lib/tasks/instrumentation_generator/instrumentation.thor +3 -3
- data/lib/tasks/instrumentation_generator/templates/chain.tt +0 -1
- data/lib/tasks/instrumentation_generator/templates/chain_method.tt +0 -1
- data/lib/tasks/tests.rake +71 -0
- data/newrelic.yml +76 -36
- data/newrelic_rpm.gemspec +5 -4
- data/test/agent_helper.rb +14 -2
- metadata +43 -7
- data/bin/newrelic_cmd +0 -7
@@ -31,9 +31,14 @@ module NewRelic
|
|
31
31
|
SYNTHETICS_RESOURCE_ID_KEY = 'nr.syntheticsResourceId'.freeze
|
32
32
|
SYNTHETICS_JOB_ID_KEY = 'nr.syntheticsJobId'.freeze
|
33
33
|
SYNTHETICS_MONITOR_ID_KEY = 'nr.syntheticsMonitorId'.freeze
|
34
|
+
SYNTHETICS_TYPE_KEY = 'nr.syntheticsType'
|
35
|
+
SYNTHETICS_INITIATOR_KEY = 'nr.syntheticsInitiator'
|
36
|
+
SYNTHETICS_KEY_PREFIX = 'nr.synthetics'
|
34
37
|
PRIORITY_KEY = 'priority'.freeze
|
35
38
|
SPAN_ID_KEY = 'spanId'.freeze
|
36
39
|
|
40
|
+
SYNTHETICS_PAYLOAD_EXPECTED = [:synthetics_resource_id, :synthetics_job_id, :synthetics_monitor_id, :synthetics_type, :synthetics_initiator]
|
41
|
+
|
37
42
|
def create(noticed_error, payload, span_id)
|
38
43
|
[
|
39
44
|
intrinsic_attributes_for(noticed_error, payload, span_id),
|
@@ -71,9 +76,20 @@ module NewRelic
|
|
71
76
|
end
|
72
77
|
|
73
78
|
def append_synthetics(payload, sample)
|
79
|
+
return unless payload[:synthetics_job_id]
|
80
|
+
|
74
81
|
sample[SYNTHETICS_RESOURCE_ID_KEY] = payload[:synthetics_resource_id] if payload[:synthetics_resource_id]
|
75
82
|
sample[SYNTHETICS_JOB_ID_KEY] = payload[:synthetics_job_id] if payload[:synthetics_job_id]
|
76
83
|
sample[SYNTHETICS_MONITOR_ID_KEY] = payload[:synthetics_monitor_id] if payload[:synthetics_monitor_id]
|
84
|
+
sample[SYNTHETICS_TYPE_KEY] = payload[:synthetics_type] if payload[:synthetics_type]
|
85
|
+
sample[SYNTHETICS_INITIATOR_KEY] = payload[:synthetics_initiator] if payload[:synthetics_initiator]
|
86
|
+
|
87
|
+
payload.each do |k, v|
|
88
|
+
next unless k.to_s.start_with?('synthetics_') && !SYNTHETICS_PAYLOAD_EXPECTED.include?(k)
|
89
|
+
|
90
|
+
new_key = SYNTHETICS_KEY_PREFIX + NewRelic::LanguageSupport.camelize(k.to_s.gsub('synthetics_', ''))
|
91
|
+
sample[new_key] = v
|
92
|
+
end
|
77
93
|
end
|
78
94
|
|
79
95
|
def append_cat(payload, sample)
|
@@ -38,6 +38,11 @@ module NewRelic
|
|
38
38
|
SYNTHETICS_RESOURCE_ID_KEY = 'nr.syntheticsResourceId'
|
39
39
|
SYNTHETICS_JOB_ID_KEY = 'nr.syntheticsJobId'
|
40
40
|
SYNTHETICS_MONITOR_ID_KEY = 'nr.syntheticsMonitorId'
|
41
|
+
SYNTHETICS_TYPE_KEY = 'nr.syntheticsType'
|
42
|
+
SYNTHETICS_INITIATOR_KEY = 'nr.syntheticsInitiator'
|
43
|
+
SYNTHETICS_KEY_PREFIX = 'nr.synthetics'
|
44
|
+
|
45
|
+
SYNTHETICS_PAYLOAD_EXPECTED = [:synthetics_resource_id, :synthetics_job_id, :synthetics_monitor_id, :synthetics_type, :synthetics_initiator]
|
41
46
|
|
42
47
|
def create(payload)
|
43
48
|
intrinsics = {
|
@@ -71,9 +76,23 @@ module NewRelic
|
|
71
76
|
optionally_append(SYNTHETICS_RESOURCE_ID_KEY, :synthetics_resource_id, sample, payload)
|
72
77
|
optionally_append(SYNTHETICS_JOB_ID_KEY, :synthetics_job_id, sample, payload)
|
73
78
|
optionally_append(SYNTHETICS_MONITOR_ID_KEY, :synthetics_monitor_id, sample, payload)
|
79
|
+
optionally_append(SYNTHETICS_TYPE_KEY, :synthetics_type, sample, payload)
|
80
|
+
optionally_append(SYNTHETICS_INITIATOR_KEY, :synthetics_initiator, sample, payload)
|
81
|
+
append_synthetics_info_attributes(sample, payload)
|
74
82
|
append_cat_alternate_path_hashes(sample, payload)
|
75
83
|
end
|
76
84
|
|
85
|
+
def append_synthetics_info_attributes(sample, payload)
|
86
|
+
return unless payload.include?(:synthetics_job_id)
|
87
|
+
|
88
|
+
payload.each do |k, v|
|
89
|
+
next unless k.to_s.start_with?('synthetics_') && !SYNTHETICS_PAYLOAD_EXPECTED.include?(k)
|
90
|
+
|
91
|
+
new_key = SYNTHETICS_KEY_PREFIX + NewRelic::LanguageSupport.camelize(k.to_s.gsub('synthetics_', ''))
|
92
|
+
sample[new_key] = v.to_s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
77
96
|
def append_cat_alternate_path_hashes(sample, payload)
|
78
97
|
if payload.include?(:cat_alternate_path_hashes)
|
79
98
|
sample[CAT_ALTERNATE_PATH_HASHES_KEY] = payload[:cat_alternate_path_hashes].sort.join(COMMA)
|
@@ -8,7 +8,7 @@ require 'new_relic/agent/vm/snapshot'
|
|
8
8
|
module NewRelic
|
9
9
|
module Agent
|
10
10
|
module VM
|
11
|
-
class
|
11
|
+
class CRubyVM
|
12
12
|
def snapshot
|
13
13
|
snap = Snapshot.new
|
14
14
|
gather_stats(snap)
|
@@ -24,7 +24,7 @@ module NewRelic
|
|
24
24
|
|
25
25
|
def gather_gc_stats(snap)
|
26
26
|
gather_gc_runs(snap) if supports?(:gc_runs)
|
27
|
-
gather_derived_stats(snap)
|
27
|
+
gather_derived_stats(snap)
|
28
28
|
end
|
29
29
|
|
30
30
|
def gather_gc_runs(snap)
|
@@ -33,19 +33,11 @@ module NewRelic
|
|
33
33
|
|
34
34
|
def gather_derived_stats(snap)
|
35
35
|
stat = GC.stat
|
36
|
-
snap.total_allocated_object =
|
37
|
-
snap.major_gc_count =
|
38
|
-
snap.minor_gc_count =
|
39
|
-
snap.heap_live =
|
40
|
-
snap.heap_free =
|
41
|
-
end
|
42
|
-
|
43
|
-
def derive_from_gc_stats(keys, stat)
|
44
|
-
Array(keys).each do |key|
|
45
|
-
value = stat[key]
|
46
|
-
return value if value
|
47
|
-
end
|
48
|
-
nil
|
36
|
+
snap.total_allocated_object = stat.fetch(:total_allocated_objects, nil)
|
37
|
+
snap.major_gc_count = stat.fetch(:major_gc_count, nil)
|
38
|
+
snap.minor_gc_count = stat.fetch(:minor_gc_count, nil)
|
39
|
+
snap.heap_live = stat.fetch(:heap_live_slots, nil)
|
40
|
+
snap.heap_free = stat.fetch(:heap_free_slots, nil)
|
49
41
|
end
|
50
42
|
|
51
43
|
def gather_gc_time(snap)
|
data/lib/new_relic/agent/vm.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require 'new_relic/language_support'
|
6
|
-
require 'new_relic/agent/vm/
|
6
|
+
require 'new_relic/agent/vm/c_ruby_vm'
|
7
7
|
require 'new_relic/agent/vm/jruby_vm'
|
8
8
|
|
9
9
|
module NewRelic
|
@@ -21,7 +21,7 @@ module NewRelic
|
|
21
21
|
if NewRelic::LanguageSupport.jruby?
|
22
22
|
JRubyVM.new
|
23
23
|
else
|
24
|
-
|
24
|
+
CRubyVM.new
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -88,7 +88,7 @@ module NewRelic
|
|
88
88
|
raise
|
89
89
|
rescue => e
|
90
90
|
# Don't blow out the stack for anything that hasn't already propagated
|
91
|
-
::NewRelic::Agent.logger.error('Error running task in
|
91
|
+
::NewRelic::Agent.logger.error('Error running task in agent worker loop:', e)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
data/lib/new_relic/agent.rb
CHANGED
@@ -31,6 +31,7 @@ module NewRelic
|
|
31
31
|
require 'new_relic/noticed_error'
|
32
32
|
require 'new_relic/agent/noticeable_error'
|
33
33
|
require 'new_relic/supportability_helper'
|
34
|
+
require 'new_relic/thread_local_storage'
|
34
35
|
|
35
36
|
require 'new_relic/agent/encoding_normalizer'
|
36
37
|
require 'new_relic/agent/stats'
|
@@ -62,6 +63,7 @@ module NewRelic
|
|
62
63
|
require 'new_relic/agent/attribute_processing'
|
63
64
|
require 'new_relic/agent/linking_metadata'
|
64
65
|
require 'new_relic/agent/local_log_decorator'
|
66
|
+
require 'new_relic/agent/llm'
|
65
67
|
|
66
68
|
require 'new_relic/agent/instrumentation/controller_instrumentation'
|
67
69
|
|
@@ -105,11 +107,14 @@ module NewRelic
|
|
105
107
|
|
106
108
|
# placeholder name used when we cannot determine a transaction's name
|
107
109
|
UNKNOWN_METRIC = '(unknown)'.freeze
|
110
|
+
LLM_FEEDBACK_MESSAGE = 'LlmFeedbackMessage'
|
108
111
|
|
109
112
|
attr_reader :error_group_callback
|
113
|
+
attr_reader :llm_token_count_callback
|
110
114
|
|
111
115
|
@agent = nil
|
112
116
|
@error_group_callback = nil
|
117
|
+
@llm_token_count_callback = nil
|
113
118
|
@logger = nil
|
114
119
|
@tracer_lock = Mutex.new
|
115
120
|
@tracer_queue = []
|
@@ -252,7 +257,7 @@ module NewRelic
|
|
252
257
|
|
253
258
|
# @!group Recording custom errors
|
254
259
|
|
255
|
-
# Set a filter to be applied to errors that the Ruby
|
260
|
+
# Set a filter to be applied to errors that the Ruby agent will
|
256
261
|
# track. The block should evaluate to the exception to track
|
257
262
|
# (which could be different from the original exception) or nil to
|
258
263
|
# ignore this exception.
|
@@ -387,17 +392,103 @@ module NewRelic
|
|
387
392
|
nil
|
388
393
|
end
|
389
394
|
|
395
|
+
# Records user feedback events for LLM applications. This API must pass
|
396
|
+
# the current trace id as a parameter, which can be obtained using:
|
397
|
+
#
|
398
|
+
# NewRelic::Agent::Tracer.current_trace_id
|
399
|
+
#
|
400
|
+
# @param [String] ID of the trace where the chat completion(s) related
|
401
|
+
# to the feedback occurred.
|
402
|
+
#
|
403
|
+
# @param [String or Integer] Rating provided by an end user
|
404
|
+
# (ex: “Good", "Bad”, 1, 2, 5, 8, 10).
|
405
|
+
#
|
406
|
+
# @param [optional, String] Category of the feedback as provided by the
|
407
|
+
# end user (ex: “informative”, “inaccurate”).
|
408
|
+
#
|
409
|
+
# @param start_time [optional, String] Freeform text feedback from an
|
410
|
+
# end user.
|
411
|
+
#
|
412
|
+
# @param [optional, Hash] Set of key-value pairs to store any other
|
413
|
+
# desired data to submit with the feedback event.
|
414
|
+
#
|
415
|
+
# @api public
|
416
|
+
#
|
417
|
+
def record_llm_feedback_event(trace_id:,
|
418
|
+
rating:,
|
419
|
+
category: nil,
|
420
|
+
message: nil,
|
421
|
+
metadata: NewRelic::EMPTY_HASH)
|
422
|
+
|
423
|
+
record_api_supportability_metric(:record_llm_feedback_event)
|
424
|
+
unless NewRelic::Agent.config[:'distributed_tracing.enabled']
|
425
|
+
return NewRelic::Agent.logger.error('Distributed tracing must be enabled to record LLM feedback')
|
426
|
+
end
|
427
|
+
|
428
|
+
feedback_message_event = {
|
429
|
+
'trace_id': trace_id,
|
430
|
+
'rating': rating,
|
431
|
+
'category': category,
|
432
|
+
'message': message,
|
433
|
+
'id': NewRelic::Agent::GuidGenerator.generate_guid,
|
434
|
+
'ingest_source': NewRelic::Agent::Llm::LlmEvent::INGEST_SOURCE
|
435
|
+
}
|
436
|
+
feedback_message_event.merge!(metadata) unless metadata.empty?
|
437
|
+
|
438
|
+
NewRelic::Agent.record_custom_event(LLM_FEEDBACK_MESSAGE, feedback_message_event)
|
439
|
+
rescue ArgumentError
|
440
|
+
raise
|
441
|
+
rescue => exception
|
442
|
+
NewRelic::Agent.logger.error('record_llm_feedback_event', exception)
|
443
|
+
end
|
444
|
+
|
445
|
+
# @!endgroup
|
446
|
+
|
447
|
+
# @!group LLM callbacks
|
448
|
+
|
449
|
+
# Set a callback proc for calculating `token_count` attributes for
|
450
|
+
# LlmEmbedding and LlmChatCompletionMessage events
|
451
|
+
#
|
452
|
+
# @param callback_proc [Proc] the callback proc
|
453
|
+
#
|
454
|
+
# This method should be called only once to set a callback for
|
455
|
+
# use with all LLM token calculations. If it is called multiple times, each
|
456
|
+
# new callback will replace the old one.
|
457
|
+
#
|
458
|
+
# The proc will be called with a single hash as its input argument and
|
459
|
+
# must return an Integer representing the number of tokens used for that
|
460
|
+
# particular prompt, completion message, or embedding. Values less than or
|
461
|
+
# equal to 0 will not be attached to an event.
|
462
|
+
#
|
463
|
+
# The hash has the following keys:
|
464
|
+
#
|
465
|
+
# :model => [String] The name of the LLM model
|
466
|
+
# :content => [String] The message content or prompt
|
467
|
+
#
|
468
|
+
# @api public
|
469
|
+
#
|
470
|
+
def set_llm_token_count_callback(callback_proc)
|
471
|
+
unless callback_proc.is_a?(Proc)
|
472
|
+
NewRelic::Agent.logger.error("#{self}.#{__method__}: expected an argument of type Proc, " \
|
473
|
+
"got #{callback_proc.class}")
|
474
|
+
return
|
475
|
+
end
|
476
|
+
|
477
|
+
record_api_supportability_metric(:set_llm_token_count_callback)
|
478
|
+
@llm_token_count_callback = callback_proc
|
479
|
+
end
|
480
|
+
|
390
481
|
# @!endgroup
|
391
482
|
|
392
483
|
# @!group Manual agent configuration and startup/shutdown
|
393
484
|
|
394
|
-
# Call this to manually start the
|
485
|
+
# Call this to manually start the agent in situations where the agent does
|
395
486
|
# not auto-start.
|
396
487
|
#
|
397
|
-
# When the app environment loads, so does the
|
398
|
-
#
|
488
|
+
# When the app environment loads, so does the agent. However, the
|
489
|
+
# agent will only connect to the service if a web front-end is found. If
|
399
490
|
# you want to selectively monitor ruby processes that don't use
|
400
|
-
# web plugins, then call this method in your code and the
|
491
|
+
# web plugins, then call this method in your code and the agent
|
401
492
|
# will fire up and start reporting to the service.
|
402
493
|
#
|
403
494
|
# Options are passed in as overrides for values in the
|
@@ -633,7 +724,9 @@ module NewRelic
|
|
633
724
|
def add_new_segment_attributes(params, segment)
|
634
725
|
# Make sure not to override existing segment-level custom attributes
|
635
726
|
segment_custom_keys = segment.attributes.custom_attributes.keys.map(&:to_sym)
|
636
|
-
segment.add_custom_attributes(params.reject
|
727
|
+
segment.add_custom_attributes(params.reject do |k, _v|
|
728
|
+
segment_custom_keys.include?(k.to_sym) if k.respond_to?(:to_sym) # param keys can be integers
|
729
|
+
end)
|
637
730
|
end
|
638
731
|
|
639
732
|
# Add custom attributes to the span event for the current span. Attributes will be visible on spans in the
|
@@ -661,7 +754,9 @@ module NewRelic
|
|
661
754
|
end
|
662
755
|
end
|
663
756
|
|
664
|
-
# Add custom attributes to log events for the current agent instance.
|
757
|
+
# Add global custom attributes to log events for the current agent instance. As these attributes are global to the
|
758
|
+
# agent instance, they will be attached to all log events generated by the agent, and this methods usage isn't
|
759
|
+
# suitable for setting dynamic values.
|
665
760
|
#
|
666
761
|
# @param [Hash] params A Hash of attributes to attach to log
|
667
762
|
# events. The agent accepts up to 240 custom
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Base64
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def encode64(bin)
|
10
|
+
[bin].pack('m')
|
11
|
+
end
|
12
|
+
|
13
|
+
def decode64(str)
|
14
|
+
str.unpack1('m')
|
15
|
+
end
|
16
|
+
|
17
|
+
def strict_encode64(bin)
|
18
|
+
[bin].pack('m0')
|
19
|
+
end
|
20
|
+
|
21
|
+
def strict_decode64(str)
|
22
|
+
str.unpack1('m0')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -60,11 +60,13 @@ module NewRelic
|
|
60
60
|
extra = []
|
61
61
|
options = ARGV.options do |opts|
|
62
62
|
script_name = File.basename($0)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
|
64
|
+
# TODO: MAJOR VERSION - remove newrelic, deprecated since version x.xx
|
65
|
+
if /newrelic$/.match?(script_name)
|
66
|
+
$stdout.puts "warning: the 'newrelic' script has been renamed 'newrelic_rpm'"
|
67
|
+
script_name = 'newrelic_rpm'
|
67
68
|
end
|
69
|
+
|
68
70
|
opts.banner = "Usage: #{script_name} [ #{@command_names.join(' | ')} ] [options]"
|
69
71
|
opts.separator("use '#{script_name} <command> -h' to see detailed command options")
|
70
72
|
opts
|
data/lib/new_relic/constants.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
5
|
module NewRelic
|
6
|
+
ASTERISK = '*'
|
7
|
+
|
6
8
|
PRIORITY_PRECISION = 6
|
7
9
|
|
8
10
|
EMPTY_ARRAY = [].freeze
|
@@ -35,4 +37,7 @@ module NewRelic
|
|
35
37
|
|
36
38
|
CONNECT_RETRY_PERIODS = [15, 15, 30, 60, 120, 300]
|
37
39
|
MAX_RETRY_PERIOD = 300
|
40
|
+
|
41
|
+
SLASH = '/'
|
42
|
+
ROOT = SLASH
|
38
43
|
end
|
@@ -10,6 +10,9 @@ module NewRelic
|
|
10
10
|
# Rails specific configuration, instrumentation, environment values,
|
11
11
|
# etc.
|
12
12
|
class Rails < NewRelic::Control::Frameworks::Ruby
|
13
|
+
BROWSER_MONITORING_INSTALLED_SINGLETON = NewRelic::Agent.config
|
14
|
+
BROWSER_MONITORING_INSTALLED_VARIABLE = :@browser_monitoring_installed
|
15
|
+
|
13
16
|
def env
|
14
17
|
@env ||= (ENV['NEW_RELIC_ENV'] || RAILS_ENV.dup)
|
15
18
|
end
|
@@ -71,7 +74,7 @@ module NewRelic
|
|
71
74
|
# Might not be running if it does not think mongrel, thin,
|
72
75
|
# passenger, etc. is running, if it thinks it's a rake task, or
|
73
76
|
# if the agent_enabled is false.
|
74
|
-
::NewRelic::Agent.logger.info('New Relic
|
77
|
+
::NewRelic::Agent.logger.info('New Relic agent not running. Skipping browser monitoring and agent hooks.')
|
75
78
|
else
|
76
79
|
install_browser_monitoring(rails_config)
|
77
80
|
install_agent_hooks(rails_config)
|
@@ -89,17 +92,17 @@ module NewRelic
|
|
89
92
|
return unless NewRelic::Rack::AgentHooks.needed?
|
90
93
|
|
91
94
|
config.middleware.use(NewRelic::Rack::AgentHooks)
|
92
|
-
::NewRelic::Agent.logger.debug('Installed New Relic
|
95
|
+
::NewRelic::Agent.logger.debug('Installed New Relic agent hooks middleware')
|
93
96
|
rescue => e
|
94
|
-
::NewRelic::Agent.logger.warn('Error installing New Relic
|
97
|
+
::NewRelic::Agent.logger.warn('Error installing New Relic agent hooks middleware', e)
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
98
101
|
def install_browser_monitoring(config)
|
99
102
|
@install_lock.synchronize do
|
100
|
-
return if
|
103
|
+
return if browser_agent_already_installed?
|
101
104
|
|
102
|
-
|
105
|
+
mark_browser_agent_as_installed
|
103
106
|
return if config.nil? || !config.respond_to?(:middleware) || !Agent.config[:'browser_monitoring.auto_instrument']
|
104
107
|
|
105
108
|
begin
|
@@ -112,6 +115,15 @@ module NewRelic
|
|
112
115
|
end
|
113
116
|
end
|
114
117
|
|
118
|
+
def browser_agent_already_installed?
|
119
|
+
BROWSER_MONITORING_INSTALLED_SINGLETON.instance_variable_defined?(BROWSER_MONITORING_INSTALLED_VARIABLE) &&
|
120
|
+
BROWSER_MONITORING_INSTALLED_SINGLETON.instance_variable_get(BROWSER_MONITORING_INSTALLED_VARIABLE)
|
121
|
+
end
|
122
|
+
|
123
|
+
def mark_browser_agent_as_installed
|
124
|
+
BROWSER_MONITORING_INSTALLED_SINGLETON.instance_variable_set(BROWSER_MONITORING_INSTALLED_VARIABLE, true)
|
125
|
+
end
|
126
|
+
|
115
127
|
def rails_version
|
116
128
|
@rails_version ||= Gem::Version.new(::Rails::VERSION::STRING)
|
117
129
|
end
|
@@ -70,7 +70,7 @@ module NewRelic
|
|
70
70
|
def rails_32_deprecation
|
71
71
|
return unless defined?(Rails::VERSION) && Gem::Version.new(Rails::VERSION::STRING) <= Gem::Version.new('3.2')
|
72
72
|
|
73
|
-
deprecation_msg = 'The Ruby
|
73
|
+
deprecation_msg = 'The Ruby agent is dropping support for Rails 3.2 ' \
|
74
74
|
'in a future major release. Please upgrade your Rails version to continue receiving support. ' \
|
75
75
|
|
76
76
|
Agent.logger.log_once(
|
@@ -83,6 +83,10 @@ module NewRelic
|
|
83
83
|
camelized[0].downcase.concat(camelized[1..-1])
|
84
84
|
end
|
85
85
|
|
86
|
+
def snakeize(string)
|
87
|
+
string.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
88
|
+
end
|
89
|
+
|
86
90
|
def bundled_gem?(gem_name)
|
87
91
|
defined?(Bundler) && Bundler.rubygems.all_specs.map(&:name).include?(gem_name)
|
88
92
|
rescue => e
|
@@ -74,6 +74,7 @@ module NewRelic
|
|
74
74
|
unicorn
|
75
75
|
webrick
|
76
76
|
fastcgi
|
77
|
+
falcon
|
77
78
|
]
|
78
79
|
while dispatchers.any? && @discovered_dispatcher.nil?
|
79
80
|
send('check_for_' + (dispatchers.shift))
|
@@ -126,16 +127,24 @@ module NewRelic
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def check_for_unicorn
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
130
|
+
return unless (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) &&
|
131
|
+
NewRelic::LanguageSupport.object_space_usable?
|
132
|
+
|
133
|
+
v = find_class_in_object_space(::Unicorn::HttpServer)
|
134
|
+
@discovered_dispatcher = :unicorn if v
|
133
135
|
end
|
134
136
|
|
135
137
|
def check_for_puma
|
136
|
-
|
137
|
-
|
138
|
-
|
138
|
+
return unless defined?(::Puma) && File.basename($0) == 'puma'
|
139
|
+
|
140
|
+
@discovered_dispatcher = :puma
|
141
|
+
end
|
142
|
+
|
143
|
+
def check_for_falcon
|
144
|
+
return unless defined?(::Falcon::Server) &&
|
145
|
+
NewRelic::LanguageSupport.object_space_usable?
|
146
|
+
|
147
|
+
@discovered_dispatcher = :falcon if find_class_in_object_space(::Falcon::Server)
|
139
148
|
end
|
140
149
|
|
141
150
|
def check_for_delayed_job
|
@@ -178,15 +187,15 @@ module NewRelic
|
|
178
187
|
end
|
179
188
|
|
180
189
|
def check_for_litespeed
|
181
|
-
|
182
|
-
|
183
|
-
|
190
|
+
return unless caller.pop.include?('fcgi-bin/RailsRunner.rb')
|
191
|
+
|
192
|
+
@discovered_dispatcher = :litespeed
|
184
193
|
end
|
185
194
|
|
186
195
|
def check_for_passenger
|
187
|
-
|
188
|
-
|
189
|
-
|
196
|
+
return unless defined?(::PhusionPassenger)
|
197
|
+
|
198
|
+
@discovered_dispatcher = :passenger
|
190
199
|
end
|
191
200
|
|
192
201
|
public
|
@@ -23,8 +23,8 @@ module NewRelic
|
|
23
23
|
CONTENT_TYPE = 'Content-Type'.freeze
|
24
24
|
CONTENT_DISPOSITION = 'Content-Disposition'.freeze
|
25
25
|
CONTENT_LENGTH = 'Content-Length'.freeze
|
26
|
-
ATTACHMENT =
|
27
|
-
TEXT_HTML =
|
26
|
+
ATTACHMENT = /attachment/.freeze
|
27
|
+
TEXT_HTML = %r{text/html}.freeze
|
28
28
|
|
29
29
|
BODY_START = '<body'.freeze
|
30
30
|
HEAD_START = '<head'.freeze
|
@@ -65,6 +65,10 @@ module NewRelic
|
|
65
65
|
html?(headers) &&
|
66
66
|
!attachment?(headers) &&
|
67
67
|
!streaming?(env, headers)
|
68
|
+
rescue StandardError => e
|
69
|
+
NewRelic::Agent.logger.error('RUM instrumentation applicability check failed on exception:' \
|
70
|
+
"#{e.class} - #{e.message}")
|
71
|
+
false
|
68
72
|
end
|
69
73
|
|
70
74
|
private
|
@@ -100,11 +104,11 @@ module NewRelic
|
|
100
104
|
|
101
105
|
def html?(headers)
|
102
106
|
# needs else branch coverage
|
103
|
-
headers[CONTENT_TYPE]
|
107
|
+
headers[CONTENT_TYPE]&.match?(TEXT_HTML)
|
104
108
|
end
|
105
109
|
|
106
110
|
def attachment?(headers)
|
107
|
-
headers[CONTENT_DISPOSITION]&.
|
111
|
+
headers[CONTENT_DISPOSITION]&.match?(ATTACHMENT)
|
108
112
|
end
|
109
113
|
|
110
114
|
def streaming?(env, headers)
|
@@ -43,9 +43,11 @@ module NewRelic
|
|
43
43
|
:process_response_metadata,
|
44
44
|
:record_custom_event,
|
45
45
|
:record_metric,
|
46
|
+
:record_llm_feedback_event,
|
46
47
|
:recording_web_transaction?,
|
47
48
|
:require_test_helper,
|
48
49
|
:set_error_group_callback,
|
50
|
+
:set_llm_token_count_callback,
|
49
51
|
:set_segment_callback,
|
50
52
|
:set_sql_obfuscator,
|
51
53
|
:set_transaction_name,
|
@@ -80,7 +82,7 @@ module NewRelic
|
|
80
82
|
"Expected #{klass} for `#{name}` but got #{arg.class}"
|
81
83
|
|
82
84
|
NewRelic::Agent.logger.warn(message)
|
83
|
-
|
85
|
+
false
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module ThreadLocalStorage
|
7
|
+
def self.get(thread, key)
|
8
|
+
if Agent.config[:thread_local_tracer_state]
|
9
|
+
thread.thread_variable_get(key)
|
10
|
+
else
|
11
|
+
thread[key]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.set(thread, key, value)
|
16
|
+
if Agent.config[:thread_local_tracer_state]
|
17
|
+
thread.thread_variable_set(key, value)
|
18
|
+
else
|
19
|
+
thread[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.[](key)
|
24
|
+
get(::Thread.current, key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.[]=(key, value)
|
28
|
+
set(::Thread.current, key, value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/new_relic/version.rb
CHANGED
data/lib/tasks/config.rake
CHANGED
@@ -19,7 +19,7 @@ namespace :newrelic do
|
|
19
19
|
DISABLING => 'Use these settings to toggle instrumentation types during agent startup.',
|
20
20
|
ATTRIBUTES => '[Attributes](/docs/features/agent-attributes) are key-value pairs containing information that determines the properties of an event or transaction. These key-value pairs can be viewed within transaction traces in APM, traced errors in APM, transaction events in dashboards, and page views in dashboards. You can customize exactly which attributes will be sent to each of these destinations',
|
21
21
|
'transaction_tracer' => 'The [transaction traces](/docs/apm/traces/transaction-traces/transaction-traces) feature collects detailed information from a selection of transactions, including a summary of the calling sequence, a breakdown of time spent, and a list of SQL queries and their query plans (on mysql and postgresql). Available features depend on your New Relic subscription level.',
|
22
|
-
'error_collector' => "The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\n\nFor information on ignored and expected errors, [see this page on Error Analytics in APM](/docs/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/). To set expected errors via the `NewRelic::Agent.notice_error` Ruby method, [consult the Ruby
|
22
|
+
'error_collector' => "The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\n\nFor information on ignored and expected errors, [see this page on Error Analytics in APM](/docs/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/). To set expected errors via the `NewRelic::Agent.notice_error` Ruby method, [consult the Ruby agent API](/docs/agents/ruby-agent/api-guides/sending-handled-errors-new-relic/).",
|
23
23
|
'browser_monitoring' => "The browser monitoring [page load timing](/docs/browser/new-relic-browser/page-load-timing/page-load-timing-process) feature (sometimes referred to as real user monitoring or RUM) gives you insight into the performance real users are experiencing with your website. This is accomplished by measuring the time it takes for your users' browsers to download and render your web pages by injecting a small amount of JavaScript code into the header and footer of each page.",
|
24
24
|
'application_logging' => "The Ruby agent supports [APM logs in context](/docs/apm/new-relic-apm/getting-started/get-started-logs-context). For some tips on configuring logs for the Ruby agent, see [Configure Ruby logs in context](/docs/logs/logs-context/configure-logs-context-ruby).\n\nAvailable logging-related config options include:",
|
25
25
|
'analytics_events' => '[New Relic dashboards](/docs/query-your-data/explore-query-data/dashboards/introduction-new-relic-one-dashboards) is a resource to gather and visualize data about your software and what it says about your business. With it you can quickly and easily create real-time dashboards to get immediate answers about end-user experiences, clickstreams, mobile activities, and server transactions.'
|